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