rake 0.9.2.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. data/CHANGES +4 -0
  2. data/README.rdoc +10 -8
  3. data/Rakefile +6 -1
  4. data/TODO +1 -0
  5. data/bin/rake +4 -0
  6. data/doc/command_line_usage.rdoc +61 -12
  7. data/doc/release_notes/rake-0.9.2.2.rdoc +55 -0
  8. data/doc/release_notes/rake-0.9.3.rdoc +102 -0
  9. data/install.rb +1 -1
  10. data/lib/rake/application.rb +244 -140
  11. data/lib/rake/backtrace.rb +18 -0
  12. data/lib/rake/clean.rb +1 -1
  13. data/lib/rake/cloneable.rb +7 -16
  14. data/lib/rake/contrib/ftptools.rb +2 -1
  15. data/lib/rake/contrib/sys.rb +6 -6
  16. data/lib/rake/dsl_definition.rb +13 -7
  17. data/lib/rake/ext/module.rb +1 -1
  18. data/lib/rake/ext/string.rb +2 -1
  19. data/lib/rake/ext/time.rb +2 -1
  20. data/lib/rake/file_list.rb +2 -2
  21. data/lib/rake/file_utils_ext.rb +4 -3
  22. data/lib/rake/multi_task.rb +2 -5
  23. data/lib/rake/phony.rb +13 -0
  24. data/lib/rake/private_reader.rb +20 -0
  25. data/lib/rake/promise.rb +99 -0
  26. data/lib/rake/rake_module.rb +15 -0
  27. data/lib/rake/rdoctask.rb +1 -1
  28. data/lib/rake/runtest.rb +1 -1
  29. data/lib/rake/task.rb +29 -7
  30. data/lib/rake/task_arguments.rb +1 -1
  31. data/lib/rake/task_manager.rb +1 -1
  32. data/lib/rake/testtask.rb +5 -1
  33. data/lib/rake/thread_history_display.rb +48 -0
  34. data/lib/rake/thread_pool.rb +155 -0
  35. data/lib/rake/version.rb +6 -4
  36. data/lib/rake.rb +1 -0
  37. data/test/helper.rb +30 -0
  38. data/test/test_private_reader.rb +42 -0
  39. data/test/test_rake_application.rb +12 -1
  40. data/test/test_rake_application_options.rb +114 -4
  41. data/test/test_rake_backtrace.rb +81 -0
  42. data/test/test_rake_directory_task.rb +16 -5
  43. data/test/test_rake_file_task.rb +21 -1
  44. data/test/test_rake_functional.rb +22 -0
  45. data/test/test_rake_multi_task.rb +8 -0
  46. data/test/test_rake_reduce_compat.rb +65 -0
  47. data/test/test_rake_task.rb +50 -1
  48. data/test/test_rake_thread_pool.rb +123 -0
  49. data/test/test_thread_history_display.rb +91 -0
  50. metadata +53 -37
@@ -385,6 +385,18 @@ class TestRakeApplication < Rake::TestCase
385
385
  ARGV.clear
386
386
  end
387
387
 
388
+ def test_bad_run_with_backtrace
389
+ @app.intern(Rake::Task, "default").enhance { fail }
390
+ ARGV.clear
391
+ ARGV << '-f' << '-s' << '--backtrace'
392
+ assert_raises(SystemExit) {
393
+ _, err = capture_io { @app.run }
394
+ refute_match(/see full trace/, err)
395
+ }
396
+ ensure
397
+ ARGV.clear
398
+ end
399
+
388
400
  def test_run_with_bad_options
389
401
  @app.intern(Rake::Task, "default").enhance { fail }
390
402
  ARGV.clear
@@ -486,4 +498,3 @@ class TestRakeApplication < Rake::TestCase
486
498
  end
487
499
 
488
500
  end
489
-
@@ -29,10 +29,12 @@ class TestRakeApplicationOptions < Rake::TestCase
29
29
 
30
30
  def test_default_options
31
31
  opts = command_line
32
+ assert_nil opts.backtrace
32
33
  assert_nil opts.classic_namespace
33
34
  assert_nil opts.dryrun
34
35
  assert_nil opts.ignore_system
35
36
  assert_nil opts.load_system
37
+ assert_nil opts.always_multitask
36
38
  assert_nil opts.nosearch
37
39
  assert_equal ['rakelib'], opts.rakelib
38
40
  assert_nil opts.show_prereqs
@@ -40,6 +42,7 @@ class TestRakeApplicationOptions < Rake::TestCase
40
42
  assert_nil opts.show_tasks
41
43
  assert_nil opts.silent
42
44
  assert_nil opts.trace
45
+ assert_nil opts.thread_pool_size
43
46
  assert_equal ['rakelib'], opts.rakelib
44
47
  assert ! Rake::FileUtilsExt.verbose_flag
45
48
  assert ! Rake::FileUtilsExt.nowrite_flag
@@ -110,6 +113,18 @@ class TestRakeApplicationOptions < Rake::TestCase
110
113
  assert_equal :exit, @exit
111
114
  end
112
115
 
116
+ def test_jobs
117
+ flags(['--jobs', '4'], ['-j', '4']) do |opts|
118
+ assert_equal 4, opts.thread_pool_size
119
+ end
120
+ flags(['--jobs', 'asdas'], ['-j', 'asdas']) do |opts|
121
+ assert_equal 2, opts.thread_pool_size
122
+ end
123
+ flags('--jobs', '-j') do |opts|
124
+ assert_equal 2, opts.thread_pool_size
125
+ end
126
+ end
127
+
113
128
  def test_libdir
114
129
  flags(['--libdir', 'xx'], ['-I', 'xx'], ['-Ixx']) do |opts|
115
130
  $:.include?('xx')
@@ -118,6 +133,12 @@ class TestRakeApplicationOptions < Rake::TestCase
118
133
  $:.delete('xx')
119
134
  end
120
135
 
136
+ def test_multitask
137
+ flags('--multitask', '-m') do |opts|
138
+ assert_equal opts.always_multitask, true
139
+ end
140
+ end
141
+
121
142
  def test_rakefile
122
143
  flags(['--rakefile', 'RF'], ['--rakefile=RF'], ['-f', 'RF'], ['-fRF']) do |opts|
123
144
  assert_equal ['RF'], @app.instance_eval { @rakefiles }
@@ -125,7 +146,8 @@ class TestRakeApplicationOptions < Rake::TestCase
125
146
  end
126
147
 
127
148
  def test_rakelib
128
- flags(['--rakelibdir', 'A:B:C'], ['--rakelibdir=A:B:C'], ['-R', 'A:B:C'], ['-RA:B:C']) do |opts|
149
+ dirs = %w(A B C).join(File::PATH_SEPARATOR)
150
+ flags(['--rakelibdir', dirs], ["--rakelibdir=#{dirs}"], ['-R', dirs], ["-R#{dirs}"]) do |opts|
129
151
  assert_equal ['A', 'B', 'C'], opts.rakelib
130
152
  end
131
153
  end
@@ -197,12 +219,76 @@ class TestRakeApplicationOptions < Rake::TestCase
197
219
 
198
220
  def test_trace
199
221
  flags('--trace', '-t') do |opts|
200
- assert opts.trace
222
+ assert opts.trace, "should enable trace option"
223
+ assert opts.backtrace, "should enabled backtrace option"
224
+ assert_equal $stderr, opts.trace_output
225
+ assert Rake::FileUtilsExt.verbose_flag
226
+ assert ! Rake::FileUtilsExt.nowrite_flag
227
+ end
228
+ end
229
+
230
+ def test_trace_with_stdout
231
+ flags('--trace=stdout', '-tstdout', '-t stdout') do |opts|
232
+ assert opts.trace, "should enable trace option"
233
+ assert opts.backtrace, "should enabled backtrace option"
234
+ assert_equal $stdout, opts.trace_output
201
235
  assert Rake::FileUtilsExt.verbose_flag
202
236
  assert ! Rake::FileUtilsExt.nowrite_flag
203
237
  end
204
238
  end
205
239
 
240
+ def test_trace_with_stderr
241
+ flags('--trace=stderr', '-tstderr', '-t stderr') do |opts|
242
+ assert opts.trace, "should enable trace option"
243
+ assert opts.backtrace, "should enabled backtrace option"
244
+ assert_equal $stderr, opts.trace_output
245
+ assert Rake::FileUtilsExt.verbose_flag
246
+ assert ! Rake::FileUtilsExt.nowrite_flag
247
+ end
248
+ end
249
+
250
+ def test_trace_with_error
251
+ ex = assert_raises(Rake::CommandLineOptionError) do
252
+ flags('--trace=xyzzy') do |opts| end
253
+ end
254
+ assert_match(/un(known|recognized).*\btrace\b.*xyzzy/i, ex.message)
255
+ end
256
+
257
+
258
+ def test_backtrace
259
+ flags('--backtrace') do |opts|
260
+ assert opts.backtrace, "should enable backtrace option"
261
+ assert_equal $stderr, opts.trace_output
262
+ assert ! opts.trace, "should not enable trace option"
263
+ assert ! Rake::FileUtilsExt.verbose_flag
264
+ end
265
+ end
266
+
267
+ def test_backtrace_with_stdout
268
+ flags('--backtrace=stdout') do |opts|
269
+ assert opts.backtrace, "should enable backtrace option"
270
+ assert_equal $stdout, opts.trace_output
271
+ assert ! opts.trace, "should not enable trace option"
272
+ assert ! Rake::FileUtilsExt.verbose_flag
273
+ end
274
+ end
275
+
276
+ def test_backtrace_with_stderr
277
+ flags('--backtrace=stderr') do |opts|
278
+ assert opts.backtrace, "should enable backtrace option"
279
+ assert_equal $stderr, opts.trace_output
280
+ assert ! opts.trace, "should not enable trace option"
281
+ assert ! Rake::FileUtilsExt.verbose_flag
282
+ end
283
+ end
284
+
285
+ def test_backtrace_with_error
286
+ ex = assert_raises(Rake::CommandLineOptionError) do
287
+ flags('--backtrace=xyzzy') do |opts| end
288
+ end
289
+ assert_match(/un(known|recognized).*\bbacktrace\b.*xyzzy/i, ex.message)
290
+ end
291
+
206
292
  def test_trace_rules
207
293
  flags('--rules') do |opts|
208
294
  assert opts.trace_rules
@@ -213,10 +299,17 @@ class TestRakeApplicationOptions < Rake::TestCase
213
299
  flags('--tasks', '-T') do |opts|
214
300
  assert_equal :tasks, opts.show_tasks
215
301
  assert_equal(//.to_s, opts.show_task_pattern.to_s)
302
+ assert_equal nil, opts.show_all_tasks
216
303
  end
217
304
  flags(['--tasks', 'xyz'], ['-Txyz']) do |opts|
218
305
  assert_equal :tasks, opts.show_tasks
219
306
  assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
307
+ assert_equal nil, opts.show_all_tasks
308
+ end
309
+ flags(['--tasks', 'xyz', '--comments']) do |opts|
310
+ assert_equal :tasks, opts.show_tasks
311
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
312
+ assert_equal false, opts.show_all_tasks
220
313
  end
221
314
  end
222
315
 
@@ -224,10 +317,17 @@ class TestRakeApplicationOptions < Rake::TestCase
224
317
  flags('--where', '-W') do |opts|
225
318
  assert_equal :lines, opts.show_tasks
226
319
  assert_equal(//.to_s, opts.show_task_pattern.to_s)
320
+ assert_equal true, opts.show_all_tasks
227
321
  end
228
322
  flags(['--where', 'xyz'], ['-Wxyz']) do |opts|
229
323
  assert_equal :lines, opts.show_tasks
230
324
  assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
325
+ assert_equal true, opts.show_all_tasks
326
+ end
327
+ flags(['--where', 'xyz', '--comments'], ['-Wxyz', '--comments']) do |opts|
328
+ assert_equal :lines, opts.show_tasks
329
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
330
+ assert_equal false, opts.show_all_tasks
231
331
  end
232
332
  end
233
333
 
@@ -268,7 +368,7 @@ class TestRakeApplicationOptions < Rake::TestCase
268
368
  assert_equal opts.trace, $trace
269
369
  assert_equal opts.dryrun, $dryrun
270
370
  assert_equal opts.silent, $silent
271
- end
371
+ end
272
372
  end
273
373
 
274
374
  assert_match(/deprecated/, err)
@@ -308,6 +408,17 @@ class TestRakeApplicationOptions < Rake::TestCase
308
408
  assert '12', ENV['TESTKEY']
309
409
  end
310
410
 
411
+ def test_rake_explicit_task_library
412
+ Rake.add_rakelib 'app/task', 'other'
413
+
414
+ libs = Rake.application.options.rakelib
415
+
416
+ assert libs.include?("app/task")
417
+ assert libs.include?("other")
418
+ end
419
+
420
+ private
421
+
311
422
  def flags(*sets)
312
423
  sets.each do |set|
313
424
  ARGV.clear
@@ -332,4 +443,3 @@ class TestRakeApplicationOptions < Rake::TestCase
332
443
  @app.options
333
444
  end
334
445
  end
335
-
@@ -0,0 +1,81 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'open3'
3
+
4
+ class TestRakeBacktrace < Rake::TestCase
5
+ # TODO: factor out similar code in test_rake_functional.rb
6
+ def rake(*args)
7
+ lib = File.join(@orig_PWD, "lib")
8
+ bin_rake = File.join(@orig_PWD, "bin", "rake")
9
+ Open3.popen3(RUBY, "-I", lib, bin_rake, *args) { |_, _, err, _| err.read }
10
+ end
11
+
12
+ def invoke(task_name)
13
+ rake task_name.to_s
14
+ end
15
+
16
+ def test_single_collapse
17
+ rakefile %q{
18
+ task :foo do
19
+ raise "foooo!"
20
+ end
21
+ }
22
+
23
+ lines = invoke(:foo).split("\n")
24
+
25
+ assert_equal "rake aborted!", lines[0]
26
+ assert_equal "foooo!", lines[1]
27
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
28
+ assert_something_matches %r!\ATasks:!, lines
29
+ end
30
+
31
+ def test_multi_collapse
32
+ rakefile %q{
33
+ task :foo do
34
+ Rake.application.invoke_task(:bar)
35
+ end
36
+ task :bar do
37
+ raise "barrr!"
38
+ end
39
+ }
40
+
41
+ lines = invoke(:foo).split("\n")
42
+
43
+ assert_equal "rake aborted!", lines[0]
44
+ assert_equal "barrr!", lines[1]
45
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:6!i, lines
46
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
47
+ assert_something_matches %r!\ATasks:!, lines
48
+ end
49
+
50
+ def test_suppress_option
51
+ rakefile %q{
52
+ task :baz do
53
+ raise "bazzz!"
54
+ end
55
+ }
56
+
57
+ lines = rake("baz").split("\n")
58
+ assert_equal "rake aborted!", lines[0]
59
+ assert_equal "bazzz!", lines[1]
60
+ assert_something_matches %r!Rakefile!i, lines
61
+
62
+ lines = rake("--suppress-backtrace", ".ak.file", "baz").split("\n")
63
+ assert_equal "rake aborted!", lines[0]
64
+ assert_equal "bazzz!", lines[1]
65
+ refute_match %r!Rakefile!i, lines[2]
66
+ end
67
+
68
+ private
69
+
70
+ # Assert that the pattern matches at least one line in +lines+.
71
+ def assert_something_matches(pattern, lines)
72
+ lines.each do |ln|
73
+ if pattern =~ ln
74
+ assert_match pattern, ln
75
+ return
76
+ end
77
+ end
78
+ flunk "expected #{pattern.inspect} to match something in:\n #{lines.join("\n ")}"
79
+ end
80
+
81
+ end
@@ -36,11 +36,22 @@ class TestRakeDirectoryTask < Rake::TestCase
36
36
  assert_nil Task['c:/'].comment
37
37
  assert_equal "WIN32 DESC", Task['c:/a/b/c'].comment
38
38
  assert_nil Task['c:/a/b'].comment
39
- verbose(false) {
40
- Task['c:/a/b'].invoke
41
- }
42
- assert File.exist?('c:/a/b')
43
- refute File.exist?('c:/a/b/c')
44
39
  end
45
40
  end
41
+
42
+ def test_can_use_blocks
43
+ runlist = []
44
+
45
+ t1 = directory("a/b/c" => :t2) { |t| runlist << t.name }
46
+ t2 = task(:t2) { |t| runlist << t.name }
47
+
48
+ verbose(false) {
49
+ t1.invoke
50
+ }
51
+
52
+ assert_equal Task["a/b/c"], t1
53
+ assert_equal FileCreationTask, Task["a/b/c"].class
54
+ assert_equal ["t2", "a/b/c"], runlist
55
+ assert File.directory?("a/b/c")
56
+ end
46
57
  end
@@ -1,6 +1,10 @@
1
1
  require File.expand_path('../helper', __FILE__)
2
2
  require 'fileutils'
3
3
 
4
+ def load_phony
5
+ load File.dirname(__FILE__) + "/../lib/rake/phony.rb"
6
+ end
7
+
4
8
  class TestRakeFileTask < Rake::TestCase
5
9
  include Rake
6
10
 
@@ -41,6 +45,23 @@ class TestRakeFileTask < Rake::TestCase
41
45
  assert ! t1.needed?, "Should not need to rebuild new file because of old"
42
46
  end
43
47
 
48
+ def test_file_times_new_depend_on_regular_task_timestamps
49
+ load_phony
50
+
51
+ name = "dummy"
52
+ task name
53
+
54
+ create_timed_files(NEWFILE)
55
+
56
+ t1 = Rake.application.intern(FileTask, NEWFILE).enhance([name])
57
+
58
+ assert t1.needed?, "depending on non-file task uses Time.now"
59
+
60
+ task(name => :phony)
61
+
62
+ assert ! t1.needed?, "unless the non-file task has a timestamp"
63
+ end
64
+
44
65
  def test_file_times_old_depends_on_new
45
66
  create_timed_files(OLDFILE, NEWFILE)
46
67
 
@@ -99,4 +120,3 @@ class TestRakeFileTask < Rake::TestCase
99
120
  end
100
121
 
101
122
  end
102
-
@@ -417,6 +417,28 @@ class TestRakeFunctional < Rake::TestCase
417
417
  assert_equal "1\n", @out
418
418
  end
419
419
 
420
+ def can_detect_signals?
421
+ system "ruby -e 'Process.kill \"TERM\", $$'"
422
+ status = $?
423
+ if @verbose
424
+ puts " SIG status = #{$?.inspect}"
425
+ puts " SIG status.respond_to?(:signaled?) = #{$?.respond_to?(:signaled?).inspect}"
426
+ puts " SIG status.signaled? = #{status.signaled?}" if status.respond_to?(:signaled?)
427
+ end
428
+ status.respond_to?(:signaled?) && status.signaled?
429
+ end
430
+
431
+ def test_signal_propagation_in_tests
432
+ if can_detect_signals?
433
+ rakefile_test_signal
434
+ rake
435
+ assert_match(/ATEST/, @out)
436
+ refute_match(/BTEST/, @out)
437
+ else
438
+ skip "Signal detect seems broken on this system"
439
+ end
440
+ end
441
+
420
442
  private
421
443
 
422
444
  # Run a shell Ruby command with command line options (using the
@@ -47,5 +47,13 @@ class TestRakeMultiTask < Rake::TestCase
47
47
  assert @runs.index("B0") < @runs.index("B1")
48
48
  assert @runs.index("B1") < @runs.index("B2")
49
49
  end
50
+
51
+ def test_multitasks_with_parameters
52
+ task :a, [:arg] do |t,args| add_run(args[:arg]) end
53
+ multitask :b, [:arg] => [:a] do |t,args| add_run(args[:arg]+'mt') end
54
+ Task[:b].invoke "b"
55
+ assert @runs[0] == "b"
56
+ assert @runs[1] == "bmt"
57
+ end
50
58
  end
51
59
 
@@ -0,0 +1,65 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'open3'
3
+
4
+ class TestRakeReduceCompat < Rake::TestCase
5
+ # TODO: factor out similar code in test_rake_functional.rb
6
+ def rake(*args)
7
+ lib = File.join(@orig_PWD, "lib")
8
+ bin_rake = File.join(@orig_PWD, "bin", "rake")
9
+ Open3.popen3(RUBY, "-I", lib, bin_rake, *args) { |_, out, _, _| out.read }
10
+ end
11
+
12
+ def invoke_normal(task_name)
13
+ rake task_name.to_s
14
+ end
15
+
16
+ def invoke_reduce_compat(task_name)
17
+ rake "--reduce-compat", task_name.to_s
18
+ end
19
+
20
+ def test_no_deprecated_dsl
21
+ rakefile %q{
22
+ task :check_task do
23
+ Module.new { p defined?(task) }
24
+ end
25
+
26
+ task :check_file do
27
+ Module.new { p defined?(file) }
28
+ end
29
+ }
30
+
31
+ assert_equal %{"method"}, invoke_normal(:check_task).chomp
32
+ assert_equal %{"method"}, invoke_normal(:check_file).chomp
33
+
34
+ assert_equal "nil", invoke_reduce_compat(:check_task).chomp
35
+ assert_equal "nil", invoke_reduce_compat(:check_file).chomp
36
+ end
37
+
38
+ def test_no_classic_namespace
39
+ rakefile %q{
40
+ task :check_task do
41
+ begin
42
+ Task
43
+ print "present"
44
+ rescue NameError
45
+ print "absent"
46
+ end
47
+ end
48
+
49
+ task :check_file_task do
50
+ begin
51
+ FileTask
52
+ print "present"
53
+ rescue NameError
54
+ print "absent"
55
+ end
56
+ end
57
+ }
58
+
59
+ assert_equal "present", invoke_normal(:check_task)
60
+ assert_equal "present", invoke_normal(:check_file_task)
61
+
62
+ assert_equal "absent", invoke_reduce_compat(:check_task)
63
+ assert_equal "absent", invoke_reduce_compat(:check_file_task)
64
+ end
65
+ end
@@ -104,10 +104,12 @@ class TestRakeTask < Rake::TestCase
104
104
  end
105
105
 
106
106
  def test_clear
107
+ desc "a task"
107
108
  t = task("t" => "a") { }
108
109
  t.clear
109
110
  assert t.prerequisites.empty?, "prerequisites should be empty"
110
111
  assert t.actions.empty?, "actions should be empty"
112
+ assert_nil t.comment, "comments should be empty"
111
113
  end
112
114
 
113
115
  def test_clear_prerequisites
@@ -123,6 +125,22 @@ class TestRakeTask < Rake::TestCase
123
125
  assert t.actions.empty?, "actions should be empty"
124
126
  end
125
127
 
128
+ def test_clear_comments
129
+ desc "the original foo"
130
+ task :foo => [:x] do
131
+ # Dummy action
132
+ end
133
+
134
+ task(:foo).clear_comments
135
+
136
+ desc "a slightly different foo"
137
+ task :foo
138
+
139
+ assert_equal "a slightly different foo", task(:foo).comment
140
+ assert_equal ["x"], task(:foo).prerequisites
141
+ assert_equal 1, task(:foo).actions.size
142
+ end
143
+
126
144
  def test_find
127
145
  task :tfind
128
146
  assert_equal "tfind", Task[:tfind].name
@@ -223,6 +241,38 @@ class TestRakeTask < Rake::TestCase
223
241
  assert_in_delta now + 10, a.timestamp, 0.1, 'computer too slow?'
224
242
  end
225
243
 
244
+ def test_always_multitask
245
+ mx = Mutex.new
246
+ result = []
247
+
248
+ t_a = task(:a) do |t|
249
+ sleep 0.02
250
+ mx.synchronize{ result << t.name }
251
+ end
252
+
253
+ t_b = task(:b) do |t|
254
+ mx.synchronize{ result << t.name }
255
+ end
256
+
257
+ t_c = task(:c => [:a,:b]) do |t|
258
+ mx.synchronize{ result << t.name }
259
+ end
260
+
261
+ t_c.invoke
262
+
263
+ # task should always run in order
264
+ assert_equal ['a', 'b', 'c'], result
265
+
266
+ [t_a, t_b, t_c].each { |t| t.reenable }
267
+ result.clear
268
+
269
+ Rake.application.options.always_multitask = true
270
+ t_c.invoke
271
+
272
+ # with multitask, task 'b' should grab the mutex first
273
+ assert_equal ['b', 'a', 'c'], result
274
+ end
275
+
226
276
  def test_investigation_output
227
277
  t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
228
278
  task(:t2)
@@ -264,4 +314,3 @@ class TestRakeTask < Rake::TestCase
264
314
  assert_equal "HI", t.comment
265
315
  end
266
316
  end
267
-
@@ -0,0 +1,123 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'rake/thread_pool'
3
+ require 'test/unit/assertions'
4
+
5
+ class TestRakeTestThreadPool < Rake::TestCase
6
+ include Rake
7
+
8
+ def test_pool_executes_in_current_thread_for_zero_threads
9
+ pool = ThreadPool.new(0)
10
+ f = pool.future{Thread.current}
11
+ pool.join
12
+ assert_equal Thread.current, f.value
13
+ end
14
+
15
+ def test_pool_executes_in_other_thread_for_pool_of_size_one
16
+ pool = ThreadPool.new(1)
17
+ f = pool.future{Thread.current}
18
+ pool.join
19
+ refute_equal Thread.current, f.value
20
+ end
21
+
22
+ def test_pool_executes_in_two_other_threads_for_pool_of_size_two
23
+ pool = ThreadPool.new(2)
24
+ threads = 2.times.collect{ pool.future{ sleep 0.1; Thread.current } }.each{|f|f.value}
25
+
26
+ refute_equal threads[0], threads[1]
27
+ refute_equal Thread.current, threads[0]
28
+ refute_equal Thread.current, threads[1]
29
+ end
30
+
31
+ def test_pool_creates_the_correct_number_of_threads
32
+ pool = ThreadPool.new(2)
33
+ threads = Set.new
34
+ t_mutex = Mutex.new
35
+ 10.times.each do
36
+ pool.future do
37
+ sleep 0.02
38
+ t_mutex.synchronize{ threads << Thread.current }
39
+ end
40
+ end
41
+ pool.join
42
+ assert_equal 2, threads.count
43
+ end
44
+
45
+ def test_pool_future_captures_arguments
46
+ pool = ThreadPool.new(2)
47
+ a = 'a'
48
+ b = 'b'
49
+ c = 5 # 5 throws an execption with 5.dup. It should be ignored
50
+ pool.future(a,c){ |a_var,ignore| a_var.capitalize!; b.capitalize! }
51
+ pool.join
52
+ assert_equal 'a', a
53
+ assert_equal 'b'.capitalize, b
54
+ end
55
+
56
+ def test_pool_join_empties_queue
57
+ pool = ThreadPool.new(2)
58
+ repeat = 25
59
+ repeat.times {
60
+ pool.future do
61
+ repeat.times {
62
+ pool.future do
63
+ repeat.times {
64
+ pool.future do end
65
+ }
66
+ end
67
+ }
68
+ end
69
+ }
70
+
71
+ pool.join
72
+ assert_equal true, pool.__send__(:__queue__).empty?, "queue should be empty"
73
+ end
74
+
75
+ # test that throwing an exception way down in the blocks propagates
76
+ # to the top
77
+ def test_exceptions
78
+ pool = ThreadPool.new(10)
79
+
80
+ deep_exception_block = lambda do |count|
81
+ next raise Exception.new if ( count < 1 )
82
+ pool.future(count-1, &deep_exception_block).value
83
+ end
84
+
85
+ assert_raises(Exception) do
86
+ pool.future(2, &deep_exception_block).value
87
+ end
88
+
89
+ end
90
+
91
+ def test_pool_prevents_deadlock
92
+ pool = ThreadPool.new(5)
93
+
94
+ common_dependency_a = pool.future { sleep 0.2 }
95
+ futures_a = 10.times.collect { pool.future{ common_dependency_a.value; sleep(rand() * 0.01) } }
96
+
97
+ common_dependency_b = pool.future { futures_a.each { |f| f.value } }
98
+ futures_b = 10.times.collect { pool.future{ common_dependency_b.value; sleep(rand() * 0.01) } }
99
+
100
+ futures_b.each{|f|f.value}
101
+ pool.join
102
+ end
103
+
104
+ def test_pool_reports_correct_results
105
+ pool = ThreadPool.new(7)
106
+
107
+ a = 18
108
+ b = 5
109
+ c = 3
110
+
111
+ result = a.times.collect do
112
+ pool.future do
113
+ b.times.collect do
114
+ pool.future { sleep rand * 0.001; c }
115
+ end.inject(0) { |m,f| m+f.value }
116
+ end
117
+ end.inject(0) { |m,f| m+f.value }
118
+
119
+ assert_equal( (a*b*c), result )
120
+ pool.join
121
+ end
122
+
123
+ end