ci-queue 0.16.0 → 0.17.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 +4 -4
- data/.gitignore +2 -1
- data/Rakefile +1 -0
- data/bin/console +1 -0
- data/ci-queue.gemspec +2 -0
- data/exe/minitest-queue +1 -0
- data/exe/rspec-queue +1 -0
- data/lib/ci/queue.rb +3 -0
- data/lib/ci/queue/bisect.rb +1 -0
- data/lib/ci/queue/build_record.rb +1 -0
- data/lib/ci/queue/circuit_breaker.rb +39 -0
- data/lib/ci/queue/common.rb +3 -2
- data/lib/ci/queue/configuration.rb +25 -11
- data/lib/ci/queue/file.rb +1 -0
- data/lib/ci/queue/grind.rb +23 -0
- data/lib/ci/queue/output_helpers.rb +1 -0
- data/lib/ci/queue/redis.rb +6 -0
- data/lib/ci/queue/redis/base.rb +1 -0
- data/lib/ci/queue/redis/build_record.rb +4 -3
- data/lib/ci/queue/redis/grind.rb +17 -0
- data/lib/ci/queue/redis/grind_record.rb +66 -0
- data/lib/ci/queue/redis/grind_supervisor.rb +13 -0
- data/lib/ci/queue/redis/retry.rb +1 -0
- data/lib/ci/queue/redis/supervisor.rb +2 -1
- data/lib/ci/queue/redis/test_time_record.rb +66 -0
- data/lib/ci/queue/redis/worker.rb +2 -1
- data/lib/ci/queue/static.rb +3 -1
- data/lib/ci/queue/version.rb +3 -1
- data/lib/minitest/queue.rb +10 -4
- data/lib/minitest/queue/build_status_recorder.rb +1 -0
- data/lib/minitest/queue/build_status_reporter.rb +1 -0
- data/lib/minitest/queue/error_report.rb +13 -0
- data/lib/minitest/queue/failure_formatter.rb +4 -0
- data/lib/minitest/queue/grind_recorder.rb +68 -0
- data/lib/minitest/queue/grind_reporter.rb +74 -0
- data/lib/minitest/queue/junit_reporter.rb +6 -5
- data/lib/minitest/queue/local_requeue_reporter.rb +1 -0
- data/lib/minitest/queue/order_reporter.rb +1 -0
- data/lib/minitest/queue/runner.rb +156 -32
- data/lib/minitest/queue/statsd.rb +1 -0
- data/lib/minitest/queue/test_data.rb +138 -0
- data/lib/minitest/queue/test_data_reporter.rb +32 -0
- data/lib/minitest/queue/test_time_recorder.rb +17 -0
- data/lib/minitest/queue/test_time_reporter.rb +70 -0
- data/lib/minitest/reporters/bisect_reporter.rb +1 -0
- data/lib/minitest/reporters/statsd_reporter.rb +1 -0
- data/lib/rspec/queue.rb +15 -14
- data/lib/rspec/queue/build_status_recorder.rb +1 -0
- data/lib/rspec/queue/order_recorder.rb +20 -0
- metadata +29 -4
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'ci/queue/static'
|
2
3
|
|
3
4
|
module CI
|
@@ -44,7 +45,7 @@ module CI
|
|
44
45
|
|
45
46
|
def poll
|
46
47
|
wait_for_master
|
47
|
-
until shutdown_required? || config.
|
48
|
+
until shutdown_required? || config.circuit_breakers.any?(&:open?) || exhausted?
|
48
49
|
if test = reserve
|
49
50
|
yield index.fetch(test), @last_warning
|
50
51
|
else
|
data/lib/ci/queue/static.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CI
|
2
4
|
module Queue
|
3
5
|
class Static
|
@@ -48,7 +50,7 @@ module CI
|
|
48
50
|
end
|
49
51
|
|
50
52
|
def poll
|
51
|
-
while !config.
|
53
|
+
while !config.circuit_breakers.any?(&:open?) && test = @queue.shift
|
52
54
|
yield index.fetch(test)
|
53
55
|
end
|
54
56
|
end
|
data/lib/ci/queue/version.rb
CHANGED
data/lib/minitest/queue.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'minitest'
|
2
|
-
gem 'minitest-reporters', '~> 1.1'
|
3
3
|
require 'minitest/reporters'
|
4
4
|
|
5
5
|
require 'minitest/queue/failure_formatter'
|
@@ -9,6 +9,11 @@ require 'minitest/queue/build_status_recorder'
|
|
9
9
|
require 'minitest/queue/build_status_reporter'
|
10
10
|
require 'minitest/queue/order_reporter'
|
11
11
|
require 'minitest/queue/junit_reporter'
|
12
|
+
require 'minitest/queue/test_data_reporter'
|
13
|
+
require 'minitest/queue/grind_recorder'
|
14
|
+
require 'minitest/queue/grind_reporter'
|
15
|
+
require 'minitest/queue/test_time_recorder'
|
16
|
+
require 'minitest/queue/test_time_reporter'
|
12
17
|
|
13
18
|
module Minitest
|
14
19
|
class Requeue < Skip
|
@@ -45,7 +50,7 @@ module Minitest
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def result_label
|
48
|
-
"
|
53
|
+
"Flaked"
|
49
54
|
end
|
50
55
|
|
51
56
|
def backtrace
|
@@ -97,6 +102,7 @@ module Minitest
|
|
97
102
|
|
98
103
|
module Queue
|
99
104
|
class SingleExample
|
105
|
+
|
100
106
|
def initialize(runnable, method_name)
|
101
107
|
@runnable = runnable
|
102
108
|
@method_name = method_name
|
@@ -144,8 +150,8 @@ module Minitest
|
|
144
150
|
if queue
|
145
151
|
run_from_queue(*args)
|
146
152
|
|
147
|
-
if queue.config.
|
148
|
-
STDERR.puts
|
153
|
+
if queue.config.circuit_breakers.any?(&:open?)
|
154
|
+
STDERR.puts queue.config.circuit_breakers.map(&:message).join(' ').strip
|
149
155
|
end
|
150
156
|
else
|
151
157
|
super
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Minitest
|
2
3
|
module Queue
|
3
4
|
class ErrorReport
|
@@ -57,6 +58,18 @@ module Minitest
|
|
57
58
|
@data[:test_and_module_name]
|
58
59
|
end
|
59
60
|
|
61
|
+
def test_file
|
62
|
+
@data[:test_file]
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_line
|
66
|
+
@data[:test_line]
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_h
|
70
|
+
@data
|
71
|
+
end
|
72
|
+
|
60
73
|
def to_s
|
61
74
|
output
|
62
75
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'delegate'
|
2
3
|
require 'ansi'
|
3
4
|
|
@@ -20,7 +21,10 @@ module Minitest
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_h
|
24
|
+
test_file, test_line = test.source_location
|
23
25
|
{
|
26
|
+
test_file: test_file,
|
27
|
+
test_line: test_line,
|
24
28
|
test_and_module_name: "#{test.klass}##{test.name}",
|
25
29
|
test_name: test.name,
|
26
30
|
output: to_s,
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Minitest
|
3
|
+
module Queue
|
4
|
+
class GrindRecorder < Minitest::Reporters::BaseReporter
|
5
|
+
|
6
|
+
attr_accessor :test_count
|
7
|
+
|
8
|
+
def self.counters
|
9
|
+
@counters ||= {
|
10
|
+
'failures' => 0,
|
11
|
+
'errors' => 0,
|
12
|
+
'skips' => 0,
|
13
|
+
'test_count' => 0
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :failure_formatter
|
19
|
+
end
|
20
|
+
self.failure_formatter = FailureFormatter
|
21
|
+
|
22
|
+
def initialize(build:, **options)
|
23
|
+
super(options)
|
24
|
+
@build = build
|
25
|
+
end
|
26
|
+
|
27
|
+
def record(test)
|
28
|
+
increment_counter(test)
|
29
|
+
record_test(test)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def record_test(test)
|
35
|
+
stats = self.class.counters
|
36
|
+
if (test.failure || test.error?) && !test.skipped?
|
37
|
+
build.record_error(dump(test), stats: stats)
|
38
|
+
else
|
39
|
+
build.record_success(stats: stats)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def increment_counter(test)
|
44
|
+
if test.skipped?
|
45
|
+
self.class.counters['skips'] += 1
|
46
|
+
elsif test.error?
|
47
|
+
self.class.counters['errors'] += 1
|
48
|
+
elsif test.failure
|
49
|
+
self.class.counters['failures'] += 1
|
50
|
+
end
|
51
|
+
self.class.counters['test_count'] += 1
|
52
|
+
|
53
|
+
key = "count##{test.klass}##{test.name}"
|
54
|
+
|
55
|
+
unless self.class.counters.key?(key)
|
56
|
+
self.class.counters[key] = 0
|
57
|
+
end
|
58
|
+
self.class.counters[key] += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def dump(test)
|
62
|
+
ErrorReport.new(self.class.failure_formatter.new(test).to_h).dump
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :build
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'minitest/reporters'
|
3
|
+
|
4
|
+
module Minitest
|
5
|
+
module Queue
|
6
|
+
class GrindReporter < Minitest::Reporters::BaseReporter
|
7
|
+
include ::CI::Queue::OutputHelpers
|
8
|
+
|
9
|
+
def initialize(build:, **options)
|
10
|
+
@build = build
|
11
|
+
@success = true
|
12
|
+
super(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def report
|
16
|
+
puts '+++ Results'
|
17
|
+
|
18
|
+
if flaky_tests.empty?
|
19
|
+
puts green('all tests passed every time, grinding did not uncover any flakiness')
|
20
|
+
return
|
21
|
+
end
|
22
|
+
@success = false
|
23
|
+
|
24
|
+
flaky_tests.each do |name, errors|
|
25
|
+
total_runs = fetch_counts(name)
|
26
|
+
flakiness_percentage = (errors.count / total_runs) * 100
|
27
|
+
|
28
|
+
error_messages = errors.map do |message|
|
29
|
+
message.to_s.lines.map { |l| "\t#{l}"}.join
|
30
|
+
end.to_set.to_a.join("\n\n")
|
31
|
+
|
32
|
+
puts <<~EOS
|
33
|
+
#{red(name)}
|
34
|
+
Runs: #{total_runs.to_i}
|
35
|
+
Failures: #{errors.count}
|
36
|
+
Flakiness Percentage: #{flakiness_percentage.to_i}%
|
37
|
+
Errors:
|
38
|
+
#{error_messages}
|
39
|
+
EOS
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def success?
|
44
|
+
@success
|
45
|
+
end
|
46
|
+
|
47
|
+
def flaky_tests
|
48
|
+
@flaky_tests ||= begin
|
49
|
+
flaky_tests = {}
|
50
|
+
build.error_reports.each do |error|
|
51
|
+
err = ErrorReport.load(error)
|
52
|
+
name = err.test_and_module_name
|
53
|
+
flaky_tests[name] ||= []
|
54
|
+
flaky_tests[name] << err
|
55
|
+
end
|
56
|
+
flaky_tests
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def record(*)
|
61
|
+
raise NotImplementedError
|
62
|
+
end
|
63
|
+
|
64
|
+
def fetch_counts(test)
|
65
|
+
key = "count##{test}"
|
66
|
+
build.fetch_stats([key])[key]
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
attr_reader :build
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'minitest/reporters'
|
2
3
|
require 'builder'
|
3
4
|
require 'fileutils'
|
@@ -57,16 +58,16 @@ module Minitest
|
|
57
58
|
|
58
59
|
def xml_message_for(test)
|
59
60
|
xml = XmlMarkup.new(indent: 2, margin: 2)
|
60
|
-
|
61
|
+
failure = test.failure
|
61
62
|
|
62
63
|
if test.skipped? && !test.flaked?
|
63
|
-
xml.skipped(type:
|
64
|
+
xml.skipped(type: failure.error.class.name)
|
64
65
|
elsif test.error?
|
65
|
-
xml.error(type:
|
66
|
+
xml.error(type: failure.error.class.name, message: xml.trunc!(failure.message)) do
|
66
67
|
xml.text!(message_for(test))
|
67
68
|
end
|
68
|
-
elsif
|
69
|
-
xml.failure(type:
|
69
|
+
elsif failure
|
70
|
+
xml.failure(type: failure.error.class.name, message: xml.trunc!(failure.message)) do
|
70
71
|
xml.text!(message_for(test))
|
71
72
|
end
|
72
73
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'optparse'
|
3
|
+
require 'json'
|
2
4
|
require 'minitest/queue'
|
3
5
|
require 'ci/queue'
|
4
6
|
require 'digest/md5'
|
@@ -59,6 +61,7 @@ module Minitest
|
|
59
61
|
LocalRequeueReporter.new,
|
60
62
|
BuildStatusRecorder.new(build: queue.build),
|
61
63
|
JUnitReporter.new,
|
64
|
+
TestDataReporter.new(namespace: queue_config&.namespace),
|
62
65
|
OrderReporter.new(path: 'log/test_order.log'),
|
63
66
|
]
|
64
67
|
if queue_config.statsd_endpoint
|
@@ -70,7 +73,7 @@ module Minitest
|
|
70
73
|
trap('INT') { Minitest.queue.shutdown! }
|
71
74
|
|
72
75
|
if queue.rescue_connection_errors { queue.exhausted? }
|
73
|
-
puts green(
|
76
|
+
puts green('All tests were ran already')
|
74
77
|
else
|
75
78
|
load_tests
|
76
79
|
populate_queue
|
@@ -78,6 +81,41 @@ module Minitest
|
|
78
81
|
# Let minitest's at_exit hook trigger
|
79
82
|
end
|
80
83
|
|
84
|
+
def grind_command
|
85
|
+
invalid_usage!('No list to grind provided') if grind_list.nil?
|
86
|
+
invalid_usage!('No grind count provided') if grind_count.nil?
|
87
|
+
|
88
|
+
set_load_path
|
89
|
+
|
90
|
+
queue_config.build_id = queue_config.build_id + '-grind'
|
91
|
+
queue_config.grind_count = grind_count
|
92
|
+
|
93
|
+
reporter_queue = CI::Queue::Redis::Grind.new(queue_url, queue_config)
|
94
|
+
test_time_record = CI::Queue::Redis::TestTimeRecord.new(queue_url, queue_config)
|
95
|
+
|
96
|
+
Minitest.queue = queue
|
97
|
+
reporters = [
|
98
|
+
GrindRecorder.new(build: reporter_queue.build),
|
99
|
+
TestDataReporter.new(namespace: queue_config&.namespace),
|
100
|
+
TestTimeRecorder.new(build: test_time_record)
|
101
|
+
]
|
102
|
+
if queue_config.statsd_endpoint
|
103
|
+
reporters << Minitest::Reporters::StatsdReporter.new(statsd_endpoint: queue_config.statsd_endpoint)
|
104
|
+
end
|
105
|
+
Minitest.queue_reporters = reporters
|
106
|
+
|
107
|
+
trap('TERM') { Minitest.queue.shutdown! }
|
108
|
+
trap('INT') { Minitest.queue.shutdown! }
|
109
|
+
|
110
|
+
load_tests
|
111
|
+
|
112
|
+
@queue = CI::Queue::Grind.new(grind_list, queue_config)
|
113
|
+
Minitest.queue = queue
|
114
|
+
populate_queue
|
115
|
+
|
116
|
+
# Let minitest's at_exit hook trigger
|
117
|
+
end
|
118
|
+
|
81
119
|
def bisect_command
|
82
120
|
invalid_usage! "Missing the FAILING_TEST argument." unless queue_config.failing_test
|
83
121
|
|
@@ -144,14 +182,44 @@ module Minitest
|
|
144
182
|
end
|
145
183
|
|
146
184
|
reporter = BuildStatusReporter.new(build: supervisor.build)
|
185
|
+
|
186
|
+
if queue_config.failure_file
|
187
|
+
failures = reporter.error_reports.map(&:to_h).to_json
|
188
|
+
File.write(queue_config.failure_file, failures)
|
189
|
+
end
|
190
|
+
|
147
191
|
reporter.report
|
148
192
|
exit! reporter.success? ? 0 : 1
|
149
193
|
end
|
150
194
|
|
195
|
+
def report_grind_command
|
196
|
+
queue_config.build_id = queue_config.build_id + '-grind'
|
197
|
+
@queue = CI::Queue::Redis::Grind.new(queue_url, queue_config)
|
198
|
+
|
199
|
+
supervisor = begin
|
200
|
+
queue.supervisor
|
201
|
+
rescue NotImplementedError => error
|
202
|
+
abort! error.message
|
203
|
+
end
|
204
|
+
|
205
|
+
grind_reporter = GrindReporter.new(build: supervisor.build)
|
206
|
+
grind_reporter.report
|
207
|
+
|
208
|
+
test_time_record = CI::Queue::Redis::TestTimeRecord.new(queue_url, queue_config)
|
209
|
+
test_time_reporter = Minitest::Queue::TestTimeReporter.new(
|
210
|
+
build: test_time_record,
|
211
|
+
limit: queue_config.max_test_duration,
|
212
|
+
percentile: queue_config.max_test_duration_percentile,
|
213
|
+
)
|
214
|
+
test_time_reporter.report
|
215
|
+
|
216
|
+
exit! grind_reporter.success? && test_time_reporter.success? ? 0 : 1
|
217
|
+
end
|
218
|
+
|
151
219
|
private
|
152
220
|
|
153
221
|
attr_reader :queue_config, :options, :command, :argv
|
154
|
-
attr_accessor :queue, :queue_url, :load_paths
|
222
|
+
attr_accessor :queue, :queue_url, :grind_list, :grind_count, :load_paths
|
155
223
|
|
156
224
|
def display_warnings(build)
|
157
225
|
build.pop_warnings.each do |type, attributes|
|
@@ -160,7 +228,7 @@ module Minitest
|
|
160
228
|
puts reopen_previous_step
|
161
229
|
puts yellow(
|
162
230
|
"[WARNING] #{attributes[:test]} was picked up by another worker because it didn't complete in the allocated #{attributes[:timeout]} seconds.\n" \
|
163
|
-
"You may want to either optimize this test
|
231
|
+
"You may want to either optimize this test or bump ci-queue timeout.\n" \
|
164
232
|
"It's also possible that the worker that was processing it was terminated without being able to report back.\n"
|
165
233
|
)
|
166
234
|
end
|
@@ -217,31 +285,47 @@ module Minitest
|
|
217
285
|
opts.separator "GLOBAL OPTIONS"
|
218
286
|
|
219
287
|
|
220
|
-
help =
|
288
|
+
help = <<~EOS
|
221
289
|
URL of the queue, e.g. redis://example.com.
|
222
290
|
Defaults to $CI_QUEUE_URL if set.
|
223
291
|
EOS
|
224
292
|
opts.separator ""
|
225
|
-
opts.on('--queue URL',
|
293
|
+
opts.on('--queue URL', help) do |url|
|
226
294
|
self.queue_url = url
|
227
295
|
end
|
228
296
|
|
229
|
-
help =
|
297
|
+
help = <<~EOS
|
298
|
+
Path to the file that includes the list of tests to grind.
|
299
|
+
EOS
|
300
|
+
opts.separator ""
|
301
|
+
opts.on('--grind-list PATH', help) do |url|
|
302
|
+
self.grind_list = url
|
303
|
+
end
|
304
|
+
|
305
|
+
help = <<~EOS
|
306
|
+
Count defines how often each test in the grind list is going to be run.
|
307
|
+
EOS
|
308
|
+
opts.separator ""
|
309
|
+
opts.on('--grind-count COUNT', help) do |count|
|
310
|
+
self.grind_count = count.to_i
|
311
|
+
end
|
312
|
+
|
313
|
+
help = <<~EOS
|
230
314
|
Unique identifier for the workload. All workers working on the same suite of tests must have the same build identifier.
|
231
315
|
If the build is tried again, or another revision is built, this value must be different.
|
232
316
|
It's automatically inferred on Buildkite, CircleCI, Heroku CI, and Travis.
|
233
317
|
EOS
|
234
318
|
opts.separator ""
|
235
|
-
opts.on('--build BUILD_ID',
|
319
|
+
opts.on('--build BUILD_ID', help) do |build_id|
|
236
320
|
queue_config.build_id = build_id
|
237
321
|
end
|
238
322
|
|
239
|
-
help =
|
323
|
+
help = <<~EOS
|
240
324
|
Optional. Sets a prefix for the build id in case a single CI build runs multiple independent test suites.
|
241
325
|
Example: --namespace integration
|
242
326
|
EOS
|
243
327
|
opts.separator ""
|
244
|
-
opts.on('--namespace NAMESPACE',
|
328
|
+
opts.on('--namespace NAMESPACE', help) do |namespace|
|
245
329
|
queue_config.namespace = namespace
|
246
330
|
end
|
247
331
|
|
@@ -250,68 +334,112 @@ module Minitest
|
|
250
334
|
opts.separator ""
|
251
335
|
opts.separator " run [TEST_FILES...]: Participate in leader election, and then work off the test queue."
|
252
336
|
|
253
|
-
help =
|
337
|
+
help = <<~EOS
|
254
338
|
Specify a timeout after which if a test haven't completed, it will be picked up by another worker.
|
255
339
|
It is very important to set this vlaue higher than the slowest test in the suite, otherwise performance will be impacted.
|
256
340
|
Defaults to 30 seconds.
|
257
341
|
EOS
|
258
342
|
opts.separator ""
|
259
|
-
opts.on('--timeout TIMEOUT',
|
260
|
-
queue_config.timeout =
|
343
|
+
opts.on('--timeout TIMEOUT', Float, help) do |timeout|
|
344
|
+
queue_config.timeout = timeout
|
261
345
|
end
|
262
346
|
|
263
|
-
help =
|
347
|
+
help = <<~EOS
|
264
348
|
Specify $LOAD_PATH directory, similar to Ruby's -I
|
265
349
|
EOS
|
266
350
|
opts.separator ""
|
267
|
-
opts.on('-IPATHS',
|
351
|
+
opts.on('-IPATHS', help) do |paths|
|
268
352
|
self.load_paths = paths
|
269
353
|
end
|
270
354
|
|
271
|
-
help =
|
355
|
+
help = <<~EOS
|
272
356
|
Sepcify a seed used to shuffle the test suite.
|
273
357
|
On Buildkite, CircleCI, Heroku CI, and Travis, the commit revision will be used by default.
|
274
358
|
EOS
|
275
359
|
opts.separator ""
|
276
|
-
opts.on('--seed SEED',
|
360
|
+
opts.on('--seed SEED', help) do |seed|
|
277
361
|
queue_config.seed = seed
|
278
362
|
end
|
279
363
|
|
280
|
-
help =
|
364
|
+
help = <<~EOS
|
281
365
|
A unique identifier for this worker, It must be consistent to allow retries.
|
282
366
|
If not specified, retries won't be available.
|
283
367
|
It's automatically inferred on Buildkite, Heroku CI, and CircleCI.
|
284
368
|
EOS
|
285
369
|
opts.separator ""
|
286
|
-
opts.on('--worker WORKER_ID',
|
370
|
+
opts.on('--worker WORKER_ID', help) do |worker_id|
|
287
371
|
queue_config.worker_id = worker_id
|
288
372
|
end
|
289
373
|
|
290
|
-
help =
|
374
|
+
help = <<~EOS
|
291
375
|
Defines how many time a single test can be requeued.
|
292
376
|
Defaults to 0.
|
293
377
|
EOS
|
294
378
|
opts.separator ""
|
295
|
-
opts.on('--max-requeues MAX',
|
296
|
-
queue_config.max_requeues =
|
379
|
+
opts.on('--max-requeues MAX', Integer, help) do |max|
|
380
|
+
queue_config.max_requeues = max
|
381
|
+
end
|
382
|
+
|
383
|
+
help = <<~EOS
|
384
|
+
Defines how long ci-queue should maximally run in seconds
|
385
|
+
Defaults to none.
|
386
|
+
EOS
|
387
|
+
opts.separator ""
|
388
|
+
opts.on('--max-duration SECONDS', Integer, help) do |max|
|
389
|
+
queue_config.max_duration = max
|
297
390
|
end
|
298
391
|
|
299
|
-
help =
|
392
|
+
help = <<~EOS
|
300
393
|
Defines how many requeues can happen overall, based on the test suite size. e.g 0.05 for 5%.
|
301
394
|
Defaults to 0.
|
302
395
|
EOS
|
303
396
|
opts.separator ""
|
304
|
-
opts.on('--requeue-tolerance RATIO',
|
305
|
-
queue_config.requeue_tolerance =
|
397
|
+
opts.on('--requeue-tolerance RATIO', Float, help) do |ratio|
|
398
|
+
queue_config.requeue_tolerance = ratio
|
399
|
+
end
|
400
|
+
|
401
|
+
help = <<~EOS
|
402
|
+
Defines a file where the test failures are written to in the json format.
|
403
|
+
Defaults to disabled.
|
404
|
+
EOS
|
405
|
+
opts.separator ""
|
406
|
+
opts.on('--failure-file FILE', help) do |file|
|
407
|
+
queue_config.failure_file = file
|
306
408
|
end
|
307
409
|
|
308
|
-
help =
|
410
|
+
help = <<~EOS
|
309
411
|
Defines after how many consecutive failures the worker will be considered unhealthy and terminate itself.
|
310
412
|
Defaults to disabled.
|
311
413
|
EOS
|
312
414
|
opts.separator ""
|
313
|
-
opts.on('--max-consecutive-failures MAX',
|
314
|
-
queue_config.max_consecutive_failures =
|
415
|
+
opts.on('--max-consecutive-failures MAX', Integer, help) do |max|
|
416
|
+
queue_config.max_consecutive_failures = max
|
417
|
+
end
|
418
|
+
|
419
|
+
help = <<~EOS
|
420
|
+
Set the time limit of the execution time from grinds on a given test.
|
421
|
+
For example, when max-test-duration is set to 10 and
|
422
|
+
max-test-duration-percentile is set to 0.5, the test's median execution time during a grind must be
|
423
|
+
lower than 10 milliseconds.
|
424
|
+
The unit is milliseconds and decimal is allowed.
|
425
|
+
Defaults to disabled.
|
426
|
+
EOS
|
427
|
+
opts.on('--max-test-duration LIMIT_IN_MILLISECONDS', Float, help) do |limit|
|
428
|
+
queue_config.max_test_duration = limit
|
429
|
+
end
|
430
|
+
|
431
|
+
help = <<~EOS
|
432
|
+
The percentile for max-test-duration. For example, when max-test-duration is set to 10 and
|
433
|
+
max-test-duration-percentile is set to 0.5, the test's median execution time during a grind must be
|
434
|
+
lower than 10 milliseconds.
|
435
|
+
The percentile must be within the range 0 < percentile <= 1.
|
436
|
+
Defaults to 0.5 (50th percentile).
|
437
|
+
EOS
|
438
|
+
opts.on('--max-test-duration-percentile LIMIT_IN_MILLISECONDS', Float, help) do |percentile|
|
439
|
+
queue_config.max_test_duration_percentile = percentile
|
440
|
+
if queue_config.max_test_duration_percentile <= 0 || queue_config.max_test_duration_percentile > 1
|
441
|
+
raise OptionParser::ParseError.new("must be within range (0, 1]")
|
442
|
+
end
|
315
443
|
end
|
316
444
|
|
317
445
|
opts.separator ""
|
@@ -322,7 +450,7 @@ module Minitest
|
|
322
450
|
|
323
451
|
opts.separator ""
|
324
452
|
opts.separator " bisect: bisect a test suite to find global state leaks."
|
325
|
-
help =
|
453
|
+
help = <<~EOS
|
326
454
|
The identifier of the failing test.
|
327
455
|
EOS
|
328
456
|
opts.separator ""
|
@@ -332,10 +460,6 @@ module Minitest
|
|
332
460
|
end
|
333
461
|
end
|
334
462
|
|
335
|
-
def split_heredoc(string)
|
336
|
-
string.lines.map(&:strip)
|
337
|
-
end
|
338
|
-
|
339
463
|
def ordering_seed
|
340
464
|
if queue_config.seed
|
341
465
|
Random.new(Digest::MD5.hexdigest(queue_config.seed).to_i(16))
|