megatest 0.6.0 → 0.7.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/CHANGELOG.md +9 -2
- data/README.md +2 -2
- data/lib/megatest/cli.rb +48 -49
- data/lib/megatest/config.rb +44 -3
- data/lib/megatest/dsl.rb +3 -3
- data/lib/megatest/executor.rb +20 -6
- data/lib/megatest/multi_process.rb +4 -5
- data/lib/megatest/patience_diff.rb +1 -1
- data/lib/megatest/pretty_print.rb +2 -2
- data/lib/megatest/queue.rb +4 -4
- data/lib/megatest/redis_queue.rb +6 -5
- data/lib/megatest/reporters.rb +8 -3
- data/lib/megatest/runner.rb +2 -4
- data/lib/megatest/runtime.rb +1 -1
- data/lib/megatest/test_task.rb +1 -1
- data/lib/megatest/version.rb +1 -1
- data/lib/megatest.rb +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a5522d82ae4b5780fa9fdfa8029df9cf1e1b4ec3afaeacbea2a3f4491e93dcff
|
|
4
|
+
data.tar.gz: a3f5e2553b5c4a8654d2d0d300b2ec604e982ef19a1851a67d84c851a149e2e9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b603f4ea08c46dd91937b4214a3f82da29c4a1788dcfe06d53ead22aaaa7bebc3f8247bd6ca163e3f744f7215ef72eb1ed1fb389c9dd99c035c2f0af33f6a2ad
|
|
7
|
+
data.tar.gz: 0cccdb03115bb61e7ed7ab0d1ef2d0bb6dab8acdb61e40e628958ca1e5b34b16ce51549402834d780adb08f1dc2c9ad0106af14a5a6ad94e8379a8956ffc3b24
|
data/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
-
## [0.
|
|
3
|
+
## [0.7.0] - 2026-03-21
|
|
4
|
+
|
|
5
|
+
- Automatic parallelization using cgroups or nprocs.
|
|
6
|
+
- Don't output escape codes when output is not a TTY.
|
|
7
|
+
- Don't retry skipped tests.
|
|
8
|
+
- Improve help message.
|
|
9
|
+
|
|
10
|
+
## [0.6.0] - 2026-01-17
|
|
4
11
|
|
|
5
12
|
- Allow defining setup and teardown with method names.
|
|
6
13
|
- Allow multiple `setup`, `around` and `teardown` blocks in the same test suite.
|
|
7
14
|
|
|
8
|
-
## [0.5.0] -
|
|
15
|
+
## [0.5.0] - 2026-01-17
|
|
9
16
|
|
|
10
17
|
- Adds `megatest/autorun`
|
|
11
18
|
- Adds `assert_nothing_raised`.
|
data/README.md
CHANGED
|
@@ -142,7 +142,7 @@ and will generally expose environment variables to help split the workload.
|
|
|
142
142
|
|
|
143
143
|
```yaml
|
|
144
144
|
- label: "Run Unit Tests"
|
|
145
|
-
run: megatest --workers-count $
|
|
145
|
+
run: megatest --workers-count $CI_NODE_TOTAL --worker-id $CI_NODE_INDEX
|
|
146
146
|
parallel: 8
|
|
147
147
|
```
|
|
148
148
|
|
|
@@ -154,7 +154,7 @@ very large test suites containing lots of slow test cases being sharded as one u
|
|
|
154
154
|
If you are using CircleCI, Buildkite or HerokuCI, the workers count and worker id
|
|
155
155
|
will be automatically inferred from the environment.
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
#### Redis Distribution
|
|
158
158
|
|
|
159
159
|
A more efficient way to parallelize tests on CI is to use a Redis server to act as a queue.
|
|
160
160
|
|
data/lib/megatest/cli.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Megatest
|
|
|
27
27
|
|
|
28
28
|
undef_method :puts, :print # Should only use @out.puts or @err.puts
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
COMMANDS = {
|
|
31
31
|
"bisect" => :bisect,
|
|
32
32
|
"report" => :report,
|
|
33
33
|
"run" => :run,
|
|
@@ -40,17 +40,17 @@ module Megatest
|
|
|
40
40
|
@processes = nil
|
|
41
41
|
@config = Config.new(env)
|
|
42
42
|
@program_name = @config.program_name = program_name
|
|
43
|
-
@
|
|
43
|
+
@command = nil
|
|
44
44
|
@verbose = false
|
|
45
45
|
@junit = false
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def run
|
|
49
49
|
configure
|
|
50
|
-
case @
|
|
50
|
+
case @command
|
|
51
51
|
when :report
|
|
52
52
|
report
|
|
53
|
-
when
|
|
53
|
+
when :run
|
|
54
54
|
run_tests
|
|
55
55
|
when :bisect
|
|
56
56
|
bisect_tests
|
|
@@ -71,24 +71,24 @@ module Megatest
|
|
|
71
71
|
def configure
|
|
72
72
|
Megatest.running = true
|
|
73
73
|
|
|
74
|
-
if @
|
|
74
|
+
if @command = COMMANDS[@argv.first]
|
|
75
75
|
@argv.shift
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
Megatest.config = @config
|
|
79
|
-
@parser = build_parser
|
|
79
|
+
@parser = build_parser
|
|
80
80
|
@parser.parse!(@argv)
|
|
81
81
|
@argv.shift if @argv.first == "--"
|
|
82
|
+
@queue = @config.build_queue
|
|
83
|
+
@config.parallelize_maybe if @command == :run && !@queue.distributed? && !@queue.sharded?
|
|
82
84
|
@config
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
def run_tests
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if queue.distributed?
|
|
88
|
+
if @queue.distributed?
|
|
89
89
|
raise InvalidArgument, "Distributed queues require a build-id" unless @config.build_id
|
|
90
90
|
raise InvalidArgument, "Distributed queues require a worker-id" unless @config.worker_id
|
|
91
|
-
elsif queue.sharded?
|
|
91
|
+
elsif @queue.sharded?
|
|
92
92
|
unless @config.valid_worker_index?
|
|
93
93
|
raise InvalidArgument, "Splitting the queue requires a worker-id lower than workers-count, got: #{@config.worker_id.inspect}"
|
|
94
94
|
end
|
|
@@ -102,44 +102,43 @@ module Megatest
|
|
|
102
102
|
if test_cases.empty?
|
|
103
103
|
@err.puts "No tests to run"
|
|
104
104
|
return 1
|
|
105
|
+
elsif test_cases.size == 1
|
|
106
|
+
@config.jobs_count = 1
|
|
105
107
|
end
|
|
106
108
|
|
|
107
|
-
queue.populate(test_cases)
|
|
108
|
-
executor.run(queue, default_reporters)
|
|
109
|
-
queue.success? ? 0 : 1
|
|
109
|
+
@queue.populate(test_cases)
|
|
110
|
+
executor.run(@queue, default_reporters)
|
|
111
|
+
@queue.success? ? 0 : 1
|
|
110
112
|
end
|
|
111
113
|
|
|
112
114
|
def report
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
raise InvalidArgument, "Only distributed queues can be summarized" unless queue.distributed?
|
|
115
|
+
raise InvalidArgument, "Only distributed queues can be summarized" unless @queue.distributed?
|
|
116
116
|
raise InvalidArgument, "Distributed queues require a build-id" unless @config.build_id
|
|
117
117
|
raise InvalidArgument, @argv.join(" ") unless @argv.empty?
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
@config.selectors = Selector.new(@config).parse(@argv)
|
|
120
|
+
Megatest.load_config(@config)
|
|
120
121
|
|
|
121
|
-
QueueReporter.new(@config, queue, @out).run(default_reporters) ? 0 : 1
|
|
122
|
+
QueueReporter.new(@config, @queue, @out).run(default_reporters) ? 0 : 1
|
|
122
123
|
end
|
|
123
124
|
|
|
124
125
|
def bisect_tests
|
|
125
126
|
require "megatest/multi_process"
|
|
126
|
-
|
|
127
|
-
queue = @config.build_queue
|
|
128
|
-
raise InvalidArgument, "Distributed queues can't be bisected" if queue.distributed?
|
|
127
|
+
raise InvalidArgument, "Distributed queues can't be bisected" if @queue.distributed?
|
|
129
128
|
|
|
130
129
|
@config.selectors = Selector.new(@config).parse(@argv)
|
|
131
130
|
Megatest.load_config(@config)
|
|
132
131
|
Megatest.init(@config)
|
|
133
132
|
test_cases = Megatest.load_tests(@config)
|
|
134
|
-
queue.populate(test_cases)
|
|
135
|
-
candidates = queue.dup
|
|
133
|
+
@queue.populate(test_cases)
|
|
134
|
+
candidates = @queue.dup
|
|
136
135
|
|
|
137
136
|
if test_cases.empty?
|
|
138
137
|
@err.puts "No tests to run"
|
|
139
138
|
return 1
|
|
140
139
|
end
|
|
141
140
|
|
|
142
|
-
unless failure = find_failing_test
|
|
141
|
+
unless failure = find_failing_test
|
|
143
142
|
@err.puts "No failing test"
|
|
144
143
|
return 1
|
|
145
144
|
end
|
|
@@ -172,13 +171,13 @@ module Megatest
|
|
|
172
171
|
reporters
|
|
173
172
|
end
|
|
174
173
|
|
|
175
|
-
def find_failing_test
|
|
174
|
+
def find_failing_test
|
|
176
175
|
@config.max_consecutive_failures = 1
|
|
177
176
|
@config.jobs_count = 1
|
|
178
177
|
|
|
179
178
|
executor = MultiProcess::Executor.new(@config.dup, @out)
|
|
180
|
-
executor.run(queue, default_reporters)
|
|
181
|
-
queue.summary.failures.first
|
|
179
|
+
executor.run(@queue, default_reporters)
|
|
180
|
+
@queue.summary.failures.first
|
|
182
181
|
end
|
|
183
182
|
|
|
184
183
|
def bisect_queue(queue, failing_test_id)
|
|
@@ -260,10 +259,9 @@ module Megatest
|
|
|
260
259
|
end
|
|
261
260
|
end
|
|
262
261
|
|
|
263
|
-
def build_parser
|
|
264
|
-
runner = :run if runner.nil?
|
|
262
|
+
def build_parser
|
|
265
263
|
OptionParser.new do |opts|
|
|
266
|
-
case
|
|
264
|
+
case @command
|
|
267
265
|
when :report
|
|
268
266
|
opts.banner = "Usage: #{@program_name} report [options]"
|
|
269
267
|
when :run
|
|
@@ -281,7 +279,7 @@ module Megatest
|
|
|
281
279
|
opts.separator "\t\t\t $ #{@program_name} test/my_test.rb:42 test/another_test.rb:36"
|
|
282
280
|
opts.separator ""
|
|
283
281
|
|
|
284
|
-
opts.separator "\treport\t\tWait for the queue to be entirely processed and report the status"
|
|
282
|
+
opts.separator "\treport\t\tWait for the queue to be entirely processed and report the status."
|
|
285
283
|
opts.separator "\t\t\t $ #{@program_name} report --queue redis://ci-queue.example.com --build-id $CI_BUILD_ID"
|
|
286
284
|
opts.separator ""
|
|
287
285
|
|
|
@@ -290,50 +288,51 @@ module Megatest
|
|
|
290
288
|
opts.separator "\t\t\t $ #{@program_name} bisect --queue path/to/test_order.log"
|
|
291
289
|
opts.separator ""
|
|
292
290
|
end
|
|
291
|
+
@command ||= :run
|
|
293
292
|
|
|
294
293
|
opts.separator ""
|
|
295
294
|
opts.separator "Options:"
|
|
296
295
|
opts.separator ""
|
|
297
296
|
|
|
298
|
-
opts.on("-I PATHS", "
|
|
297
|
+
opts.on("-I PATHS", "Specify $LOAD_PATH directory (may be used more than once).") do |paths|
|
|
299
298
|
paths.split(":").each do |path|
|
|
300
299
|
$LOAD_PATH.unshift(path)
|
|
301
300
|
end
|
|
302
301
|
end
|
|
303
302
|
|
|
304
|
-
opts.on("-b", "--backtrace", "Print full backtraces") do
|
|
303
|
+
opts.on("-b", "--backtrace", "Print full backtraces.") do
|
|
305
304
|
@config.backtrace.full!
|
|
306
305
|
end
|
|
307
306
|
|
|
308
|
-
opts.on("-v", "--verbose", "Use the verbose reporter") do
|
|
307
|
+
opts.on("-v", "--verbose", "Use the verbose reporter.") do
|
|
309
308
|
@verbose = true
|
|
310
309
|
end
|
|
311
310
|
|
|
312
|
-
opts.on("--junit [PATH]", String, "Generate a junit.xml file") do |path|
|
|
311
|
+
opts.on("--junit [PATH]", String, "Generate a junit.xml file.") do |path|
|
|
313
312
|
@junit = path
|
|
314
313
|
end
|
|
315
314
|
|
|
316
|
-
if %i[run bisect].include?(
|
|
317
|
-
opts.on("--seed SEED", Integer, "The seed used to define run order") do |seed|
|
|
315
|
+
if %i[run bisect].include?(@command)
|
|
316
|
+
opts.on("--seed SEED", Integer, "The seed used to define run order.") do |seed|
|
|
318
317
|
@config.seed = seed
|
|
319
318
|
end
|
|
320
319
|
end
|
|
321
320
|
|
|
322
|
-
if
|
|
323
|
-
opts.on("-j", "--jobs JOBS", Integer, "Number of processes to use") do |jobs|
|
|
324
|
-
@config.jobs_count = jobs
|
|
321
|
+
if @command == :run
|
|
322
|
+
opts.on("-j", "--jobs [JOBS]", Integer, "Number of processes to use. Defaults to the number of processors.") do |jobs|
|
|
323
|
+
@config.jobs_count = jobs || :number_of_processors
|
|
325
324
|
end
|
|
326
325
|
|
|
327
|
-
help = "Number of consecutive failures before exiting.
|
|
326
|
+
help = "Number of consecutive failures before exiting. Defaults to 1."
|
|
328
327
|
opts.on("-f", "--fail-fast [COUNT]", Integer, help) do |max|
|
|
329
328
|
@config.max_consecutive_failures = (max || 1)
|
|
330
329
|
end
|
|
331
330
|
|
|
332
|
-
opts.on("--max-retries COUNT", Integer, "How many times a given test may be retried") do |max_retries|
|
|
331
|
+
opts.on("--max-retries COUNT", Integer, "How many times a given test may be retried.") do |max_retries|
|
|
333
332
|
@config.max_retries = max_retries
|
|
334
333
|
end
|
|
335
334
|
|
|
336
|
-
opts.on("--retry-tolerance RATE", Float, "The proportion of tests that may be retried
|
|
335
|
+
opts.on("--retry-tolerance RATE", Float, "The proportion of tests that may be retried, e.g. 0.05 for 5% of retried tests.") do |retry_tolerance|
|
|
337
336
|
@config.retry_tolerance = retry_tolerance
|
|
338
337
|
end
|
|
339
338
|
end
|
|
@@ -342,22 +341,22 @@ module Megatest
|
|
|
342
341
|
opts.separator "Test distribution and sharding:"
|
|
343
342
|
opts.separator ""
|
|
344
343
|
|
|
345
|
-
opts.on("--queue URL", String, "URL of queue server to use for test distribution. Default to $MEGATEST_QUEUE_URL") do |queue_url|
|
|
344
|
+
opts.on("--queue URL", String, "URL of queue server to use for test distribution. Default to $MEGATEST_QUEUE_URL.") do |queue_url|
|
|
346
345
|
@config.queue_url = queue_url
|
|
347
346
|
end
|
|
348
347
|
|
|
349
|
-
if %i[run report].include?(
|
|
350
|
-
opts.on("--build-id ID", String, "Unique identifier for the CI build") do |build_id|
|
|
348
|
+
if %i[run report].include?(@command)
|
|
349
|
+
opts.on("--build-id ID", String, "Unique identifier for the CI build.") do |build_id|
|
|
351
350
|
@config.build_id = build_id
|
|
352
351
|
end
|
|
353
352
|
end
|
|
354
353
|
|
|
355
|
-
if
|
|
356
|
-
opts.on("--worker-id ID", String, "Unique identifier for the CI job") do |worker_id|
|
|
354
|
+
if @command == :run
|
|
355
|
+
opts.on("--worker-id ID", String, "Unique identifier for the CI job.") do |worker_id|
|
|
357
356
|
@config.worker_id = worker_id
|
|
358
357
|
end
|
|
359
358
|
|
|
360
|
-
opts.on("--workers-count COUNT", Integer, "Number of CI jobs") do |workers_count|
|
|
359
|
+
opts.on("--workers-count COUNT", Integer, "Number of CI jobs.") do |workers_count|
|
|
361
360
|
@config.workers_count = workers_count
|
|
362
361
|
end
|
|
363
362
|
end
|
data/lib/megatest/config.rb
CHANGED
|
@@ -136,11 +136,11 @@ module Megatest
|
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
class Config
|
|
139
|
-
attr_accessor :queue_url, :retry_tolerance, :max_retries, :
|
|
139
|
+
attr_accessor :queue_url, :retry_tolerance, :max_retries, :job_index, :load_paths, :deprecations,
|
|
140
140
|
:build_id, :heartbeat_frequency, :minitest_compatibility, :ci, :selectors
|
|
141
141
|
attr_reader :before_fork_callbacks, :global_setup_callbacks, :backtrace, :circuit_breaker, :seed,
|
|
142
142
|
:worker_id, :workers_count, :test_globs
|
|
143
|
-
attr_writer :differ, :pretty_printer, :program_name, :colors
|
|
143
|
+
attr_writer :jobs_count, :differ, :pretty_printer, :program_name, :colors
|
|
144
144
|
|
|
145
145
|
def initialize(env)
|
|
146
146
|
@load_paths = ["test"] # For easier transition from other frameworks
|
|
@@ -153,7 +153,7 @@ module Megatest
|
|
|
153
153
|
@build_id = nil
|
|
154
154
|
@worker_id = nil
|
|
155
155
|
@workers_count = 1
|
|
156
|
-
@jobs_count =
|
|
156
|
+
@jobs_count = nil
|
|
157
157
|
@colors = nil # auto
|
|
158
158
|
@before_fork_callbacks = []
|
|
159
159
|
@global_setup_callbacks = []
|
|
@@ -186,6 +186,25 @@ module Megatest
|
|
|
186
186
|
@program_name || "megatest"
|
|
187
187
|
end
|
|
188
188
|
|
|
189
|
+
def jobs_count
|
|
190
|
+
if @jobs_count == :number_of_processors
|
|
191
|
+
if Megatest.fork?
|
|
192
|
+
require "etc"
|
|
193
|
+
nprocessors = Etc.nprocessors
|
|
194
|
+
jobs = [nprocessors, cgroups_cpu_quota&.to_i || nprocessors].min
|
|
195
|
+
@jobs_count = [jobs, 1].max
|
|
196
|
+
else
|
|
197
|
+
@jobs_count = 1
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
@jobs_count ||= 1
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def parallelize_maybe
|
|
204
|
+
@jobs_count ||= :number_of_processors if Megatest.fork?
|
|
205
|
+
self
|
|
206
|
+
end
|
|
207
|
+
|
|
189
208
|
def worker_id=(id)
|
|
190
209
|
@worker_id = if id.is_a?(String) && /\A\d+\z/.match?(id)
|
|
191
210
|
Integer(id)
|
|
@@ -314,6 +333,28 @@ module Megatest
|
|
|
314
333
|
|
|
315
334
|
private
|
|
316
335
|
|
|
336
|
+
def cgroups_cpu_quota
|
|
337
|
+
if RbConfig::CONFIG["target_os"].include?("linux")
|
|
338
|
+
if File.exist?("/sys/fs/cgroup/cpu.max")
|
|
339
|
+
# cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
|
|
340
|
+
cpu_max = File.read("/sys/fs/cgroup/cpu.max")
|
|
341
|
+
return nil if cpu_max.start_with?("max ") # no limit
|
|
342
|
+
|
|
343
|
+
max, period = cpu_max.split.map(&:to_f)
|
|
344
|
+
max / period
|
|
345
|
+
elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
|
|
346
|
+
# cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
|
|
347
|
+
max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
|
|
348
|
+
# If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
|
|
349
|
+
# https://docs.kernel.org/scheduler/sched-bwc.html#management
|
|
350
|
+
return nil if max <= 0
|
|
351
|
+
|
|
352
|
+
period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
|
|
353
|
+
max / period
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
317
358
|
def normalize_test_glob(patterns)
|
|
318
359
|
if patterns
|
|
319
360
|
Array(patterns).compact.map(&:to_s)
|
data/lib/megatest/dsl.rb
CHANGED
|
@@ -108,11 +108,11 @@ module Megatest
|
|
|
108
108
|
# end
|
|
109
109
|
# end
|
|
110
110
|
#
|
|
111
|
-
# Setup and teardown callbacks are not allowed
|
|
111
|
+
# Setup and teardown callbacks are not allowed within a context blocks,
|
|
112
112
|
# as it too easily lead to "write only" tests. It's only meant to help
|
|
113
113
|
# group test cases together.
|
|
114
114
|
#
|
|
115
|
-
# If you need a common setup procedure, just define a helper method, and
|
|
115
|
+
# If you need a common setup procedure, just define a helper method, and explicitly call it.
|
|
116
116
|
#
|
|
117
117
|
# Example:
|
|
118
118
|
#
|
|
@@ -147,7 +147,7 @@ module Megatest
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
# Registers a block to be invoked around every test cases.
|
|
150
|
-
# The block will
|
|
150
|
+
# The block will receive a Proc as first argument and MUST
|
|
151
151
|
# call it.
|
|
152
152
|
#
|
|
153
153
|
# Example:
|
data/lib/megatest/executor.rb
CHANGED
|
@@ -3,7 +3,26 @@
|
|
|
3
3
|
# :stopdoc:
|
|
4
4
|
|
|
5
5
|
module Megatest
|
|
6
|
-
class
|
|
6
|
+
class AbstractExecutor
|
|
7
|
+
def initialize(config, out)
|
|
8
|
+
@config = config
|
|
9
|
+
@out = Output.new(out, colors: @config.colors(out))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run(queue, reporters)
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def concurrent?
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def wall_time
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Executor < AbstractExecutor
|
|
7
26
|
class ExternalMonitor
|
|
8
27
|
def initialize(config)
|
|
9
28
|
require "rbconfig"
|
|
@@ -48,11 +67,6 @@ module Megatest
|
|
|
48
67
|
|
|
49
68
|
attr_reader :wall_time
|
|
50
69
|
|
|
51
|
-
def initialize(config, out)
|
|
52
|
-
@config = config
|
|
53
|
-
@out = Output.new(out, colors: @config.colors)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
70
|
def concurrent?
|
|
57
71
|
false
|
|
58
72
|
end
|
|
@@ -35,7 +35,7 @@ module Megatest
|
|
|
35
35
|
|
|
36
36
|
def read
|
|
37
37
|
Marshal.load(@socket)
|
|
38
|
-
rescue EOFError
|
|
38
|
+
rescue EOFError, Errno::ECONNRESET
|
|
39
39
|
nil # Other side was closed
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -203,12 +203,11 @@ module Megatest
|
|
|
203
203
|
end
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
-
class Executor
|
|
206
|
+
class Executor < AbstractExecutor
|
|
207
207
|
attr_reader :wall_time
|
|
208
208
|
|
|
209
|
-
def initialize(
|
|
210
|
-
|
|
211
|
-
@out = Output.new(out, colors: config.colors)
|
|
209
|
+
def initialize(*args, managed: false)
|
|
210
|
+
super(*args)
|
|
212
211
|
@managed = managed
|
|
213
212
|
end
|
|
214
213
|
|
|
@@ -122,7 +122,7 @@ module Megatest
|
|
|
122
122
|
last_b_pos = b_lo - 1
|
|
123
123
|
|
|
124
124
|
longest_unique_subsequence(a[a_lo...a_hi], b[b_lo...b_hi]).each do |(a_pos, b_pos)|
|
|
125
|
-
# recurse
|
|
125
|
+
# recurse between unique lines
|
|
126
126
|
a_pos += a_lo
|
|
127
127
|
b_pos += b_lo
|
|
128
128
|
if (last_a_pos + 1 != a_pos) || (last_b_pos + 1 != b_pos)
|
|
@@ -38,7 +38,7 @@ module Megatest
|
|
|
38
38
|
# Check whether the object_id +id+ is in the current buffer of objects
|
|
39
39
|
# to be pretty printed. Used to break cycles in chains of objects to be
|
|
40
40
|
# pretty printed.
|
|
41
|
-
def check_inspect_key(id)
|
|
41
|
+
def check_inspect_key?(id)
|
|
42
42
|
@recursive_key&.include?(id)
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -63,7 +63,7 @@ module Megatest
|
|
|
63
63
|
# detection
|
|
64
64
|
obj = obj.__getobj__ if defined?(::Delegator) && ::Delegator === obj
|
|
65
65
|
|
|
66
|
-
if check_inspect_key(obj)
|
|
66
|
+
if check_inspect_key?(obj)
|
|
67
67
|
group { pretty_print_cycle(obj) }
|
|
68
68
|
return
|
|
69
69
|
end
|
data/lib/megatest/queue.rb
CHANGED
|
@@ -169,7 +169,7 @@ module Megatest
|
|
|
169
169
|
alias_method :global_summary, :summary
|
|
170
170
|
|
|
171
171
|
def initialize(config)
|
|
172
|
-
super
|
|
172
|
+
super
|
|
173
173
|
|
|
174
174
|
@queue = nil
|
|
175
175
|
@summary = Summary.new
|
|
@@ -228,8 +228,8 @@ module Megatest
|
|
|
228
228
|
|
|
229
229
|
def record_result(result)
|
|
230
230
|
@leases.delete(result.test_id)
|
|
231
|
-
if result.failed?
|
|
232
|
-
if attempt_to_retry(result)
|
|
231
|
+
if result.failed? && !result.skipped?
|
|
232
|
+
if attempt_to_retry?(result)
|
|
233
233
|
result = result.retry
|
|
234
234
|
else
|
|
235
235
|
@success &&= result.ok?
|
|
@@ -241,7 +241,7 @@ module Megatest
|
|
|
241
241
|
|
|
242
242
|
private
|
|
243
243
|
|
|
244
|
-
def attempt_to_retry(result)
|
|
244
|
+
def attempt_to_retry?(result)
|
|
245
245
|
return false unless @config.retries?
|
|
246
246
|
return false unless @summary.retries_count < @config.total_max_retries(@size)
|
|
247
247
|
return false unless @retries[result.test_id] < @config.max_retries
|
data/lib/megatest/redis_queue.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Megatest
|
|
|
13
13
|
#
|
|
14
14
|
# - "leader-status": String, either `setup` or `ready`
|
|
15
15
|
#
|
|
16
|
-
# - "queue": List, contains the test ids that haven't yet been
|
|
16
|
+
# - "queue": List, contains the test ids that haven't yet been popped.
|
|
17
17
|
#
|
|
18
18
|
# - "running": SortedSet, members are the test ids currently being processed.
|
|
19
19
|
# Scores are the lease expiration timestamp. If the score is lower than
|
|
@@ -284,8 +284,8 @@ module Megatest
|
|
|
284
284
|
|
|
285
285
|
def record_result(original_result)
|
|
286
286
|
result = original_result
|
|
287
|
-
if result.failed?
|
|
288
|
-
if attempt_to_retry(result)
|
|
287
|
+
if result.failed? && !result.skipped?
|
|
288
|
+
if attempt_to_retry?(result)
|
|
289
289
|
result = result.retry
|
|
290
290
|
else
|
|
291
291
|
@success = false
|
|
@@ -381,8 +381,9 @@ module Megatest
|
|
|
381
381
|
|
|
382
382
|
return true
|
|
383
383
|
LUA
|
|
384
|
+
private_constant :REQUEUE
|
|
384
385
|
|
|
385
|
-
def attempt_to_retry(result)
|
|
386
|
+
def attempt_to_retry?(result)
|
|
386
387
|
return false unless @config.retries?
|
|
387
388
|
|
|
388
389
|
index = @config.random.rand(0..@redis.call("llen", key("queue")))
|
|
@@ -435,7 +436,7 @@ module Megatest
|
|
|
435
436
|
end
|
|
436
437
|
|
|
437
438
|
def worker_id
|
|
438
|
-
@worker_id or raise Error, "RedisQueue not
|
|
439
|
+
@worker_id or raise Error, "RedisQueue not configured with a worker id"
|
|
439
440
|
end
|
|
440
441
|
|
|
441
442
|
class RetryQueue < Queue
|
data/lib/megatest/reporters.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Megatest
|
|
|
9
9
|
|
|
10
10
|
def initialize(config, out)
|
|
11
11
|
@config = config
|
|
12
|
-
@out = Output.new(out, colors: config.colors)
|
|
12
|
+
@out = Output.new(out, colors: config.colors(out))
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def start(_executor, _queue)
|
|
@@ -32,6 +32,7 @@ module Megatest
|
|
|
32
32
|
failure: "Failure",
|
|
33
33
|
skipped: "Skipped",
|
|
34
34
|
}.freeze
|
|
35
|
+
private_constant :LABELS
|
|
35
36
|
|
|
36
37
|
def render_failure(result, command: true)
|
|
37
38
|
str = "#{LABELS.fetch(result.status)}: #{result.test_id}\n"
|
|
@@ -70,7 +71,9 @@ module Megatest
|
|
|
70
71
|
|
|
71
72
|
class SimpleReporter < AbstractReporter
|
|
72
73
|
def start(_executor, queue)
|
|
73
|
-
@out.
|
|
74
|
+
@out.print("Running #{queue.size} test cases with --seed #{@config.seed}")
|
|
75
|
+
@out.print(" in #{@config.jobs_count} processes") if @config.jobs_count > 1
|
|
76
|
+
@out.puts
|
|
74
77
|
@out.puts
|
|
75
78
|
end
|
|
76
79
|
|
|
@@ -125,7 +128,7 @@ module Megatest
|
|
|
125
128
|
end
|
|
126
129
|
end
|
|
127
130
|
|
|
128
|
-
@out.
|
|
131
|
+
@out.print format(
|
|
129
132
|
"Ran %d cases, %d assertions, %d failures, %d errors, %d retries, %d skips",
|
|
130
133
|
summary.runs_count,
|
|
131
134
|
summary.assertions_count,
|
|
@@ -134,6 +137,8 @@ module Megatest
|
|
|
134
137
|
summary.retries_count,
|
|
135
138
|
summary.skips_count,
|
|
136
139
|
)
|
|
140
|
+
@out.print(" in #{@config.jobs_count} processes") if @config.jobs_count > 1
|
|
141
|
+
@out.puts
|
|
137
142
|
end
|
|
138
143
|
|
|
139
144
|
def s(duration)
|
data/lib/megatest/runner.rb
CHANGED
|
@@ -10,9 +10,7 @@ module Megatest
|
|
|
10
10
|
|
|
11
11
|
def execute(test_case)
|
|
12
12
|
if test_case.tag(:isolated)
|
|
13
|
-
isolate(test_case)
|
|
14
|
-
run(test_case)
|
|
15
|
-
end
|
|
13
|
+
isolate(test_case)
|
|
16
14
|
else
|
|
17
15
|
run(test_case)
|
|
18
16
|
end
|
|
@@ -23,7 +21,7 @@ module Megatest
|
|
|
23
21
|
read, write = IO.pipe.each(&:binmode)
|
|
24
22
|
pid = Process.fork do
|
|
25
23
|
read.close
|
|
26
|
-
result =
|
|
24
|
+
result = run(test_case)
|
|
27
25
|
Marshal.dump(result, write)
|
|
28
26
|
write.close
|
|
29
27
|
# We don't want to run at_exit hooks the app may have
|
data/lib/megatest/runtime.rb
CHANGED
data/lib/megatest/test_task.rb
CHANGED
data/lib/megatest/version.rb
CHANGED
data/lib/megatest.rb
CHANGED
|
@@ -73,14 +73,14 @@ module Megatest
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def load_files(paths, name)
|
|
76
|
-
|
|
76
|
+
scanned = {}
|
|
77
77
|
paths.each do |path|
|
|
78
78
|
path = File.dirname(path) unless File.directory?(path)
|
|
79
79
|
|
|
80
80
|
while path.start_with?(PWD)
|
|
81
|
-
break if
|
|
81
|
+
break if scanned[path]
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
scanned[path] = true
|
|
84
84
|
|
|
85
85
|
config_path = File.join(path, name)
|
|
86
86
|
if File.exist?(config_path)
|