test-queue 0.4.0 → 0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 550cb255057332f8736ac60ed578044f7ec94694
4
- data.tar.gz: c1e268f35f59f1eae129e2f4c6df801ac7bd32e5
3
+ metadata.gz: 14379da7829bc7f9e40a46dce33c344ff0bd434a
4
+ data.tar.gz: 7b21acc430c9bcada801f9aa48352afffdd78708
5
5
  SHA512:
6
- metadata.gz: 7dc83049443c7c45cbb8bd1a9a72316ce22d674caf7339af6943a15ddf8c2fbad6eec9160cf36245d0058ac645b10ea72b803ccd7f422c2bda91d9c863aa8601
7
- data.tar.gz: e68b2b79d5b981e054d1b3f24dab94a060983d4ca0a7d8861f5ef2b8286c2249fb142e2a3ec7f69437b89ca7620fdbd3dc73c1dc59f9ae0036ad5fd15470f947
6
+ metadata.gz: ac7142497a109a2c40ce726e74f32d5f86c18c21d6618820f85b3d13986c39214f4b6a1feeb3eb73d53ec4d93bb382a7b68e123ebb7502a0a6f2e49fe6062258
7
+ data.tar.gz: 3a9bfaee682cbde11361547ce43d4a7b6c61f60a8f31b8be9ea2081aec533741281fc6eec4ddcb9b3eb7c7186bfede9f5ea4bbcdaba2f27cbccda2fb785e51fa
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test-queue (0.3.1)
4
+ test-queue (0.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -2,7 +2,7 @@ module TestQueue
2
2
  class Iterator
3
3
  attr_reader :sock
4
4
 
5
- def initialize(test_framework, sock, filter=nil, early_failure_limit: nil)
5
+ def initialize(test_framework, sock, filter=nil, run_token:, early_failure_limit: nil)
6
6
  @test_framework = test_framework
7
7
  @done = false
8
8
  @suite_stats = []
@@ -14,6 +14,7 @@ module TestQueue
14
14
  end
15
15
  @failures = 0
16
16
  @early_failure_limit = early_failure_limit
17
+ @run_token = run_token
17
18
  end
18
19
 
19
20
  def each
@@ -29,7 +30,7 @@ module TestQueue
29
30
  connect_to_master("KABOOM")
30
31
  break
31
32
  else
32
- client = connect_to_master('POP')
33
+ client = connect_to_master("POP #{Socket.gethostname} #{Process.pid}")
33
34
  end
34
35
  break if client.nil?
35
36
  _r, _w, e = IO.select([client], nil, [client], nil)
@@ -79,6 +80,7 @@ module TestQueue
79
80
  else
80
81
  UNIXSocket.new(@sock)
81
82
  end
83
+ sock.puts("TOKEN=#{@run_token}")
82
84
  sock.puts(cmd)
83
85
  sock
84
86
  rescue Errno::EPIPE
@@ -31,6 +31,8 @@ module TestQueue
31
31
  attr_accessor :concurrency, :exit_when_done
32
32
  attr_reader :stats
33
33
 
34
+ TOKEN_REGEX = /^TOKEN=(\w+)/
35
+
34
36
  def initialize(test_framework, concurrency=nil, socket=nil, relay=nil)
35
37
  @test_framework = test_framework
36
38
  @stats = Stats.new(stats_file)
@@ -63,7 +65,7 @@ module TestQueue
63
65
  @queue.sort_by! { |suite_name, path| @whitelist.index(suite_name) }
64
66
  end
65
67
 
66
- @awaited_suites = Set.new(@whitelist - @queue.map(&:first))
68
+ @awaited_suites = Set.new(@whitelist)
67
69
  @original_queue = Set.new(@queue).freeze
68
70
 
69
71
  @workers = {}
@@ -72,7 +74,7 @@ module TestQueue
72
74
  @concurrency =
73
75
  concurrency ||
74
76
  (ENV['TEST_QUEUE_WORKERS'] && ENV['TEST_QUEUE_WORKERS'].to_i) ||
75
- if File.exists?('/proc/cpuinfo')
77
+ if File.exist?('/proc/cpuinfo')
76
78
  File.read('/proc/cpuinfo').split("\n").grep(/processor/).size
77
79
  elsif RUBY_PLATFORM =~ /darwin/
78
80
  `/usr/sbin/sysctl -n hw.activecpu`.to_i
@@ -107,7 +109,12 @@ module TestQueue
107
109
  @queue = []
108
110
  end
109
111
 
112
+ @discovered_suites = Set.new
113
+ @assignments = {}
114
+
110
115
  @exit_when_done = true
116
+
117
+ @aborting = false
111
118
  end
112
119
 
113
120
  # Run the tests.
@@ -134,9 +141,23 @@ module TestQueue
134
141
  puts "==> Summary (#{@completed.size} workers in %.4fs)" % (Time.now-@start_time)
135
142
  puts
136
143
 
144
+ estatus = 0
145
+ misrun_suites = []
146
+ unassigned_suites = []
137
147
  @failures = ''
138
148
  @completed.each do |worker|
149
+ estatus += worker.status.exitstatus
139
150
  @stats.record_suites(worker.suites)
151
+ worker.suites.each do |suite|
152
+ assignment = @assignments.delete([suite.name, suite.path])
153
+ host = worker.host || Socket.gethostname
154
+ if assignment.nil?
155
+ unassigned_suites << [suite.name, suite.path]
156
+ elsif assignment != [host, worker.pid]
157
+ misrun_suites << [suite.name, suite.path] + assignment + [host, worker.pid]
158
+ end
159
+ @discovered_suites.delete([suite.name, suite.path])
160
+ end
140
161
 
141
162
  summarize_worker(worker)
142
163
 
@@ -160,15 +181,44 @@ module TestQueue
160
181
  puts @failures
161
182
  end
162
183
 
184
+ if !relay?
185
+ unless @discovered_suites.empty?
186
+ estatus += 1
187
+ puts
188
+ puts "The following suites were discovered but were not run:"
189
+ puts
190
+
191
+ @discovered_suites.sort.each do |suite_name, path|
192
+ puts "#{suite_name} - #{path}"
193
+ end
194
+ end
195
+ unless unassigned_suites.empty?
196
+ estatus += 1
197
+ puts
198
+ puts "The following suites were not discovered but were run anyway:"
199
+ puts
200
+ unassigned_suites.sort.each do |suite_name, path|
201
+ puts "#{suite_name} - #{path}"
202
+ end
203
+ end
204
+ unless misrun_suites.empty?
205
+ estatus += 1
206
+ puts
207
+ puts "The following suites were run on the wrong workers:"
208
+ puts
209
+ misrun_suites.each do |suite_name, path, target_host, target_pid, actual_host, actual_pid|
210
+ puts "#{suite_name} - #{path}: #{actual_host} (#{actual_pid}) - assigned to #{target_host} (#{target_pid})"
211
+ end
212
+ end
213
+ end
214
+
163
215
  puts
164
216
 
165
217
  @stats.save
166
218
 
167
219
  summarize
168
220
 
169
- estatus = @completed.inject(0){ |s, worker| s + worker.status.exitstatus }
170
- estatus = 255 if estatus > 255
171
- estatus
221
+ [estatus, 255].min
172
222
  end
173
223
 
174
224
  def summarize
@@ -201,7 +251,7 @@ module TestQueue
201
251
  @socket = "#$1:#$2"
202
252
  @server = TCPServer.new(address, port)
203
253
  else
204
- FileUtils.rm(@socket) if File.exists?(@socket)
254
+ FileUtils.rm(@socket) if File.exist?(@socket)
205
255
  @server = UNIXServer.new(@socket)
206
256
  end
207
257
  end
@@ -217,7 +267,8 @@ module TestQueue
217
267
  sock = connect_to_relay
218
268
  message = @slave_message ? " #{@slave_message}" : ""
219
269
  message.gsub!(/(\r|\n)/, "") # Our "protocol" is newline-separated
220
- sock.puts("SLAVE #{@concurrency} #{Socket.gethostname} #{@run_token}#{message}")
270
+ sock.puts("TOKEN=#{@run_token}")
271
+ sock.puts("SLAVE #{@concurrency} #{Socket.gethostname} #{message}")
221
272
  response = sock.gets.strip
222
273
  unless response == "OK"
223
274
  STDERR.puts "*** Got non-OK response from master: #{response}"
@@ -245,7 +296,7 @@ module TestQueue
245
296
  pid = fork do
246
297
  @server.close if @server
247
298
 
248
- iterator = Iterator.new(@test_framework, relay?? @relay : @socket, method(:around_filter), early_failure_limit: @early_failure_limit)
299
+ iterator = Iterator.new(@test_framework, relay?? @relay : @socket, method(:around_filter), early_failure_limit: @early_failure_limit, run_token: @run_token)
249
300
  after_fork_internal(num, iterator)
250
301
  ret = run_worker(iterator) || 0
251
302
  cleanup_worker
@@ -276,6 +327,7 @@ module TestQueue
276
327
  Kernel.exit!(0) if terminate
277
328
 
278
329
  @server.connect_address.connect do |sock|
330
+ sock.puts("TOKEN=#{@run_token}")
279
331
  sock.puts("NEW SUITE #{Marshal.dump([suite_name, path])}")
280
332
  end
281
333
  end
@@ -305,8 +357,11 @@ module TestQueue
305
357
  return
306
358
  end
307
359
 
360
+ @discovered_suites << [suite_name, path]
361
+
308
362
  if @original_queue.include?([suite_name, path])
309
363
  # This suite was already added to the queue some other way.
364
+ @awaited_suites.delete(suite_name)
310
365
  return
311
366
  end
312
367
 
@@ -393,12 +448,12 @@ module TestQueue
393
448
  end
394
449
 
395
450
  def collect_worker_data(worker)
396
- if File.exists?(file = "/tmp/test_queue_worker_#{worker.pid}_output")
451
+ if File.exist?(file = "/tmp/test_queue_worker_#{worker.pid}_output")
397
452
  worker.output = IO.binread(file)
398
453
  FileUtils.rm(file)
399
454
  end
400
455
 
401
- if File.exists?(file = "/tmp/test_queue_worker_#{worker.pid}_suites")
456
+ if File.exist?(file = "/tmp/test_queue_worker_#{worker.pid}_suites")
402
457
  worker.suites.replace(Marshal.load(IO.binread(file)))
403
458
  FileUtils.rm(file)
404
459
  end
@@ -426,29 +481,38 @@ module TestQueue
426
481
  reap_workers(false) # check for worker deaths
427
482
  else
428
483
  sock = @server.accept
484
+ token = sock.gets.strip
429
485
  cmd = sock.gets.strip
486
+
487
+ token = token[TOKEN_REGEX, 1]
488
+ # If we have a slave from a different test run, respond with "WRONG RUN", and it will consider the test run done.
489
+ if token != @run_token
490
+ message = token.nil? ? "Worker sent no token to master" : "Worker from run #{token} connected to master"
491
+ STDERR.puts "*** #{message} for run #{@run_token}; ignoring."
492
+ sock.write("WRONG RUN\n")
493
+ next
494
+ end
495
+
430
496
  case cmd
431
- when /^POP/
497
+ when /^POP (\S+) (\d+)/
432
498
  # If we have a slave from a different test run, don't respond, and it will consider the test run done.
499
+ hostname = $1
500
+ pid = Integer($2)
433
501
  if awaiting_suites?
434
502
  sock.write(Marshal.dump("WAIT"))
435
503
  elsif obj = @queue.shift
436
504
  data = Marshal.dump(obj)
437
505
  sock.write(data)
506
+ @assignments[obj] = [hostname, pid]
438
507
  end
439
- when /^SLAVE (\d+) ([\w\.-]+) (\w+)(?: (.+))?/
508
+ when /^SLAVE (\d+) ([\w\.-]+)(?: (.+))?/
440
509
  num = $1.to_i
441
510
  slave = $2
442
- run_token = $3
443
- slave_message = $4
444
- if run_token == @run_token
445
- # If we have a slave from a different test run, don't respond, and it will consider the test run done.
446
- sock.write("OK\n")
447
- remote_workers += num
448
- else
449
- STDERR.puts "*** Worker from run #{run_token} connected to master for run #{@run_token}; ignoring."
450
- sock.write("WRONG RUN\n")
451
- end
511
+ slave_message = $3
512
+
513
+ sock.write("OK\n")
514
+ remote_workers += num
515
+
452
516
  message = "*** #{num} workers connected from #{slave} after #{Time.now-@start_time}s"
453
517
  message << " " + slave_message if slave_message
454
518
  STDERR.puts message
@@ -464,6 +528,8 @@ module TestQueue
464
528
  # worker reporting an abnormal number of test failures;
465
529
  # stop everything immediately and report the results.
466
530
  break
531
+ else
532
+ STDERR.puts("Ignoring unrecognized command: \"#{cmd}\"")
467
533
  end
468
534
  sock.close
469
535
  end
@@ -498,6 +564,7 @@ module TestQueue
498
564
  data = Marshal.dump(worker)
499
565
 
500
566
  sock = connect_to_relay
567
+ sock.puts("TOKEN=#{@run_token}")
501
568
  sock.puts("WORKER #{data.bytesize}")
502
569
  sock.write(data)
503
570
  ensure
@@ -74,7 +74,7 @@ module TestQueue
74
74
  def load
75
75
  data = begin
76
76
  File.open(@path, "rb") { |f| Marshal.load(f) }
77
- rescue Errno::ENOENT, EOFError, TypeError
77
+ rescue Errno::ENOENT, EOFError, TypeError, ArgumentError
78
78
  end
79
79
  return unless data && data.is_a?(Hash) && data[:version] == CURRENT_VERSION
80
80
  data[:suites].each do |suite_hash|
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.4.0'
3
+ s.version = '0.4.1'
4
4
  s.summary = 'parallel test runner'
5
5
  s.description = 'minitest/rspec parallel test runner for CI environments'
6
6
 
data/test/minitest5.bats CHANGED
@@ -74,23 +74,52 @@ assert_test_queue_force_ordering() {
74
74
  assert_output_contains "Failed to discover DoesNotExist specified in TEST_QUEUE_FORCE"
75
75
  }
76
76
 
77
- @test "multi-master succeeds when all tests pass" {
77
+ @test "multi-master central master succeeds when all tests pass" {
78
78
  export TEST_QUEUE_RELAY_TOKEN=$(date | cksum | cut -d' ' -f1)
79
- TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/samples/sample_minitest5.rb || true &
79
+ export SLEEP_AS_RELAY=1
80
+ TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb || true &
80
81
  sleep 0.1
81
- TEST_QUEUE_SOCKET=0.0.0.0:12345 run bundle exec minitest-queue ./test/samples/sample_minitest5.rb
82
+ TEST_QUEUE_SOCKET=0.0.0.0:12345 run bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb
82
83
  wait
83
84
 
84
85
  assert_status 0
85
86
  assert_output_contains "Starting test-queue master"
86
87
  }
87
88
 
88
- @test "multi-master fails when a test fails" {
89
+ @test "multi-master remote master succeeds when all tests pass" {
90
+ export TEST_QUEUE_RELAY_TOKEN=$(date | cksum | cut -d' ' -f1)
91
+ export SLEEP_AS_MASTER=1
92
+ TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb || true &
93
+ sleep 0.1
94
+ TEST_QUEUE_RELAY=0.0.0.0:12345 run bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb
95
+ wait
96
+
97
+ assert_status 0
98
+ assert_output_contains "Starting test-queue master"
99
+ }
100
+
101
+ @test "multi-master central master fails when a test fails" {
102
+ export FAIL=1
103
+ export SLEEP_AS_RELAY=1
104
+ export TEST_QUEUE_RELAY_TOKEN=$(date | cksum | cut -d' ' -f1)
105
+ TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb || true &
106
+ sleep 0.1
107
+ TEST_QUEUE_SOCKET=0.0.0.0:12345 run bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb
108
+ wait
109
+
110
+ assert_status 1
111
+ assert_output_contains "Starting test-queue master"
112
+ assert_output_contains "1) Failure:"
113
+ assert_output_contains "MiniTestFailure#test_fail"
114
+ }
115
+
116
+ @test "multi-master remote master fails when a test fails" {
89
117
  export FAIL=1
118
+ export SLEEP_AS_MASTER=1
90
119
  export TEST_QUEUE_RELAY_TOKEN=$(date | cksum | cut -d' ' -f1)
91
- TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/samples/sample_minitest5.rb || true &
120
+ TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb || true &
92
121
  sleep 0.1
93
- TEST_QUEUE_SOCKET=0.0.0.0:12345 run bundle exec minitest-queue ./test/samples/sample_minitest5.rb
122
+ TEST_QUEUE_RELAY=0.0.0.0:12345 run bundle exec ruby ./test/sleepy_runner.rb ./test/samples/sample_minitest5.rb
94
123
  wait
95
124
 
96
125
  assert_status 1
data/test/rspec.bats CHANGED
@@ -37,7 +37,7 @@ setup() {
37
37
  assert_output_contains "0 examples, 0 failures"
38
38
  }
39
39
 
40
- @test "Use to Shared Exsamples." {
40
+ @test "rspec-queue supports shared example groups" {
41
41
  run bundle exec rspec-queue ./test/samples/sample_use_shared_example1_spec.rb \
42
42
  ./test/samples/sample_use_shared_example2_spec.rb
43
43
  assert_status 0
@@ -1,7 +1,7 @@
1
1
  require 'rspec'
2
2
  require_relative 'sample_rspec_helper'
3
3
 
4
- describe 'Use SharedExcaplesFor test_1' do
4
+ describe 'Use SharedExamplesFor test_1' do
5
5
  subject { 5 }
6
6
  it_behaves_like 'Sample Example'
7
7
  end
@@ -1,7 +1,7 @@
1
1
  require 'rspec'
2
2
  require_relative 'sample_rspec_helper'
3
3
 
4
- describe 'Use SharedExcaplesFor test_2' do
4
+ describe 'Use SharedExamplesFor test_2' do
5
5
  subject { 5 }
6
6
  it_behaves_like 'Sample Example'
7
7
  end
@@ -0,0 +1,14 @@
1
+ require 'test_queue'
2
+ require 'test_queue/runner/minitest'
3
+
4
+ class SleepyTestRunner < TestQueue::Runner::MiniTest
5
+ def after_fork(num)
6
+ if ENV['SLEEP_AS_RELAY'] && relay?
7
+ sleep 5
8
+ elsif ENV['SLEEP_AS_MASTER'] && !relay?
9
+ sleep 5
10
+ end
11
+ end
12
+ end
13
+
14
+ SleepyTestRunner.new.execute
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-04 00:00:00.000000000 Z
11
+ date: 2017-01-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: minitest/rspec parallel test runner for CI environments
14
14
  email: ruby@tmm1.net
@@ -86,6 +86,7 @@ files:
86
86
  - test/samples/sample_testunit.rb
87
87
  - test/samples/sample_use_shared_example1_spec.rb
88
88
  - test/samples/sample_use_shared_example2_spec.rb
89
+ - test/sleepy_runner.rb
89
90
  - test/testlib.bash
90
91
  - test/testunit.bats
91
92
  homepage: http://github.com/tmm1/test-queue