1require File.expand_path('../helper', __FILE__)
2require 'fileutils'
3
4class TestRakeRules < Rake::TestCase
5  include Rake
6
7  SRCFILE    = "abc.c"
8  SRCFILE2   = "xyz.c"
9  FTNFILE    = "abc.f"
10  OBJFILE    = "abc.o"
11  FOOFILE    = "foo"
12  DOTFOOFILE = ".foo"
13
14  def setup
15    super
16
17    Task.clear
18    @runs = []
19  end
20
21  def test_multiple_rules1
22    create_file(FTNFILE)
23    delete_file(SRCFILE)
24    delete_file(OBJFILE)
25    rule(/\.o$/ => ['.c']) do @runs << :C end
26    rule(/\.o$/ => ['.f']) do @runs << :F end
27    t = Task[OBJFILE]
28    t.invoke
29    Task[OBJFILE].invoke
30    assert_equal [:F], @runs
31  end
32
33  def test_multiple_rules2
34    create_file(FTNFILE)
35    delete_file(SRCFILE)
36    delete_file(OBJFILE)
37    rule(/\.o$/ => ['.f']) do @runs << :F end
38    rule(/\.o$/ => ['.c']) do @runs << :C end
39    Task[OBJFILE].invoke
40    assert_equal [:F], @runs
41  end
42
43  def test_create_with_source
44    create_file(SRCFILE)
45    rule(/\.o$/ => ['.c']) do |t|
46      @runs << t.name
47      assert_equal OBJFILE, t.name
48      assert_equal SRCFILE, t.source
49    end
50    Task[OBJFILE].invoke
51    assert_equal [OBJFILE], @runs
52  end
53
54  def test_single_dependent
55    create_file(SRCFILE)
56    rule(/\.o$/ => '.c') do |t|
57      @runs << t.name
58    end
59    Task[OBJFILE].invoke
60    assert_equal [OBJFILE], @runs
61  end
62
63  def test_rule_can_be_created_by_string
64    create_file(SRCFILE)
65    rule '.o' => ['.c'] do |t|
66      @runs << t.name
67    end
68    Task[OBJFILE].invoke
69    assert_equal [OBJFILE], @runs
70  end
71
72  def test_rule_prereqs_can_be_created_by_string
73    create_file(SRCFILE)
74    rule '.o' => '.c' do |t|
75      @runs << t.name
76    end
77    Task[OBJFILE].invoke
78    assert_equal [OBJFILE], @runs
79  end
80
81  def test_plain_strings_as_dependents_refer_to_files
82    create_file(SRCFILE)
83    rule '.o' => SRCFILE do |t|
84      @runs << t.name
85    end
86    Task[OBJFILE].invoke
87    assert_equal [OBJFILE], @runs
88  end
89
90  def test_file_names_beginning_with_dot_can_be_tricked_into_referring_to_file
91    verbose(false) do
92      create_file('.foo')
93      rule '.o' => "./.foo" do |t|
94        @runs << t.name
95      end
96      Task[OBJFILE].invoke
97      assert_equal [OBJFILE], @runs
98    end
99  end
100
101  def test_file_names_beginning_with_dot_can_be_wrapped_in_lambda
102    verbose(false) do
103
104      create_file(".foo")
105      rule '.o' => lambda{".foo"} do |t|
106        @runs << "#{t.name} - #{t.source}"
107      end
108      Task[OBJFILE].invoke
109      assert_equal ["#{OBJFILE} - .foo"], @runs
110    end
111  end
112
113  def test_file_names_containing_percent_can_be_wrapped_in_lambda
114    verbose(false) do
115      create_file("foo%x")
116      rule '.o' => lambda{"foo%x"} do |t|
117        @runs << "#{t.name} - #{t.source}"
118      end
119      Task[OBJFILE].invoke
120      assert_equal ["#{OBJFILE} - foo%x"], @runs
121    end
122  end
123
124  def test_non_extension_rule_name_refers_to_file
125    verbose(false) do
126      create_file("abc.c")
127      rule "abc" => '.c' do |t|
128        @runs << t.name
129      end
130      Task["abc"].invoke
131      assert_equal ["abc"], @runs
132    end
133  end
134
135  def test_pathmap_automatically_applies_to_name
136    verbose(false) do
137      create_file("zzabc.c")
138      rule ".o" => 'zz%{x,a}n.c' do |t|
139        @runs << "#{t.name} - #{t.source}"
140      end
141      Task["xbc.o"].invoke
142      assert_equal ["xbc.o - zzabc.c"], @runs
143    end
144  end
145
146  def test_plain_strings_are_just_filenames
147    verbose(false) do
148      create_file("plainname")
149      rule ".o" => 'plainname' do |t|
150        @runs << "#{t.name} - #{t.source}"
151      end
152      Task["xbc.o"].invoke
153      assert_equal ["xbc.o - plainname"], @runs
154    end
155  end
156
157  def test_rule_runs_when_explicit_task_has_no_actions
158    create_file(SRCFILE)
159    create_file(SRCFILE2)
160    delete_file(OBJFILE)
161    rule '.o' => '.c' do |t|
162      @runs << t.source
163    end
164    file OBJFILE => [SRCFILE2]
165    Task[OBJFILE].invoke
166    assert_equal [SRCFILE], @runs
167  end
168
169  def test_close_matches_on_name_do_not_trigger_rule
170    create_file("x.c")
171    rule '.o' => ['.c'] do |t|
172      @runs << t.name
173    end
174    assert_raises(RuntimeError) { Task['x.obj'].invoke }
175    assert_raises(RuntimeError) { Task['x.xyo'].invoke }
176  end
177
178  def test_rule_rebuilds_obj_when_source_is_newer
179    create_timed_files(OBJFILE, SRCFILE)
180    rule(/\.o$/ => ['.c']) do
181      @runs << :RULE
182    end
183    Task[OBJFILE].invoke
184    assert_equal [:RULE], @runs
185  end
186
187  def test_rule_with_two_sources_runs_if_both_sources_are_present
188    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
189    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
190      @runs << :RULE
191    end
192    Task[OBJFILE].invoke
193    assert_equal [:RULE], @runs
194  end
195
196  def test_rule_with_two_sources_but_one_missing_does_not_run
197    create_timed_files(OBJFILE, SRCFILE)
198    delete_file(SRCFILE2)
199    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
200      @runs << :RULE
201    end
202    Task[OBJFILE].invoke
203    assert_equal [], @runs
204  end
205
206  def test_rule_with_two_sources_builds_both_sources
207    task 'x.aa'
208    task 'x.bb'
209    rule '.a' => '.aa' do
210      @runs << "A"
211    end
212    rule '.b' => '.bb' do
213      @runs << "B"
214    end
215    rule ".c" => ['.a', '.b'] do
216      @runs << "C"
217    end
218    Task["x.c"].invoke
219    assert_equal ["A", "B", "C"], @runs.sort
220  end
221
222  def test_second_rule_runs_when_first_rule_doesnt
223    create_timed_files(OBJFILE, SRCFILE)
224    delete_file(SRCFILE2)
225    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
226      @runs << :RULE1
227    end
228    rule OBJFILE => [lambda{SRCFILE}] do
229      @runs << :RULE2
230    end
231    Task[OBJFILE].invoke
232    assert_equal [:RULE2], @runs
233  end
234
235  def test_second_rule_doest_run_if_first_triggers
236    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
237    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
238      @runs << :RULE1
239    end
240    rule OBJFILE => [lambda{SRCFILE}] do
241      @runs << :RULE2
242    end
243    Task[OBJFILE].invoke
244    assert_equal [:RULE1], @runs
245  end
246
247  def test_second_rule_doest_run_if_first_triggers_with_reversed_rules
248    create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
249    rule OBJFILE => [lambda{SRCFILE}] do
250      @runs << :RULE1
251    end
252    rule OBJFILE => [lambda{SRCFILE}, lambda{SRCFILE2}] do
253      @runs << :RULE2
254    end
255    Task[OBJFILE].invoke
256    assert_equal [:RULE1], @runs
257  end
258
259  def test_rule_with_proc_dependent_will_trigger
260    mkdir_p("src/jw")
261    create_file("src/jw/X.java")
262    rule %r(classes/.*\.class) => [
263      proc { |fn| fn.pathmap("%{classes,src}d/%n.java") }
264    ] do |task|
265      assert_equal task.name, 'classes/jw/X.class'
266      assert_equal task.source, 'src/jw/X.java'
267      @runs << :RULE
268    end
269    Task['classes/jw/X.class'].invoke
270    assert_equal [:RULE], @runs
271  ensure
272    rm_r("src", :verbose=>false) rescue nil
273  end
274
275  def test_proc_returning_lists_are_flattened_into_prereqs
276    ran = false
277    mkdir_p("flatten")
278    create_file("flatten/a.txt")
279    task 'flatten/b.data' do |t|
280      ran = true
281      touch t.name, :verbose => false
282    end
283    rule '.html' =>
284      proc { |fn|
285      [
286        fn.ext("txt"),
287        "flatten/b.data"
288      ]
289    } do |task|
290    end
291    Task['flatten/a.html'].invoke
292    assert ran, "Should have triggered flattened dependency"
293  ensure
294    rm_r("flatten", :verbose=>false) rescue nil
295  end
296
297  def test_recursive_rules_will_work_as_long_as_they_terminate
298    actions = []
299    create_file("abc.xml")
300    rule '.y' => '.xml' do actions << 'y' end
301    rule '.c' => '.y' do actions << 'c'end
302    rule '.o' => '.c' do actions << 'o'end
303    rule '.exe' => '.o' do actions << 'exe'end
304    Task["abc.exe"].invoke
305    assert_equal ['y', 'c', 'o', 'exe'], actions
306  end
307
308  def test_recursive_rules_that_dont_terminate_will_overflow
309    create_file("a.a")
310    prev = 'a'
311    ('b'..'z').each do |letter|
312      rule ".#{letter}" => ".#{prev}" do |t| puts "#{t.name}" end
313      prev = letter
314    end
315    ex = assert_raises(Rake::RuleRecursionOverflowError) {
316      Task["a.z"].invoke
317    }
318    assert_match(/a\.z => a.y/, ex.message)
319  end
320
321  def test_rules_with_bad_dependents_will_fail
322    rule "a" => [ 1 ] do |t| puts t.name end
323    assert_raises(RuntimeError) do Task['a'].invoke end
324  end
325
326end
327
328