ci-queue 0.7.0 → 0.8.0

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: 97f69595ec09509df32703213fd2d882db14f017
4
- data.tar.gz: cae08e13e586bd5c7cc8927057225084b0ee0d74
3
+ metadata.gz: d71f2dd528eeda6758bc37bb9142cac9814150ea
4
+ data.tar.gz: 3042fc3632751cc3ef1834dd749978dff2c628a6
5
5
  SHA512:
6
- metadata.gz: 742b3d39caf903b480950a86eafb687776e8b4d1a9465d28e1ce29ff37d3242f3127a40d7115e51eddec03b6e22322cef0108ef2a64f6a5744b642b3e1b14ecf
7
- data.tar.gz: 8f753085f6408c98e698c0a75c06b77a427e066cbad9aa7c1db102613cdcf4b6d25d27e93145e86928bd5ae956b54adeb44245f36ee2ceee3eb3e9646c45363b
6
+ metadata.gz: bc012267f7470bc5527ae5081ebfe2e268acf6d777db6c8c50d218e9aa21fa7a3dba46e2f1af467a9f7fcbf5ca98512322128dea0115ce4a047cd6a09aa09253
7
+ data.tar.gz: 80445ac4bb1f43a2a930269708c7f97178921d5da60b198af42ffdeecf07e0e0c3242cb4c065c6b220ee4ef19b6a7935716330875df1aa1b62b665f891641ae9
data/README.md CHANGED
@@ -39,10 +39,16 @@ Additionally you can configure the requeue settings (see main README) with `--ma
39
39
 
40
40
  If you'd like to centralize the error reporting you can do so with:
41
41
 
42
- ```
42
+ ```bash
43
43
  minitest-queue --queue redis://example.com --timeout 600 report
44
44
  ```
45
45
 
46
+ The runner also comes with a tool to investigate leaky tests:
47
+
48
+ ```bash
49
+ minitest-queue --queue path/to/test_order.log --failing-test 'SomeTest#test_something' bisect -Itest test/**/*_test.rb
50
+ ```
51
+
46
52
  ### RSpec
47
53
 
48
54
  The RSpec integration is not implemented yet.
data/lib/ci/queue.rb CHANGED
@@ -7,6 +7,7 @@ require 'ci/queue/index'
7
7
  require 'ci/queue/configuration'
8
8
  require 'ci/queue/static'
9
9
  require 'ci/queue/file'
10
+ require 'ci/queue/bisect'
10
11
 
11
12
  module CI
12
13
  module Queue
@@ -0,0 +1,59 @@
1
+ module CI
2
+ module Queue
3
+ class Bisect
4
+ def initialize(path, config)
5
+ @tests = ::File.readlines(path).map(&:strip).reject(&:empty?).take_while { |t| t != config.failing_test }
6
+ @config = config
7
+ end
8
+
9
+ def size
10
+ @tests.size
11
+ end
12
+
13
+ def populate(all_tests, &test_indexer)
14
+ @all_tests = all_tests
15
+ @test_indexer = test_indexer
16
+ end
17
+
18
+ def to_a
19
+ @tests + [config.failing_test]
20
+ end
21
+
22
+ def suspects_left
23
+ @tests.size
24
+ end
25
+
26
+ def failing_test
27
+ Static.new([config.failing_test], config).populate(@all_tests, &@test_indexer)
28
+ end
29
+
30
+ def candidates
31
+ Static.new(first_half + [config.failing_test], config).populate(@all_tests, &@test_indexer)
32
+ end
33
+
34
+ def failed!
35
+ @tests = first_half
36
+ end
37
+
38
+ def succeeded!
39
+ @tests = second_half
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :config
45
+
46
+ def slices
47
+ @tests.each_slice((@tests.size / 2.0).ceil).to_a
48
+ end
49
+
50
+ def first_half
51
+ slices.first
52
+ end
53
+
54
+ def second_half
55
+ slices.last
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,7 +1,7 @@
1
1
  module CI
2
2
  module Queue
3
3
  class Configuration
4
- attr_accessor :timeout, :build_id, :worker_id, :max_requeues, :requeue_tolerance, :namespace, :seed
4
+ attr_accessor :timeout, :build_id, :worker_id, :max_requeues, :requeue_tolerance, :namespace, :seed, :failing_test
5
5
 
6
6
  class << self
7
7
  def from_env(env)
data/lib/ci/queue/file.rb CHANGED
@@ -10,7 +10,8 @@ module CI
10
10
  end
11
11
 
12
12
  def initialize(path, *args)
13
- super(::File.readlines(path).map(&:strip).reject(&:empty?), *args)
13
+ io = path == '-' ? STDIN : ::File.open(path)
14
+ super(io.each_line.map(&:strip).reject(&:empty?), *args)
14
15
  end
15
16
  end
16
17
  end
@@ -1,6 +1,6 @@
1
1
  module CI
2
2
  module Queue
3
- VERSION = '0.7.0'
3
+ VERSION = '0.8.0'
4
4
  DEV_SCRIPTS_ROOT = ::File.expand_path('../../../../../redis', __FILE__)
5
5
  RELEASE_SCRIPTS_ROOT = ::File.expand_path('../redis', __FILE__)
6
6
  end
@@ -58,7 +58,8 @@ module Minitest
58
58
 
59
59
  def queue_reporters=(reporters)
60
60
  @queue_reporters ||= []
61
- Reporters.reporters = ((Reporters.reporters || []) - @queue_reporters) + reporters
61
+ Reporters.use!(((Reporters.reporters || []) - @queue_reporters) + reporters)
62
+ Minitest.backtrace_filter.add_filter(%r{exe/minitest-queue|lib/ci/queue/})
62
63
  @queue_reporters = reporters
63
64
  end
64
65
 
@@ -49,6 +49,52 @@ module Minitest
49
49
  # Let minitest's at_exit hook trigger
50
50
  end
51
51
 
52
+ def bisect_command
53
+ invalid_usage! "Missing the FAILING_TEST argument." unless queue_config.failing_test
54
+
55
+ @queue = CI::Queue::Bisect.new(queue_url, queue_config)
56
+ Minitest.queue = queue
57
+ set_load_path
58
+ load_tests
59
+ populate_queue
60
+
61
+ step("Testing the failing test in isolation")
62
+ unless run_tests_in_fork(queue.failing_test)
63
+ puts reopen_previous_step
64
+ puts red("The test fail when run alone, no need to bisect.")
65
+ exit! 0
66
+ end
67
+
68
+ run_index = 0
69
+ while queue.suspects_left > 1
70
+ run_index += 1
71
+ step("Run ##{run_index}, #{queue.suspects_left} suspects left")
72
+ if run_tests_in_fork(queue.candidates)
73
+ queue.succeeded!
74
+ else
75
+ queue.failed!
76
+ end
77
+ puts
78
+ end
79
+
80
+ failing_order = queue.candidates
81
+ step("Final validation")
82
+ status = if run_tests_in_fork(failing_order)
83
+ step(yellow("The bisection was inconclusive, there might not be any leaky test here."))
84
+ exit! 1
85
+ else
86
+ step(green('The following command should reproduce the leak on your machine:'), collapsed: false)
87
+ command = %w(bundle exec minitest-queue --queue - run)
88
+ command << "-I#{load_paths}" if load_paths
89
+ command += argv
90
+
91
+ puts
92
+ puts "cat <<EOF |\n#{failing_order.to_a.join("\n")}\nEOF\n#{command.join(' ')}"
93
+ puts
94
+ exit! 0
95
+ end
96
+ end
97
+
52
98
  def report_command
53
99
  supervisor = begin
54
100
  queue.supervisor
@@ -73,7 +119,6 @@ module Minitest
73
119
  reporter.report
74
120
  end
75
121
 
76
- STDOUT.flush
77
122
  exit! success ? 0 : 1
78
123
  end
79
124
 
@@ -82,6 +127,17 @@ module Minitest
82
127
  attr_reader :queue_config, :options, :command, :argv
83
128
  attr_accessor :queue, :queue_url, :load_paths
84
129
 
130
+ def run_tests_in_fork(queue)
131
+ child_pid = fork do
132
+ Minitest.queue = queue
133
+ Minitest::Reporters.use!([Minitest::Reporters::SpecReporter.new])
134
+ exit # let minitest excute its at_exit
135
+ end
136
+
137
+ _, status = Process.wait2(child_pid)
138
+ return status.success?
139
+ end
140
+
85
141
  def populate_queue
86
142
  Minitest.queue.populate(shuffle(Minitest.loaded_tests), &:to_s) # TODO: stop serializing
87
143
  end
@@ -210,6 +266,16 @@ module Minitest
210
266
 
211
267
  opts.separator ""
212
268
  opts.separator " report: Wait for all workers to complete and summarize the test failures."
269
+
270
+ opts.separator ""
271
+ opts.separator " bisect: bisect a test suite to find global state leaks."
272
+ help = split_heredoc(<<-EOS)
273
+ The identifier of the failing test.
274
+ EOS
275
+ opts.separator ""
276
+ opts.on('--failing-test TEST_IDENTIFIER') do |identifier|
277
+ queue_config.failing_test = identifier
278
+ end
213
279
  end
214
280
  end
215
281
 
@@ -218,6 +284,7 @@ module Minitest
218
284
  end
219
285
 
220
286
  def shuffle(tests)
287
+ return tests unless queue_config.seed
221
288
  random = Random.new(Digest::MD5.hexdigest(queue_config.seed).to_i(16))
222
289
  tests.shuffle(random: random)
223
290
  end
@@ -233,6 +300,12 @@ module Minitest
233
300
  exit! 1 # exit! is required to avoid minitest at_exit callback
234
301
  end
235
302
 
303
+ def exit!(*)
304
+ STDOUT.flush
305
+ STDERR.flush
306
+ super
307
+ end
308
+
236
309
  def abort!(message)
237
310
  reopen_previous_step
238
311
  puts red(message)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ci-queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-22 00:00:00.000000000 Z
11
+ date: 2017-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,6 +114,7 @@ files:
114
114
  - dev.yml
115
115
  - exe/minitest-queue
116
116
  - lib/ci/queue.rb
117
+ - lib/ci/queue/bisect.rb
117
118
  - lib/ci/queue/configuration.rb
118
119
  - lib/ci/queue/file.rb
119
120
  - lib/ci/queue/index.rb
@@ -155,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
156
  version: '0'
156
157
  requirements: []
157
158
  rubyforge_project:
158
- rubygems_version: 2.6.10
159
+ rubygems_version: 2.6.13
159
160
  signing_key:
160
161
  specification_version: 4
161
162
  summary: Distribute tests over many workers using a queue