test-queue-patched 0.4.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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile-cucumber1-3 +4 -0
  6. data/Gemfile-cucumber1-3.lock +33 -0
  7. data/Gemfile-cucumber2-4 +4 -0
  8. data/Gemfile-cucumber2-4.lock +37 -0
  9. data/Gemfile-minitest4 +3 -0
  10. data/Gemfile-minitest4.lock +19 -0
  11. data/Gemfile-minitest5 +3 -0
  12. data/Gemfile-minitest5.lock +19 -0
  13. data/Gemfile-rspec2-1 +3 -0
  14. data/Gemfile-rspec2-1.lock +27 -0
  15. data/Gemfile-rspec3-0 +3 -0
  16. data/Gemfile-rspec3-0.lock +31 -0
  17. data/Gemfile-rspec3-1 +3 -0
  18. data/Gemfile-rspec3-1.lock +31 -0
  19. data/Gemfile-rspec3-2 +3 -0
  20. data/Gemfile-rspec3-2.lock +32 -0
  21. data/Gemfile-testunit +3 -0
  22. data/Gemfile-testunit.lock +21 -0
  23. data/Gemfile.lock +41 -0
  24. data/README.md +126 -0
  25. data/bin/cucumber-queue +4 -0
  26. data/bin/minitest-queue +4 -0
  27. data/bin/rspec-queue +4 -0
  28. data/bin/testunit-queue +4 -0
  29. data/lib/test-queue.rb +1 -0
  30. data/lib/test_queue/iterator.rb +107 -0
  31. data/lib/test_queue/runner/cucumber.rb +115 -0
  32. data/lib/test_queue/runner/minitest.rb +21 -0
  33. data/lib/test_queue/runner/minitest4.rb +88 -0
  34. data/lib/test_queue/runner/minitest5.rb +87 -0
  35. data/lib/test_queue/runner/puppet_lint.rb +31 -0
  36. data/lib/test_queue/runner/rspec.rb +79 -0
  37. data/lib/test_queue/runner/rspec2.rb +44 -0
  38. data/lib/test_queue/runner/rspec3.rb +54 -0
  39. data/lib/test_queue/runner/sample.rb +74 -0
  40. data/lib/test_queue/runner/testunit.rb +74 -0
  41. data/lib/test_queue/runner.rb +632 -0
  42. data/lib/test_queue/stats.rb +95 -0
  43. data/lib/test_queue/test_framework.rb +29 -0
  44. data/lib/test_queue.rb +8 -0
  45. data/script/bootstrap +12 -0
  46. data/script/cibuild +19 -0
  47. data/script/spec +7 -0
  48. data/spec/stats_spec.rb +76 -0
  49. data/test/cucumber.bats +57 -0
  50. data/test/minitest4.bats +34 -0
  51. data/test/minitest5.bats +194 -0
  52. data/test/rspec.bats +46 -0
  53. data/test/samples/features/bad.feature +5 -0
  54. data/test/samples/features/sample.feature +25 -0
  55. data/test/samples/features/sample2.feature +29 -0
  56. data/test/samples/features/step_definitions/common.rb +19 -0
  57. data/test/samples/sample_minispec.rb +37 -0
  58. data/test/samples/sample_minitest4.rb +25 -0
  59. data/test/samples/sample_minitest5.rb +33 -0
  60. data/test/samples/sample_rspec_helper.rb +1 -0
  61. data/test/samples/sample_shared_examples_for_spec.rb +5 -0
  62. data/test/samples/sample_spec.rb +25 -0
  63. data/test/samples/sample_split_spec.rb +17 -0
  64. data/test/samples/sample_testunit.rb +25 -0
  65. data/test/samples/sample_use_shared_example1_spec.rb +8 -0
  66. data/test/samples/sample_use_shared_example2_spec.rb +8 -0
  67. data/test/sleepy_runner.rb +14 -0
  68. data/test/testlib.bash +89 -0
  69. data/test/testunit.bats +20 -0
  70. data/test-queue-patched.gemspec +21 -0
  71. metadata +117 -0
@@ -0,0 +1,115 @@
1
+ require 'cucumber'
2
+ require 'cucumber/rspec/disable_option_parser'
3
+ require 'cucumber/cli/main'
4
+
5
+ module Cucumber
6
+ module Ast
7
+ class Features
8
+ attr_accessor :features
9
+ end
10
+
11
+ class Feature
12
+ def to_s
13
+ title
14
+ end
15
+ end
16
+ end
17
+
18
+ class Runtime
19
+ if defined?(::Cucumber::Runtime::FeaturesLoader)
20
+ # Without this module, Runtime#features would load all features specified
21
+ # on the command line. We want to avoid that and load only the features
22
+ # each worker needs ourselves, so we override the default behavior to let
23
+ # us put our iterator in place without loading any features directly.
24
+ module InjectableFeatures
25
+ def features
26
+ return @features if defined?(@features)
27
+ super
28
+ end
29
+
30
+ def features=(iterator)
31
+ @features = ::Cucumber::Ast::Features.new
32
+ @features.features = iterator
33
+ end
34
+ end
35
+
36
+ prepend InjectableFeatures
37
+ else
38
+ attr_writer :features
39
+ end
40
+ end
41
+ end
42
+
43
+ module TestQueue
44
+ class Runner
45
+ class Cucumber < Runner
46
+ def initialize
47
+ super(TestFramework::Cucumber.new)
48
+ end
49
+
50
+ def run_worker(iterator)
51
+ runtime = @test_framework.runtime
52
+ runtime.features = iterator
53
+
54
+ @test_framework.cli.execute!(runtime)
55
+
56
+ if runtime.respond_to?(:summary_report, true)
57
+ runtime.send(:summary_report).test_cases.total_failed
58
+ else
59
+ runtime.results.scenarios(:failed).size
60
+ end
61
+ end
62
+
63
+ def summarize_worker(worker)
64
+ output = worker.output.gsub(/\e\[\d+./, '')
65
+ worker.summary = output.split("\n").grep(/^\d+ (scenarios?|steps?)/).first
66
+ worker.failure_output = output.scan(/^Failing Scenarios:\n(.*)\n\d+ scenarios?/m).join("\n")
67
+ end
68
+ end
69
+ end
70
+
71
+ class TestFramework
72
+ class Cucumber < TestFramework
73
+ class FakeKernel
74
+ def exit(n)
75
+ if $!
76
+ # Let Cucumber exit for raised exceptions.
77
+ Kernel.exit(n)
78
+ end
79
+ # Don't let Cucumber exit to indicate test failures. We want to
80
+ # return the number of failures from #run_worker instead.
81
+ end
82
+ end
83
+
84
+ def cli
85
+ @cli ||= ::Cucumber::Cli::Main.new(ARGV.dup, $stdin, $stdout, $stderr, FakeKernel.new)
86
+ end
87
+
88
+ def runtime
89
+ @runtime ||= ::Cucumber::Runtime.new(cli.configuration)
90
+ end
91
+
92
+ def all_suite_files
93
+ if runtime.respond_to?(:feature_files, true)
94
+ runtime.send(:feature_files)
95
+ else
96
+ cli.configuration.feature_files
97
+ end
98
+ end
99
+
100
+ def suites_from_file(path)
101
+ if defined?(::Cucumber::Core::Gherkin::Document)
102
+ source = ::Cucumber::Runtime::NormalisedEncodingFile.read(path)
103
+ doc = ::Cucumber::Core::Gherkin::Document.new(path, source)
104
+ [[File.basename(doc.uri), doc]]
105
+ else
106
+ loader =
107
+ ::Cucumber::Runtime::FeaturesLoader.new([path],
108
+ cli.configuration.filters,
109
+ cli.configuration.tag_expression)
110
+ loader.features.map { |feature| [feature.title, feature] }
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'minitest'
3
+ require 'test_queue/runner/minitest5'
4
+ rescue LoadError => e
5
+ require 'minitest/unit'
6
+ require 'test_queue/runner/minitest4'
7
+ end
8
+
9
+ module TestQueue
10
+ class Runner
11
+ class MiniTest < Runner
12
+ def summarize_worker(worker)
13
+ worker.summary = worker.lines.grep(/, \d+ errors?, /).first
14
+ failures = worker.lines.select{ |line|
15
+ line if (line =~ /^Finished/) ... (line =~ /, \d+ errors?, /)
16
+ }[1..-2]
17
+ worker.failure_output = failures.join("\n") if failures
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,88 @@
1
+ require 'test_queue/runner'
2
+ require 'set'
3
+ require 'stringio'
4
+
5
+ class MiniTestQueueRunner < MiniTest::Unit
6
+ def _run_suites(suites, type)
7
+ self.class.output = $stdout
8
+
9
+ if defined?(ParallelEach)
10
+ # Ignore its _run_suites implementation since we don't handle it gracefully.
11
+ # If we don't do this #partition is called on the iterator and all suites
12
+ # distributed immediately, instead of picked up as workers are available.
13
+ suites.map { |suite| _run_suite suite, type }
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def _run_anything(*)
20
+ ret = super
21
+ output.puts
22
+ ret
23
+ end
24
+
25
+ def _run_suite(suite, type)
26
+ output.print ' '
27
+ output.print suite
28
+ output.print ': '
29
+
30
+ start = Time.now
31
+ ret = super
32
+ diff = Time.now - start
33
+
34
+ output.puts(" <%.3f>" % diff)
35
+ ret
36
+ end
37
+
38
+ self.runner = self.new
39
+ self.output = StringIO.new
40
+ end
41
+
42
+ class MiniTest::Unit::TestCase
43
+ class << self
44
+ attr_accessor :test_suites
45
+
46
+ def original_test_suites
47
+ @@test_suites.keys.reject{ |s| s.test_methods.empty? }
48
+ end
49
+ end
50
+
51
+ def failure_count
52
+ failures.length
53
+ end
54
+ end
55
+
56
+ module TestQueue
57
+ class Runner
58
+ class MiniTest < Runner
59
+ def initialize
60
+ if ::MiniTest::Unit::TestCase.original_test_suites.any?
61
+ fail "Do not `require` test files. Pass them via ARGV instead and they will be required as needed."
62
+ end
63
+ super(TestFramework::MiniTest.new)
64
+ end
65
+
66
+ def run_worker(iterator)
67
+ ::MiniTest::Unit::TestCase.test_suites = iterator
68
+ ::MiniTest::Unit.new.run
69
+ end
70
+ end
71
+ end
72
+
73
+ class TestFramework
74
+ class MiniTest < TestFramework
75
+ def all_suite_files
76
+ ARGV
77
+ end
78
+
79
+ def suites_from_file(path)
80
+ ::MiniTest::Unit::TestCase.reset
81
+ require File.absolute_path(path)
82
+ ::MiniTest::Unit::TestCase.original_test_suites.map { |suite|
83
+ [suite.name, suite]
84
+ }
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_queue/runner'
2
+
3
+ module MiniTest
4
+ def self.__run reporter, options
5
+ suites = Runnable.runnables
6
+ suites.map { |suite| suite.run reporter, options }
7
+ end
8
+
9
+ class Runnable
10
+ def failure_count
11
+ failures.length
12
+ end
13
+ end
14
+
15
+ class Test
16
+ def self.runnables= runnables
17
+ @@runnables = runnables
18
+ end
19
+
20
+ # Synchronize all tests, even serial ones.
21
+ #
22
+ # Minitest runs serial tests before parallel ones to ensure the
23
+ # unsynchronized serial tests don't overlap the parallel tests. But since
24
+ # the test-queue master hands out tests without actually loading their
25
+ # code, there's no way to know which are parallel and which are serial.
26
+ # Synchronizing serial tests does add some overhead, but hopefully this is
27
+ # outweighed by the speed benefits of using test-queue.
28
+ def _synchronize; Test.io_lock.synchronize { yield }; end
29
+ end
30
+
31
+ class ProgressReporter
32
+ # Override original method to make test-queue specific output
33
+ def record result
34
+ io.print ' '
35
+ io.print result.class
36
+ io.print ': '
37
+ io.print result.result_code
38
+ io.puts(" <%.3f>" % result.time)
39
+ end
40
+ end
41
+
42
+ begin
43
+ require 'minitest/minitest_reporter_plugin'
44
+
45
+ class << self
46
+ private
47
+ def total_count(options)
48
+ 0
49
+ end
50
+ end
51
+ rescue LoadError
52
+ end
53
+ end
54
+
55
+ module TestQueue
56
+ class Runner
57
+ class MiniTest < Runner
58
+ def initialize
59
+ if ::MiniTest::Test.runnables.any? { |r| r.runnable_methods.any? }
60
+ fail "Do not `require` test files. Pass them via ARGV instead and they will be required as needed."
61
+ end
62
+ super(TestFramework::MiniTest.new)
63
+ end
64
+
65
+ def run_worker(iterator)
66
+ ::MiniTest::Test.runnables = iterator
67
+ ::MiniTest.run ? 0 : 1
68
+ end
69
+ end
70
+ end
71
+
72
+ class TestFramework
73
+ class MiniTest < TestFramework
74
+ def all_suite_files
75
+ ARGV
76
+ end
77
+
78
+ def suites_from_file(path)
79
+ ::MiniTest::Test.reset
80
+ require File.absolute_path(path)
81
+ ::MiniTest::Test.runnables
82
+ .reject { |s| s.runnable_methods.empty? }
83
+ .map { |s| [s.name, s] }
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_queue'
2
+ require 'puppet-lint'
3
+
4
+ module TestQueue
5
+ class Runner
6
+ class PuppetLint < Runner
7
+ def run_worker(iterator)
8
+ errors = 0
9
+ linter = PuppetLint.new
10
+ iterator.each do |file|
11
+ puts "Evaluating #{file}"
12
+ linter.file = file
13
+ linter.run
14
+ errors += 1 if linter.errors?
15
+ end
16
+ errors
17
+ end
18
+
19
+ def summarize_worker(worker)
20
+ lines = worker.lines
21
+
22
+ files = lines.select{ |line| line =~ /^Evaluating/ }
23
+ errors = lines.select{ |line| line =~ /^ERROR/ }
24
+ warnings = lines.select{ |line| line =~ /^WARNING/ }
25
+
26
+ worker.summary = "#{files.size} files, #{warnings.size} warnings, #{errors.size} errors"
27
+ worker.failure_output = errors.join("\n")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_queue/runner'
2
+ require 'rspec/core'
3
+
4
+ case ::RSpec::Core::Version::STRING.to_i
5
+ when 2
6
+ require_relative 'rspec2'
7
+ when 3
8
+ require_relative 'rspec3'
9
+ else
10
+ fail 'requires rspec version 2 or 3'
11
+ end
12
+
13
+ module TestQueue
14
+ class Runner
15
+ class RSpec < Runner
16
+ def initialize
17
+ super(TestFramework::RSpec.new)
18
+ end
19
+
20
+ def run_worker(iterator)
21
+ rspec = ::RSpec::Core::QueueRunner.new
22
+ rspec.run_each(iterator).to_i
23
+ end
24
+
25
+ def summarize_worker(worker)
26
+ worker.summary = worker.lines.grep(/ examples?, /).first
27
+ worker.failure_output = worker.output[/^Failures:\n\n(.*)\n^Finished/m, 1]
28
+ end
29
+ end
30
+ end
31
+
32
+ class TestFramework
33
+ class RSpec < TestFramework
34
+ def all_suite_files
35
+ options = ::RSpec::Core::ConfigurationOptions.new(ARGV)
36
+ options.parse_options if options.respond_to?(:parse_options)
37
+ options.configure(::RSpec.configuration)
38
+
39
+ ::RSpec.configuration.files_to_run.uniq
40
+ end
41
+
42
+ def suites_from_file(path)
43
+ ::RSpec.world.example_groups.clear
44
+ load path
45
+ split_groups(::RSpec.world.example_groups).map { |example_or_group|
46
+ name = if example_or_group.respond_to?(:id)
47
+ example_or_group.id
48
+ elsif example_or_group.respond_to?(:full_description)
49
+ example_or_group.full_description
50
+ elsif example_or_group.metadata.key?(:full_description)
51
+ example_or_group.metadata[:full_description]
52
+ else
53
+ example_or_group.metadata[:example_group][:full_description]
54
+ end
55
+ [name, example_or_group]
56
+ }
57
+ end
58
+
59
+ private
60
+
61
+ def split_groups(groups)
62
+ return groups unless split_groups?
63
+
64
+ groups_to_split, groups_to_keep = [], []
65
+ groups.each do |group|
66
+ (group.metadata[:no_split] ? groups_to_keep : groups_to_split) << group
67
+ end
68
+ queue = groups_to_split.flat_map(&:descendant_filtered_examples)
69
+ queue.concat groups_to_keep
70
+ queue
71
+ end
72
+
73
+ def split_groups?
74
+ return @split_groups if defined?(@split_groups)
75
+ @split_groups = ENV['TEST_QUEUE_SPLIT_GROUPS'] && ENV['TEST_QUEUE_SPLIT_GROUPS'].strip.downcase == 'true'
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,44 @@
1
+ class ::RSpec::Core::ExampleGroup
2
+ def self.failure_count
3
+ examples.map {|e| e.execution_result[:status] == "failed"}.length
4
+ end
5
+ end
6
+
7
+ module RSpec::Core
8
+ class QueueRunner < CommandLine
9
+ def initialize
10
+ super(ARGV)
11
+ @configuration.output_stream = $stdout
12
+ @configuration.error_stream = $stderr
13
+ end
14
+
15
+ def run_each(iterator)
16
+ @configuration.reporter.report(0, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
17
+ begin
18
+ @configuration.run_hook(:before, :suite)
19
+ iterator.map {|g|
20
+ if g.is_a? ::RSpec::Core::Example
21
+ print " #{g.full_description}: "
22
+ example = g
23
+ g = example.example_group
24
+ ::RSpec.world.filtered_examples.clear
25
+ examples = [example]
26
+ examples.extend(::RSpec::Core::Extensions::Ordered::Examples)
27
+ ::RSpec.world.filtered_examples[g] = examples
28
+ else
29
+ print " #{g.description}: "
30
+ end
31
+ start = Time.now
32
+ ret = g.run(reporter)
33
+ diff = Time.now-start
34
+ puts(" <%.3f>" % diff)
35
+
36
+ ret
37
+ }.all? ? 0 : @configuration.failure_exit_code
38
+ ensure
39
+ @configuration.run_hook(:after, :suite)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,54 @@
1
+ class ::RSpec::Core::ExampleGroup
2
+ def self.failure_count
3
+ examples.map {|e| e.execution_result.status == "failed"}.length
4
+ end
5
+ end
6
+
7
+ module RSpec::Core
8
+ # RSpec 3.2 introduced:
9
+ unless Configuration.method_defined?(:with_suite_hooks)
10
+ class Configuration
11
+ def with_suite_hooks
12
+ begin
13
+ hook_context = SuiteHookContext.new
14
+ hooks.run(:before, :suite, hook_context)
15
+ yield
16
+ ensure
17
+ hooks.run(:after, :suite, hook_context)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class QueueRunner < Runner
24
+ def initialize
25
+ options = ConfigurationOptions.new(ARGV)
26
+ super(options)
27
+ end
28
+
29
+ def run_specs(iterator)
30
+ @configuration.reporter.report(0) do |reporter|
31
+ @configuration.with_suite_hooks do
32
+ iterator.map { |g|
33
+ start = Time.now
34
+ if g.is_a? ::RSpec::Core::Example
35
+ print " #{g.full_description}: "
36
+ example = g
37
+ g = example.example_group
38
+ ::RSpec.world.filtered_examples.clear
39
+ ::RSpec.world.filtered_examples[g] = [example]
40
+ else
41
+ print " #{g.description}: "
42
+ end
43
+ ret = g.run(reporter)
44
+ diff = Time.now-start
45
+ puts(" <%.3f>" % diff)
46
+
47
+ ret
48
+ }.all? ? 0 : @configuration.failure_exit_code
49
+ end
50
+ end
51
+ end
52
+ alias_method :run_each, :run_specs
53
+ end
54
+ end
@@ -0,0 +1,74 @@
1
+ require 'test_queue'
2
+ require 'test_queue/runner'
3
+
4
+ module TestQueue
5
+ class Runner
6
+ class Sample < Runner
7
+ def spawn_workers
8
+ puts "Spawning #@concurrency workers"
9
+ super
10
+ end
11
+
12
+ def after_fork(num)
13
+ puts " -- worker #{num} booted as pid #{$$}"
14
+ super
15
+ end
16
+
17
+ def run_worker(iterator)
18
+ sum = 0
19
+ iterator.each do |item|
20
+ puts " #{item.inspect}"
21
+ sum += item
22
+ end
23
+ sum
24
+ end
25
+
26
+ def summarize_worker(worker)
27
+ worker.summary = worker.output.scan(/^\s*(\d+)/).join(', ')
28
+ worker.failure_output = ''
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if __FILE__ == $0
35
+ TestQueue::Runner::Sample.new(Array(1..10)).execute
36
+ end
37
+
38
+ __END__
39
+
40
+ Spawning 4 workers
41
+ -- worker 0 booted as pid 40406
42
+ -- worker 1 booted as pid 40407
43
+ -- worker 2 booted as pid 40408
44
+ -- worker 3 booted as pid 40409
45
+
46
+ ==> Starting ruby test-queue worker [1] (40407)
47
+
48
+ 2
49
+ 5
50
+ 8
51
+
52
+ ==> Starting ruby test-queue worker [3] (40409)
53
+
54
+
55
+ ==> Starting ruby test-queue worker [2] (40408)
56
+
57
+ 3
58
+ 6
59
+ 9
60
+
61
+ ==> Starting ruby test-queue worker [0] (40406)
62
+
63
+ 1
64
+ 4
65
+ 7
66
+ 10
67
+
68
+ ==> Summary
69
+
70
+ [1] 2, 5, 8 in 0.0024s (pid 40407 exit 15)
71
+ [3] in 0.0036s (pid 40409 exit 0)
72
+ [2] 3, 6, 9 in 0.0038s (pid 40408 exit 18)
73
+ [0] 1, 4, 7, 10 in 0.0044s (pid 40406 exit 22)
74
+
@@ -0,0 +1,74 @@
1
+ require 'test_queue/runner'
2
+
3
+ gem 'test-unit'
4
+ require 'test/unit'
5
+ require 'test/unit/collector/descendant'
6
+ require 'test/unit/testresult'
7
+ require 'test/unit/testsuite'
8
+ require 'test/unit/ui/console/testrunner'
9
+
10
+ class Test::Unit::TestSuite
11
+ attr_accessor :iterator
12
+
13
+ def run(result, &progress_block)
14
+ @start_time = Time.now
15
+ yield(STARTED, name)
16
+ yield(STARTED_OBJECT, self)
17
+ run_startup(result)
18
+ (@iterator || @tests).each do |test|
19
+ @n_tests += test.size
20
+ run_test(test, result, &progress_block)
21
+ @passed = false unless test.passed?
22
+ end
23
+ run_shutdown(result)
24
+ ensure
25
+ @elapsed_time = Time.now - @start_time
26
+ yield(FINISHED, name)
27
+ yield(FINISHED_OBJECT, self)
28
+ end
29
+
30
+ def failure_count
31
+ (@iterator || @tests).map {|t| t.instance_variable_get(:@_result).failure_count}.inject(0, :+)
32
+ end
33
+ end
34
+
35
+ module TestQueue
36
+ class Runner
37
+ class TestUnit < Runner
38
+ def initialize
39
+ if Test::Unit::Collector::Descendant.new.collect.tests.any?
40
+ fail "Do not `require` test files. Pass them via ARGV instead and they will be required as needed."
41
+ end
42
+ super(TestFramework::TestUnit.new)
43
+ end
44
+
45
+ def run_worker(iterator)
46
+ @suite = Test::Unit::TestSuite.new("specified by test-queue master")
47
+ @suite.iterator = iterator
48
+ res = Test::Unit::UI::Console::TestRunner.new(@suite).start
49
+ res.run_count - res.pass_count
50
+ end
51
+
52
+ def summarize_worker(worker)
53
+ worker.summary = worker.output.split("\n").grep(/^\d+ tests?/).first
54
+ worker.failure_output = worker.output.scan(/^Failure:\n(.*)\n=======================*/m).join("\n")
55
+ end
56
+ end
57
+ end
58
+
59
+ class TestFramework
60
+ class TestUnit < TestFramework
61
+ def all_suite_files
62
+ ARGV
63
+ end
64
+
65
+ def suites_from_file(path)
66
+ Test::Unit::TestCase::DESCENDANTS.clear
67
+ require File.absolute_path(path)
68
+ Test::Unit::Collector::Descendant.new.collect.tests.map { |suite|
69
+ [suite.name, suite]
70
+ }
71
+ end
72
+ end
73
+ end
74
+ end