test-queue 0.2.9 → 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- 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=
|