test-queue 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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