ci-queue 0.7.0 → 0.8.0

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: 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