test-queue 0.2.9 → 0.2.10
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.
- data/Gemfile +1 -0
- data/Gemfile-minitest4 +3 -0
- data/Gemfile-minitest4.lock +38 -0
- data/Gemfile.lock +3 -3
- data/README.md +11 -3
- data/lib/test_queue/iterator.rb +2 -2
- data/lib/test_queue/runner.rb +63 -14
- data/lib/test_queue/runner/cucumber.rb +2 -4
- data/lib/test_queue/runner/minitest.rb +8 -55
- data/lib/test_queue/runner/minitest4.rb +65 -0
- data/lib/test_queue/runner/minitest5.rb +71 -0
- data/lib/test_queue/runner/puppet_lint.rb +2 -3
- data/lib/test_queue/runner/rspec.rb +10 -39
- data/lib/test_queue/runner/rspec2.rb +35 -0
- data/lib/test_queue/runner/rspec3.rb +35 -0
- data/lib/test_queue/runner/sample.rb +2 -4
- data/test-multi.sh +2 -2
- data/test-queue.gemspec +3 -3
- data/test.sh +8 -2
- data/test/{sample_test.rb → sample_minitest4.rb} +0 -0
- data/test/sample_minitest5.rb +23 -0
- data/test/sample_spec.rb +3 -3
- metadata +31 -10
- checksums.yaml +0 -15
data/Gemfile
CHANGED
data/Gemfile-minitest4
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
test-queue (0.2.9)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
builder (3.2.2)
|
10
|
+
cucumber (1.3.15)
|
11
|
+
builder (>= 2.1.2)
|
12
|
+
diff-lcs (>= 1.1.3)
|
13
|
+
gherkin (~> 2.12)
|
14
|
+
multi_json (>= 1.7.5, < 2.0)
|
15
|
+
multi_test (>= 0.1.1)
|
16
|
+
diff-lcs (1.2.5)
|
17
|
+
gherkin (2.12.2)
|
18
|
+
multi_json (~> 1.3)
|
19
|
+
minitest (4.7.3)
|
20
|
+
multi_json (1.10.1)
|
21
|
+
multi_test (0.1.1)
|
22
|
+
rspec (2.99.0)
|
23
|
+
rspec-core (~> 2.99.0)
|
24
|
+
rspec-expectations (~> 2.99.0)
|
25
|
+
rspec-mocks (~> 2.99.0)
|
26
|
+
rspec-core (2.99.1)
|
27
|
+
rspec-expectations (2.99.1)
|
28
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
29
|
+
rspec-mocks (2.99.1)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
cucumber (~> 1.3.10)
|
36
|
+
minitest (= 4.7.3)
|
37
|
+
rspec (>= 2.13, < 4.0)
|
38
|
+
test-queue!
|
data/Gemfile.lock
CHANGED
@@ -16,7 +16,7 @@ GEM
|
|
16
16
|
diff-lcs (1.2.4)
|
17
17
|
gherkin (2.12.2)
|
18
18
|
multi_json (~> 1.3)
|
19
|
-
minitest (
|
19
|
+
minitest (5.3.3)
|
20
20
|
multi_json (1.8.4)
|
21
21
|
multi_test (0.0.3)
|
22
22
|
rspec (2.13.0)
|
@@ -33,6 +33,6 @@ PLATFORMS
|
|
33
33
|
|
34
34
|
DEPENDENCIES
|
35
35
|
cucumber (~> 1.3.10)
|
36
|
-
minitest (
|
37
|
-
rspec (
|
36
|
+
minitest (= 5.3.3)
|
37
|
+
rspec (>= 2.13, < 4.0)
|
38
38
|
test-queue!
|
data/README.md
CHANGED
@@ -32,6 +32,9 @@ the workload and relay results back to a central master.
|
|
32
32
|
- `TEST_QUEUE_RELAY`: relay results back to a central master, specified as tcp `address:port`
|
33
33
|
- `TEST_QUEUE_STATS`: `path` to cache build stats in-build CI runs (default: `.test_queue_stats`)
|
34
34
|
- `TEST_QUEUE_FORCE`: comma separated list of suites to run
|
35
|
+
- `TEST_QUEUE_RELAY_TIMEOUT`: when using remote workers, the amount of time a worker will try to reconnect to start work
|
36
|
+
- `TEST_QUEUE_RELAY_TOKEN`: when using remote workers, this must be the same on both workers and the server for remote workers to run tests.
|
37
|
+
- `TEST_QUEUE_SLAVE_MESSAGE`: when using remote workers, set this on a slave worker and it will appear on the slave's connection message on the master.
|
35
38
|
|
36
39
|
### usage
|
37
40
|
|
@@ -75,6 +78,9 @@ class MyAppTestRunner < TestQueue::Runner::MiniTest
|
|
75
78
|
concurrency.times do |i|
|
76
79
|
# ...
|
77
80
|
end
|
81
|
+
|
82
|
+
# If this is a remote slave, tell the master something about us
|
83
|
+
@slave_message = "Output for slave 123: http://myhost.com/build/123"
|
78
84
|
end
|
79
85
|
|
80
86
|
def around_filter(suite)
|
@@ -90,11 +96,13 @@ CustomMiniTestRunner.new.execute
|
|
90
96
|
### distributed mode
|
91
97
|
|
92
98
|
To use distributed mode, the central master must listen on a tcp port. Additional masters can be booted
|
93
|
-
in relay mode to connect to the central master.
|
99
|
+
in relay mode to connect to the central master. Workers must provide a TEST_QUEUE_RELAY_TOKEN to match
|
100
|
+
the master's.
|
94
101
|
|
95
102
|
```
|
96
|
-
$ TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
97
|
-
$ TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
103
|
+
$ TEST_QUEUE_RELAY_TOKEN=123 TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
104
|
+
$ TEST_QUEUE_RELAY_TOKEN=123 TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
105
|
+
$ TEST_QUEUE_RELAY_TOKEN=123 ./test-multi.sh
|
98
106
|
```
|
99
107
|
|
100
108
|
See the [Parameterized Trigger Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin)
|
data/lib/test_queue/iterator.rb
CHANGED
@@ -16,7 +16,7 @@ module TestQueue
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def each
|
19
|
-
fail
|
19
|
+
fail "already used this iterator. previous caller: #@done" if @done
|
20
20
|
|
21
21
|
while true
|
22
22
|
client = connect_to_master('POP')
|
@@ -44,7 +44,7 @@ module TestQueue
|
|
44
44
|
end
|
45
45
|
rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
|
46
46
|
ensure
|
47
|
-
@done =
|
47
|
+
@done = caller.first
|
48
48
|
File.open("/tmp/test_queue_worker_#{$$}_stats", "wb") do |f|
|
49
49
|
f.write Marshal.dump(@stats)
|
50
50
|
end
|
data/lib/test_queue/runner.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
5
|
module TestQueue
|
5
6
|
class Worker
|
6
7
|
attr_accessor :pid, :status, :output, :stats, :num, :host
|
7
8
|
attr_accessor :start_time, :end_time
|
9
|
+
attr_accessor :summary, :failure_output
|
8
10
|
|
9
11
|
def initialize(pid, num)
|
10
12
|
@pid = pid
|
@@ -50,6 +52,12 @@ module TestQueue
|
|
50
52
|
2
|
51
53
|
end
|
52
54
|
|
55
|
+
@slave_connection_timeout =
|
56
|
+
(ENV['TEST_QUEUE_RELAY_TIMEOUT'] && ENV['TEST_QUEUE_RELAY_TIMEOUT'].to_i) ||
|
57
|
+
30
|
58
|
+
|
59
|
+
@run_token = ENV['TEST_QUEUE_RELAY_TOKEN'] || SecureRandom.hex(8)
|
60
|
+
|
53
61
|
@socket =
|
54
62
|
socket ||
|
55
63
|
ENV['TEST_QUEUE_SOCKET'] ||
|
@@ -59,6 +67,8 @@ module TestQueue
|
|
59
67
|
relay ||
|
60
68
|
ENV['TEST_QUEUE_RELAY']
|
61
69
|
|
70
|
+
@slave_message = ENV["TEST_QUEUE_SLAVE_MESSAGE"] if ENV.has_key?("TEST_QUEUE_SLAVE_MESSAGE")
|
71
|
+
|
62
72
|
if @relay == @socket
|
63
73
|
STDERR.puts "*** Detected TEST_QUEUE_RELAY == TEST_QUEUE_SOCKET. Disabling relay mode."
|
64
74
|
@relay = nil
|
@@ -94,12 +104,12 @@ module TestQueue
|
|
94
104
|
|
95
105
|
@failures = ''
|
96
106
|
@completed.each do |worker|
|
97
|
-
|
98
|
-
@failures <<
|
107
|
+
summarize_worker(worker)
|
108
|
+
@failures << worker.failure_output if worker.failure_output
|
99
109
|
|
100
110
|
puts " [%2d] %60s %4d suites in %.4fs (pid %d exit %d%s)" % [
|
101
111
|
worker.num,
|
102
|
-
summary,
|
112
|
+
worker.summary,
|
103
113
|
worker.stats.size,
|
104
114
|
worker.end_time - worker.start_time,
|
105
115
|
worker.pid,
|
@@ -183,7 +193,15 @@ module TestQueue
|
|
183
193
|
return unless relay?
|
184
194
|
|
185
195
|
sock = connect_to_relay
|
186
|
-
|
196
|
+
message = " #{@slave_message}" if @slave_message
|
197
|
+
message.gsub!(/(\r|\n)/, "") # Our "protocol" is newline-separated
|
198
|
+
sock.puts("SLAVE #{@concurrency} #{Socket.gethostname} #{@run_token}#{message}")
|
199
|
+
response = sock.gets.strip
|
200
|
+
unless response == "OK"
|
201
|
+
STDERR.puts "*** Got non-OK response from master: #{response}"
|
202
|
+
sock.close
|
203
|
+
exit! 1
|
204
|
+
end
|
187
205
|
sock.close
|
188
206
|
rescue Errno::ECONNREFUSED
|
189
207
|
STDERR.puts "*** Unable to connect to relay #{@relay}. Aborting.."
|
@@ -209,7 +227,7 @@ module TestQueue
|
|
209
227
|
after_fork_internal(num, iterator)
|
210
228
|
ret = run_worker(iterator) || 0
|
211
229
|
cleanup_worker
|
212
|
-
exit! ret
|
230
|
+
Kernel.exit! ret
|
213
231
|
end
|
214
232
|
|
215
233
|
@workers[pid] = Worker.new(pid, num)
|
@@ -233,6 +251,9 @@ module TestQueue
|
|
233
251
|
after_fork(num)
|
234
252
|
end
|
235
253
|
|
254
|
+
# Run in the master before the fork. Used to create
|
255
|
+
# concurrency copies of any databases required by the
|
256
|
+
# test workers.
|
236
257
|
def prepare(concurrency)
|
237
258
|
end
|
238
259
|
|
@@ -240,9 +261,15 @@ module TestQueue
|
|
240
261
|
yield
|
241
262
|
end
|
242
263
|
|
264
|
+
# Prepare a worker for executing jobs after a fork.
|
243
265
|
def after_fork(num)
|
244
266
|
end
|
245
267
|
|
268
|
+
# Entry point for internal runner implementations. The iterator will yield
|
269
|
+
# jobs from the shared queue on the master.
|
270
|
+
#
|
271
|
+
# Returns nothing. exits 0 on success.
|
272
|
+
# exits N on error, where N is the number of failures.
|
246
273
|
def run_worker(iterator)
|
247
274
|
iterator.each do |item|
|
248
275
|
puts " #{item.inspect}"
|
@@ -255,10 +282,8 @@ module TestQueue
|
|
255
282
|
end
|
256
283
|
|
257
284
|
def summarize_worker(worker)
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
[ num_tests, failures ]
|
285
|
+
worker.summary = ''
|
286
|
+
worker.failure_output = ''
|
262
287
|
end
|
263
288
|
|
264
289
|
def reap_worker(blocking=true)
|
@@ -297,16 +322,28 @@ module TestQueue
|
|
297
322
|
sock = @server.accept
|
298
323
|
cmd = sock.gets.strip
|
299
324
|
case cmd
|
300
|
-
when
|
325
|
+
when /^POP/
|
326
|
+
# If we have a slave from a different test run, don't respond, and it will consider the test run done.
|
301
327
|
if obj = @queue.shift
|
302
328
|
data = Marshal.dump(obj.to_s)
|
303
329
|
sock.write(data)
|
304
330
|
end
|
305
|
-
when /^SLAVE (\d+) ([\w\.-]+)
|
331
|
+
when /^SLAVE (\d+) ([\w\.-]+) (\w+)(?: (.+))?/
|
306
332
|
num = $1.to_i
|
307
333
|
slave = $2
|
308
|
-
|
309
|
-
|
334
|
+
run_token = $3
|
335
|
+
slave_message = $4
|
336
|
+
if run_token == @run_token
|
337
|
+
# If we have a slave from a different test run, don't respond, and it will consider the test run done.
|
338
|
+
sock.write("OK\n")
|
339
|
+
remote_workers += num
|
340
|
+
else
|
341
|
+
STDERR.puts "*** Worker from run #{run_token} connected to master for run #{@run_token}; ignoring."
|
342
|
+
sock.write("WRONG RUN\n")
|
343
|
+
end
|
344
|
+
message = "*** #{num} workers connected from #{slave} after #{Time.now-@start_time}s"
|
345
|
+
message << " " + slave_message if slave_message
|
346
|
+
STDERR.puts message
|
310
347
|
when /^WORKER (\d+)/
|
311
348
|
data = sock.read($1.to_i)
|
312
349
|
worker = Marshal.load(data)
|
@@ -329,7 +366,19 @@ module TestQueue
|
|
329
366
|
end
|
330
367
|
|
331
368
|
def connect_to_relay
|
332
|
-
|
369
|
+
sock = nil
|
370
|
+
start = Time.now
|
371
|
+
puts "Attempting to connect for #{@slave_connection_timeout}s..."
|
372
|
+
while sock.nil?
|
373
|
+
begin
|
374
|
+
sock = TCPSocket.new(*@relay.split(':'))
|
375
|
+
rescue Errno::ECONNREFUSED => e
|
376
|
+
raise e if Time.now - start > @slave_connection_timeout
|
377
|
+
puts "Master not yet available, sleeping..."
|
378
|
+
sleep 0.5
|
379
|
+
end
|
380
|
+
end
|
381
|
+
sock
|
333
382
|
end
|
334
383
|
|
335
384
|
def relay_to_master(worker)
|
@@ -38,10 +38,8 @@ module TestQueue
|
|
38
38
|
end
|
39
39
|
|
40
40
|
output = worker.output.gsub(/\e\[\d+./,'')
|
41
|
-
summary = output.split("\n").grep(/^\d+ (scenarios?|steps?)/).first
|
42
|
-
|
43
|
-
|
44
|
-
[ summary, failures ]
|
41
|
+
worker.summary = output.split("\n").grep(/^\d+ (scenarios?|steps?)/).first
|
42
|
+
worker.failure_output = output.scan(/^Failing Scenarios:\n(.*)\n\d+ scenarios?/m).join("\n")
|
45
43
|
end
|
46
44
|
end
|
47
45
|
end
|
@@ -1,71 +1,24 @@
|
|
1
|
-
|
2
|
-
require 'minitest
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
self.class.output = $stdout
|
8
|
-
super
|
9
|
-
end
|
10
|
-
|
11
|
-
def _run_anything(*)
|
12
|
-
ret = super
|
13
|
-
output.puts
|
14
|
-
ret
|
15
|
-
end
|
16
|
-
|
17
|
-
def _run_suite(suite, type)
|
18
|
-
output.print ' '
|
19
|
-
output.print suite
|
20
|
-
output.print ': '
|
21
|
-
|
22
|
-
start = Time.now
|
23
|
-
ret = super
|
24
|
-
diff = Time.now - start
|
25
|
-
|
26
|
-
output.puts(" <%.3f>" % diff)
|
27
|
-
ret
|
28
|
-
end
|
29
|
-
|
30
|
-
self.runner = self.new
|
31
|
-
self.output = StringIO.new
|
32
|
-
end
|
33
|
-
|
34
|
-
class MiniTest::Unit::TestCase
|
35
|
-
class << self
|
36
|
-
attr_accessor :test_suites
|
37
|
-
|
38
|
-
def original_test_suites
|
39
|
-
@@test_suites.keys.reject{ |s| s.test_methods.empty? }
|
40
|
-
end
|
41
|
-
end
|
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'
|
42
7
|
end
|
43
8
|
|
44
9
|
module TestQueue
|
45
10
|
class Runner
|
46
11
|
class MiniTest < Runner
|
47
|
-
def initialize
|
48
|
-
tests = ::MiniTest::Unit::TestCase.original_test_suites.sort_by{ |s| -(stats[s.to_s] || 0) }
|
49
|
-
super(tests)
|
50
|
-
end
|
51
|
-
|
52
|
-
def run_worker(iterator)
|
53
|
-
::MiniTest::Unit::TestCase.test_suites = iterator
|
54
|
-
::MiniTest::Unit.new.run
|
55
|
-
end
|
56
|
-
|
57
12
|
def summarize_worker(worker)
|
58
13
|
worker.stats.each do |s, val|
|
59
14
|
stats[s.to_s] = val
|
60
15
|
end
|
61
16
|
|
62
|
-
|
17
|
+
worker.summary = worker.lines.grep(/, \d+ errors?, /).first
|
63
18
|
failures = worker.lines.select{ |line|
|
64
19
|
line if (line =~ /^Finished/) ... (line =~ /, \d+ errors?, /)
|
65
20
|
}[1..-2]
|
66
|
-
|
67
|
-
|
68
|
-
[ num_tests, failures ]
|
21
|
+
worker.failure_output = failures.join("\n") if failures
|
69
22
|
end
|
70
23
|
end
|
71
24
|
end
|
@@ -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,71 @@
|
|
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
|
+
@@orig_runnables = @@runnables
|
15
|
+
@@runnables = runnables
|
16
|
+
end
|
17
|
+
def self.orig_runnables
|
18
|
+
@@orig_runnables
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ProgressReporter
|
23
|
+
# Override original method to make test-queue specific output
|
24
|
+
def record result
|
25
|
+
io.print ' '
|
26
|
+
io.print result.class
|
27
|
+
io.print ': '
|
28
|
+
io.print result.result_code
|
29
|
+
io.puts(" <%.3f>" % result.time)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'minitest/minitest_reporter_plugin'
|
35
|
+
|
36
|
+
class << self
|
37
|
+
private
|
38
|
+
def total_count(options)
|
39
|
+
filter = options[:filter] || '/./'
|
40
|
+
filter = Regexp.new $1 if filter =~ /\/(.*)\//
|
41
|
+
|
42
|
+
Minitest::Test.orig_runnables.map(&:runnable_methods).flatten.find_all { |m|
|
43
|
+
filter === m || filter === "#{self}##{m}"
|
44
|
+
}.size
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue LoadError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module TestQueue
|
52
|
+
class Runner
|
53
|
+
class MiniTest < Runner
|
54
|
+
def initialize
|
55
|
+
tests = ::MiniTest::Test.runnables.reject { |s|
|
56
|
+
s.runnable_methods.empty?
|
57
|
+
}.sort_by { |s|
|
58
|
+
-(stats[s.to_s] || 0)
|
59
|
+
}.partition { |s|
|
60
|
+
s.test_order == :parallel
|
61
|
+
}.reverse.flatten
|
62
|
+
super(tests)
|
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
|
+
end
|
@@ -23,9 +23,8 @@ module TestQueue
|
|
23
23
|
errors = lines.select{ |line| line =~ /^ERROR/ }
|
24
24
|
warnings = lines.select{ |line| line =~ /^WARNING/ }
|
25
25
|
|
26
|
-
summary = "#{files.size} files, #{warnings.size} warnings, #{errors.size} errors"
|
27
|
-
|
28
|
-
[summary, errors.join("\n")]
|
26
|
+
worker.summary = "#{files.size} files, #{warnings.size} warnings, #{errors.size} errors"
|
27
|
+
worker.failure_output = errors.join("\n")
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
@@ -1,40 +1,13 @@
|
|
1
1
|
require 'test_queue/runner'
|
2
2
|
require 'rspec/core'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def example_groups
|
13
|
-
@options.configure(@configuration)
|
14
|
-
@configuration.load_spec_files
|
15
|
-
@world.announce_filters
|
16
|
-
@world.example_groups
|
17
|
-
end
|
18
|
-
|
19
|
-
def run_each(iterator)
|
20
|
-
@configuration.reporter.report(0, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
|
21
|
-
begin
|
22
|
-
@configuration.run_hook(:before, :suite)
|
23
|
-
iterator.map {|g|
|
24
|
-
print " #{g.description}: "
|
25
|
-
start = Time.now
|
26
|
-
ret = g.run(reporter)
|
27
|
-
diff = Time.now-start
|
28
|
-
puts(" <%.3f>" % diff)
|
29
|
-
|
30
|
-
ret
|
31
|
-
}.all? ? 0 : @configuration.failure_exit_code
|
32
|
-
ensure
|
33
|
-
@configuration.run_hook(:after, :suite)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
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'
|
38
11
|
end
|
39
12
|
|
40
13
|
module TestQueue
|
@@ -46,7 +19,7 @@ module TestQueue
|
|
46
19
|
end
|
47
20
|
|
48
21
|
def run_worker(iterator)
|
49
|
-
@rspec.run_each(iterator)
|
22
|
+
@rspec.run_each(iterator).to_i
|
50
23
|
end
|
51
24
|
|
52
25
|
def summarize_worker(worker)
|
@@ -54,10 +27,8 @@ module TestQueue
|
|
54
27
|
stats[s] = val
|
55
28
|
end
|
56
29
|
|
57
|
-
summary = worker.lines.grep(/ examples?, /).first
|
58
|
-
|
59
|
-
|
60
|
-
[ summary, failures ]
|
30
|
+
worker.summary = worker.lines.grep(/ examples?, /).first
|
31
|
+
worker.failure_output = worker.output[/^Failures:\n\n(.*)\n^Finished/m, 1]
|
61
32
|
end
|
62
33
|
end
|
63
34
|
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,35 @@
|
|
1
|
+
module RSpec::Core
|
2
|
+
class QueueRunner < Runner
|
3
|
+
def initialize
|
4
|
+
options = ::RSpec::Core::ConfigurationOptions.new(ARGV)
|
5
|
+
super(options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def example_groups
|
9
|
+
setup($stderr, $stdout)
|
10
|
+
@world.ordered_example_groups
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_specs(iterator)
|
14
|
+
@configuration.reporter.report(@world.ordered_example_groups.count) do |reporter|
|
15
|
+
begin
|
16
|
+
hook_context = SuiteHookContext.new
|
17
|
+
@configuration.hooks.run(:before, :suite, hook_context)
|
18
|
+
|
19
|
+
iterator.map { |g|
|
20
|
+
print " #{g.description}: "
|
21
|
+
start = Time.now
|
22
|
+
ret = g.run(reporter)
|
23
|
+
diff = Time.now-start
|
24
|
+
puts(" <%.3f>" % diff)
|
25
|
+
|
26
|
+
ret
|
27
|
+
}.all? ? 0 : @configuration.failure_exit_code
|
28
|
+
ensure
|
29
|
+
@configuration.hooks.run(:after, :suite, hook_context)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :run_each, :run_specs
|
34
|
+
end
|
35
|
+
end
|
@@ -26,10 +26,8 @@ module TestQueue
|
|
26
26
|
def summarize_worker(worker)
|
27
27
|
stats.update(worker.stats)
|
28
28
|
|
29
|
-
summary = worker.output.scan(/^\s*(\d+)/).join(', ')
|
30
|
-
|
31
|
-
|
32
|
-
[ summary, failures ]
|
29
|
+
worker.summary = worker.output.scan(/^\s*(\d+)/).join(', ')
|
30
|
+
worker.failure_output = ''
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
data/test-multi.sh
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
set -x
|
3
3
|
|
4
4
|
# export TEST_QUEUE_VERBOSE=1
|
5
|
-
TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec minitest-queue ./test/
|
5
|
+
TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_minitest5.rb &
|
6
6
|
sleep 0.1
|
7
|
-
TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/
|
7
|
+
TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_minitest5.rb
|
8
8
|
wait
|
data/test-queue.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
2
2
|
s.name = 'test-queue'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.10'
|
4
4
|
s.summary = 'parallel test runner'
|
5
5
|
s.description = 'minitest/rspec parallel test runner for CI environments'
|
6
6
|
|
@@ -16,8 +16,8 @@ spec = Gem::Specification.new do |s|
|
|
16
16
|
s.executables << 'minitest-queue'
|
17
17
|
s.executables << 'cucumber-queue'
|
18
18
|
|
19
|
-
s.add_development_dependency 'rspec', '
|
20
|
-
s.add_development_dependency 'minitest', '
|
19
|
+
s.add_development_dependency 'rspec', '>= 2.13', '< 4.0'
|
20
|
+
s.add_development_dependency 'minitest', '>= 4.7.3'
|
21
21
|
s.add_development_dependency 'cucumber', '~> 1.3.10'
|
22
22
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
data/test.sh
CHANGED
@@ -3,9 +3,15 @@ set -x
|
|
3
3
|
|
4
4
|
export TEST_QUEUE_WORKERS=2 TEST_QUEUE_VERBOSE=1
|
5
5
|
|
6
|
+
bundle install --gemfile=Gemfile-minitest4
|
7
|
+
bundle exec minitest-queue ./test/*_minitest4.rb
|
8
|
+
bundle exec minitest-queue ./test/*_minispec.rb
|
9
|
+
|
10
|
+
bundle install
|
11
|
+
bundle exec minitest-queue ./test/*_minitest4.rb
|
12
|
+
bundle exec minitest-queue ./test/*_minitest5.rb
|
6
13
|
bundle exec minitest-queue ./test/*_minispec.rb
|
7
|
-
bundle exec minitest-queue ./test/*_test.rb
|
8
14
|
bundle exec rspec-queue test
|
9
15
|
bundle exec cucumber-queue
|
10
16
|
|
11
|
-
TEST_QUEUE_WORKERS=1 TEST_QUEUE_FORCE="MiniTestSleep21,MiniTestSleep8,MiniTestFailure" bundle exec minitest-queue ./test/*
|
17
|
+
TEST_QUEUE_WORKERS=1 TEST_QUEUE_FORCE="MiniTestSleep21,MiniTestSleep8,MiniTestFailure" bundle exec minitest-queue ./test/*_minitest5.rb
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
class MiniTestEqual < MiniTest::Test
|
4
|
+
def test_equal
|
5
|
+
assert_equal 1, 1
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
30.times do |i|
|
10
|
+
Object.const_set("MiniTestSleep#{i}", Class.new(MiniTest::Test) do
|
11
|
+
define_method('test_sleep') do
|
12
|
+
start = Time.now
|
13
|
+
sleep(0.25)
|
14
|
+
assert_in_delta Time.now-start, 0.25, 0.02
|
15
|
+
end
|
16
|
+
end)
|
17
|
+
end
|
18
|
+
|
19
|
+
class MiniTestFailure < MiniTest::Test
|
20
|
+
def test_fail
|
21
|
+
assert_equal 0, 1
|
22
|
+
end
|
23
|
+
end
|
data/test/sample_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'rspec'
|
|
2
2
|
|
3
3
|
describe 'RSpecEqual' do
|
4
4
|
it 'checks equality' do
|
5
|
-
1.
|
5
|
+
expect(1).to eq 1
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -11,13 +11,13 @@ end
|
|
11
11
|
it "sleeps" do
|
12
12
|
start = Time.now
|
13
13
|
sleep(0.25)
|
14
|
-
(Time.now-start).
|
14
|
+
expect(Time.now-start).to be_within(0.02).of(0.25)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe 'RSpecFailure' do
|
20
20
|
it 'fails' do
|
21
|
-
:foo.
|
21
|
+
expect(:foo).to eq :bar
|
22
22
|
end
|
23
23
|
end
|
metadata
CHANGED
@@ -1,46 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test-queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Aman Gupta
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2014-
|
12
|
+
date: 2014-12-20 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: rspec
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
16
18
|
requirements:
|
17
|
-
- -
|
19
|
+
- - ! '>='
|
18
20
|
- !ruby/object:Gem::Version
|
19
21
|
version: '2.13'
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '4.0'
|
20
25
|
type: :development
|
21
26
|
prerelease: false
|
22
27
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
23
29
|
requirements:
|
24
|
-
- -
|
30
|
+
- - ! '>='
|
25
31
|
- !ruby/object:Gem::Version
|
26
32
|
version: '2.13'
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '4.0'
|
27
36
|
- !ruby/object:Gem::Dependency
|
28
37
|
name: minitest
|
29
38
|
requirement: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
30
40
|
requirements:
|
31
|
-
- -
|
41
|
+
- - ! '>='
|
32
42
|
- !ruby/object:Gem::Version
|
33
43
|
version: 4.7.3
|
34
44
|
type: :development
|
35
45
|
prerelease: false
|
36
46
|
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
37
48
|
requirements:
|
38
|
-
- -
|
49
|
+
- - ! '>='
|
39
50
|
- !ruby/object:Gem::Version
|
40
51
|
version: 4.7.3
|
41
52
|
- !ruby/object:Gem::Dependency
|
42
53
|
name: cucumber
|
43
54
|
requirement: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
44
56
|
requirements:
|
45
57
|
- - ~>
|
46
58
|
- !ruby/object:Gem::Version
|
@@ -48,6 +60,7 @@ dependencies:
|
|
48
60
|
type: :development
|
49
61
|
prerelease: false
|
50
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
51
64
|
requirements:
|
52
65
|
- - ~>
|
53
66
|
- !ruby/object:Gem::Version
|
@@ -62,6 +75,8 @@ extensions: []
|
|
62
75
|
extra_rdoc_files: []
|
63
76
|
files:
|
64
77
|
- Gemfile
|
78
|
+
- Gemfile-minitest4
|
79
|
+
- Gemfile-minitest4.lock
|
65
80
|
- Gemfile.lock
|
66
81
|
- README.md
|
67
82
|
- bin/cucumber-queue
|
@@ -77,38 +92,44 @@ files:
|
|
77
92
|
- lib/test_queue/runner.rb
|
78
93
|
- lib/test_queue/runner/cucumber.rb
|
79
94
|
- lib/test_queue/runner/minitest.rb
|
95
|
+
- lib/test_queue/runner/minitest4.rb
|
96
|
+
- lib/test_queue/runner/minitest5.rb
|
80
97
|
- lib/test_queue/runner/puppet_lint.rb
|
81
98
|
- lib/test_queue/runner/rspec.rb
|
99
|
+
- lib/test_queue/runner/rspec2.rb
|
100
|
+
- lib/test_queue/runner/rspec3.rb
|
82
101
|
- lib/test_queue/runner/sample.rb
|
83
102
|
- test-multi.sh
|
84
103
|
- test-queue.gemspec
|
85
104
|
- test.sh
|
86
105
|
- test/sample_minispec.rb
|
106
|
+
- test/sample_minitest4.rb
|
107
|
+
- test/sample_minitest5.rb
|
87
108
|
- test/sample_spec.rb
|
88
|
-
- test/sample_test.rb
|
89
109
|
homepage: http://github.com/tmm1/test-queue
|
90
110
|
licenses:
|
91
111
|
- MIT
|
92
|
-
metadata: {}
|
93
112
|
post_install_message:
|
94
113
|
rdoc_options: []
|
95
114
|
require_paths:
|
96
115
|
- lib
|
97
116
|
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
98
118
|
requirements:
|
99
119
|
- - ! '>='
|
100
120
|
- !ruby/object:Gem::Version
|
101
121
|
version: '0'
|
102
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
103
124
|
requirements:
|
104
125
|
- - ! '>='
|
105
126
|
- !ruby/object:Gem::Version
|
106
127
|
version: '0'
|
107
128
|
requirements: []
|
108
129
|
rubyforge_project:
|
109
|
-
rubygems_version:
|
130
|
+
rubygems_version: 1.8.23
|
110
131
|
signing_key:
|
111
|
-
specification_version:
|
132
|
+
specification_version: 3
|
112
133
|
summary: parallel test runner
|
113
134
|
test_files: []
|
114
135
|
has_rdoc: false
|
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
MjQ3ZGNkZDQ3OGNjMWU2YmIxZDU2NGZjOTZjYTk5ZTNhY2E4NzQxZQ==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MjJiZjRjM2M4Y2E0ODA5ZjQyYTJkMDkzOTM0MjA0Nzc2NjM1NDg3ZA==
|
7
|
-
SHA512:
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MWYwODQ5NjBmMmEwMTNiYWFjZWQ1NGNmMTcyNjRiMWRhNDM4OWVkY2E3YTNi
|
10
|
-
NDViMTBjZDNjMGU1ZDc1MTNmYjIzYTMzNDViMzMyYmEyMjY1Mjk2MjFhYmIz
|
11
|
-
MTcxODg3NzA3MTI5MjkzMTc2MGRjMWNiNGIxOTNmYWFiMmI4MWE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YzhhNTMxM2U3ZDNlNzg1YTcwY2Q4OTNiY2M1YTkwMmZlZTFhNjUzNTk5NzEw
|
14
|
-
MTk1YTNlZjZjYTcwMzhkNTM4MTQ5Yjg1ZjFjZWU4Y2MyOGY4MWFjNGRhOGZl
|
15
|
-
YTI2NWE5N2I2N2Y4MWVhMTEwZjA4ZWU0NjU0YWIwMGEzZjY1Njg=
|