test-queue-split 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile-minitest4 +3 -0
- data/Gemfile-minitest4.lock +38 -0
- data/Gemfile-rspec3-0 +3 -0
- data/Gemfile-rspec3-0.lock +40 -0
- data/Gemfile-rspec3-1 +3 -0
- data/Gemfile-rspec3-1.lock +40 -0
- data/Gemfile-rspec3-2 +3 -0
- data/Gemfile-rspec3-2.lock +41 -0
- data/Gemfile-testunit +3 -0
- data/Gemfile-testunit.lock +44 -0
- data/Gemfile.lock +54 -0
- data/README.md +114 -0
- data/Thorfile +9 -0
- data/bin/cucumber-queue +4 -0
- data/bin/minitest-queue +5 -0
- data/bin/rspec-queue +4 -0
- data/bin/testunit-queue +5 -0
- data/features/bad.feature +5 -0
- data/features/sample.feature +25 -0
- data/features/sample2.feature +29 -0
- data/features/step_definitions/common.rb +15 -0
- data/lib/test-queue.rb +1 -0
- data/lib/test_queue/iterator.rb +72 -0
- data/lib/test_queue/runner/cucumber.rb +46 -0
- data/lib/test_queue/runner/minitest.rb +25 -0
- data/lib/test_queue/runner/minitest4.rb +65 -0
- data/lib/test_queue/runner/minitest5.rb +62 -0
- data/lib/test_queue/runner/puppet_lint.rb +31 -0
- data/lib/test_queue/runner/rspec.rb +35 -0
- data/lib/test_queue/runner/rspec2.rb +35 -0
- data/lib/test_queue/runner/rspec3.rb +45 -0
- data/lib/test_queue/runner/sample.rb +76 -0
- data/lib/test_queue/runner/testunit.rb +56 -0
- data/lib/test_queue/runner.rb +395 -0
- data/lib/test_queue/version.rb +4 -0
- data/lib/test_queue.rb +8 -0
- data/test/sample_minispec.rb +31 -0
- data/test/sample_minitest4.rb +23 -0
- data/test/sample_minitest5.rb +23 -0
- data/test/sample_spec.rb +23 -0
- data/test/sample_testunit.rb +23 -0
- data/test-multi.sh +8 -0
- data/test-queue-split.gemspec +28 -0
- data/test.sh +23 -0
- metadata +141 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test_queue/runner'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class MiniTestQueueRunner < MiniTest::Unit
|
5
|
+
def _run_suites(suites, type)
|
6
|
+
self.class.output = $stdout
|
7
|
+
|
8
|
+
if defined?(ParallelEach)
|
9
|
+
# Ignore its _run_suites implementation since we don't handle it gracefully.
|
10
|
+
# If we don't do this #partition is called on the iterator and all suites
|
11
|
+
# distributed immediately, instead of picked up as workers are available.
|
12
|
+
suites.map { |suite| _run_suite suite, type }
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def _run_anything(*)
|
19
|
+
ret = super
|
20
|
+
output.puts
|
21
|
+
ret
|
22
|
+
end
|
23
|
+
|
24
|
+
def _run_suite(suite, type)
|
25
|
+
output.print ' '
|
26
|
+
output.print suite
|
27
|
+
output.print ': '
|
28
|
+
|
29
|
+
start = Time.now
|
30
|
+
ret = super
|
31
|
+
diff = Time.now - start
|
32
|
+
|
33
|
+
output.puts(" <%.3f>" % diff)
|
34
|
+
ret
|
35
|
+
end
|
36
|
+
|
37
|
+
self.runner = self.new
|
38
|
+
self.output = StringIO.new
|
39
|
+
end
|
40
|
+
|
41
|
+
class MiniTest::Unit::TestCase
|
42
|
+
class << self
|
43
|
+
attr_accessor :test_suites
|
44
|
+
|
45
|
+
def original_test_suites
|
46
|
+
@@test_suites.keys.reject{ |s| s.test_methods.empty? }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module TestQueue
|
52
|
+
class Runner
|
53
|
+
class MiniTest < Runner
|
54
|
+
def initialize
|
55
|
+
tests = ::MiniTest::Unit::TestCase.original_test_suites.sort_by{ |s| -(stats[s.to_s] || 0) }
|
56
|
+
super(tests)
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_worker(iterator)
|
60
|
+
::MiniTest::Unit::TestCase.test_suites = iterator
|
61
|
+
::MiniTest::Unit.new.run
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'test_queue/runner'
|
2
|
+
|
3
|
+
module MiniTest
|
4
|
+
def self.__run reporter, options
|
5
|
+
suites = Runnable.runnables
|
6
|
+
|
7
|
+
# Run the serial tests first after they complete, run the parallels tests
|
8
|
+
# We already sort suites based on its test_order at TestQueue::Runner::Minitest#initialize.
|
9
|
+
suites.map { |suite| suite.run reporter, options }
|
10
|
+
end
|
11
|
+
|
12
|
+
class Test
|
13
|
+
def self.runnables= runnables
|
14
|
+
@@runnables = runnables
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ProgressReporter
|
19
|
+
# Override original method to make test-queue specific output
|
20
|
+
def record result
|
21
|
+
io.print ' '
|
22
|
+
io.print result.class
|
23
|
+
io.print ': '
|
24
|
+
io.print result.result_code
|
25
|
+
io.puts(" <%.3f>" % result.time)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'minitest/minitest_reporter_plugin'
|
31
|
+
|
32
|
+
class << self
|
33
|
+
private
|
34
|
+
def total_count(options)
|
35
|
+
0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue LoadError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module TestQueue
|
43
|
+
class Runner
|
44
|
+
class MiniTest < Runner
|
45
|
+
def initialize
|
46
|
+
tests = ::MiniTest::Test.runnables.reject { |s|
|
47
|
+
s.runnable_methods.empty?
|
48
|
+
}.sort_by { |s|
|
49
|
+
-(stats[s.to_s] || 0)
|
50
|
+
}.partition { |s|
|
51
|
+
s.test_order == :parallel
|
52
|
+
}.reverse.flatten
|
53
|
+
super(tests)
|
54
|
+
end
|
55
|
+
|
56
|
+
def run_worker(iterator)
|
57
|
+
::MiniTest::Test.runnables = iterator
|
58
|
+
::MiniTest.run ? 0 : 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
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,35 @@
|
|
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
|
+
@rspec = ::RSpec::Core::QueueRunner.new
|
18
|
+
super(@rspec.example_groups.sort_by{ |s| -(stats[s.to_s] || 0) })
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_worker(iterator)
|
22
|
+
@rspec.run_each(iterator).to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def summarize_worker(worker)
|
26
|
+
worker.stats.each do |s, val|
|
27
|
+
stats[s] = val
|
28
|
+
end
|
29
|
+
|
30
|
+
worker.summary = worker.lines.grep(/ examples?, /).first
|
31
|
+
worker.failure_output = worker.output[/^Failures:\n\n(.*)\n^Finished/m, 1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RSpec::Core
|
2
|
+
class QueueRunner < CommandLine
|
3
|
+
def initialize
|
4
|
+
super(ARGV)
|
5
|
+
@configuration.output_stream = $stdout
|
6
|
+
@configuration.error_stream = $stderr
|
7
|
+
end
|
8
|
+
|
9
|
+
def example_groups
|
10
|
+
@options.configure(@configuration)
|
11
|
+
@configuration.load_spec_files
|
12
|
+
@world.announce_filters
|
13
|
+
@world.example_groups
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_each(iterator)
|
17
|
+
@configuration.reporter.report(0, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
|
18
|
+
begin
|
19
|
+
@configuration.run_hook(:before, :suite)
|
20
|
+
iterator.map {|g|
|
21
|
+
print " #{g.description}: "
|
22
|
+
start = Time.now
|
23
|
+
ret = g.run(reporter)
|
24
|
+
diff = Time.now-start
|
25
|
+
puts(" <%.3f>" % diff)
|
26
|
+
|
27
|
+
ret
|
28
|
+
}.all? ? 0 : @configuration.failure_exit_code
|
29
|
+
ensure
|
30
|
+
@configuration.run_hook(:after, :suite)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RSpec::Core
|
2
|
+
# RSpec 3.2 introduced:
|
3
|
+
unless Configuration.method_defined?(:with_suite_hooks)
|
4
|
+
class Configuration
|
5
|
+
def with_suite_hooks
|
6
|
+
begin
|
7
|
+
hook_context = SuiteHookContext.new
|
8
|
+
hooks.run(:before, :suite, hook_context)
|
9
|
+
yield
|
10
|
+
ensure
|
11
|
+
hooks.run(:after, :suite, hook_context)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class QueueRunner < Runner
|
18
|
+
def initialize
|
19
|
+
options = ConfigurationOptions.new(ARGV)
|
20
|
+
super(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def example_groups
|
24
|
+
setup($stderr, $stdout)
|
25
|
+
@world.ordered_example_groups
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_specs(iterator)
|
29
|
+
@configuration.reporter.report(@world.ordered_example_groups.count) do |reporter|
|
30
|
+
@configuration.with_suite_hooks do
|
31
|
+
iterator.map { |g|
|
32
|
+
print " #{g.description}: "
|
33
|
+
start = Time.now
|
34
|
+
ret = g.run(reporter)
|
35
|
+
diff = Time.now-start
|
36
|
+
puts(" <%.3f>" % diff)
|
37
|
+
|
38
|
+
ret
|
39
|
+
}.all? ? 0 : @configuration.failure_exit_code
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias_method :run_each, :run_specs
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,76 @@
|
|
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
|
+
stats.update(worker.stats)
|
28
|
+
|
29
|
+
worker.summary = worker.output.scan(/^\s*(\d+)/).join(', ')
|
30
|
+
worker.failure_output = ''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if __FILE__ == $0
|
37
|
+
TestQueue::Runner::Sample.new(Array(1..10)).execute
|
38
|
+
end
|
39
|
+
|
40
|
+
__END__
|
41
|
+
|
42
|
+
Spawning 4 workers
|
43
|
+
-- worker 0 booted as pid 40406
|
44
|
+
-- worker 1 booted as pid 40407
|
45
|
+
-- worker 2 booted as pid 40408
|
46
|
+
-- worker 3 booted as pid 40409
|
47
|
+
|
48
|
+
==> Starting ruby test-queue worker [1] (40407)
|
49
|
+
|
50
|
+
2
|
51
|
+
5
|
52
|
+
8
|
53
|
+
|
54
|
+
==> Starting ruby test-queue worker [3] (40409)
|
55
|
+
|
56
|
+
|
57
|
+
==> Starting ruby test-queue worker [2] (40408)
|
58
|
+
|
59
|
+
3
|
60
|
+
6
|
61
|
+
9
|
62
|
+
|
63
|
+
==> Starting ruby test-queue worker [0] (40406)
|
64
|
+
|
65
|
+
1
|
66
|
+
4
|
67
|
+
7
|
68
|
+
10
|
69
|
+
|
70
|
+
==> Summary
|
71
|
+
|
72
|
+
[1] 2, 5, 8 in 0.0024s (pid 40407 exit 15)
|
73
|
+
[3] in 0.0036s (pid 40409 exit 0)
|
74
|
+
[2] 3, 6, 9 in 0.0038s (pid 40408 exit 18)
|
75
|
+
[0] 1, 4, 7, 10 in 0.0044s (pid 40406 exit 22)
|
76
|
+
|
@@ -0,0 +1,56 @@
|
|
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
|
+
end
|
30
|
+
|
31
|
+
module TestQueue
|
32
|
+
class Runner
|
33
|
+
class TestUnit < Runner
|
34
|
+
def initialize
|
35
|
+
@suite = Test::Unit::Collector::Descendant.new.collect
|
36
|
+
tests = @suite.tests.sort_by{ |s| -(stats[s.to_s] || 0) }
|
37
|
+
super(tests)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_worker(iterator)
|
41
|
+
@suite.iterator = iterator
|
42
|
+
res = Test::Unit::UI::Console::TestRunner.new(@suite).start
|
43
|
+
res.run_count - res.pass_count
|
44
|
+
end
|
45
|
+
|
46
|
+
def summarize_worker(worker)
|
47
|
+
worker.stats.each do |s, val|
|
48
|
+
stats[s.to_s] = val
|
49
|
+
end
|
50
|
+
|
51
|
+
worker.summary = worker.output.split("\n").grep(/^\d+ tests?/).first
|
52
|
+
worker.failure_output = worker.output.scan(/^Failure:\n(.*)\n=======================*/m).join("\n")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|