ci-queue 0.97.0 → 0.98.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/Gemfile.lock +1 -1
- data/README.md +51 -0
- data/lib/ci/queue/version.rb +1 -1
- data/lib/minitest/queue/test_data.rb +20 -1
- data/lib/minitest/queue.rb +82 -0
- 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: 020b8bf0b6887aa814e3f6b310bc62888e9a5e0af94c6ed652efc0af2b947ef7
|
|
4
|
+
data.tar.gz: 11e86bbe7b3e6bacfe96b84ae131c40222fb61a1afa9788f953c3a85db02d751
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b02b9a927e82321c3792c5a479487ef2a80e1093e23d4614e1e3b9191b9a5b5f9691cbdc78bd2a2a566e97dcbcf6231c3d9df19521bc2408099b6e905c38ecfa
|
|
7
|
+
data.tar.gz: 39cbdb4ef28553b81373f5836a8267b1919815fcab5959893bf002a307b7341e6337e045b00dd8a06e4c93dba2a1b3bd05ca9b96672292b5e25b8f50cc507fbf
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -138,6 +138,57 @@ The runner also comes with a tool to investigate leaky tests:
|
|
|
138
138
|
minitest-queue --queue path/to/test_order.log --failing-test 'SomeTest#test_something' bisect -Itest test/**/*_test.rb
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
#### Parallel worker metadata
|
|
142
|
+
|
|
143
|
+
Each result is stamped, as it is recorded in the process that ran the test, with:
|
|
144
|
+
|
|
145
|
+
- `parallel_worker_pid`: the pid of the process that ran the test.
|
|
146
|
+
- `parallel_worker_test_index`: a 0-based monotonic counter of results recorded by that process (requeued executions get their own index). Restarts at 0 in each forked process.
|
|
147
|
+
- `parallel_worker_id`: an identifier injected by the embedding environment (e.g. a Rails parallel-testing worker number); `nil` when not applicable.
|
|
148
|
+
|
|
149
|
+
All three fields are included (nil-safe) in `log/test_data.json` emitted by the test data reporter, making per-worker-process execution order reconstructable downstream (`PARTITION BY parallel_worker_id, parallel_worker_pid ORDER BY parallel_worker_test_index`).
|
|
150
|
+
|
|
151
|
+
The worker id can be provided either programmatically or via the environment:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
Minitest::Queue.parallel_worker_id = 3
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
| Variable | Description |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `CI_QUEUE_PARALLEL_WORKER_ID=N` | Sets `parallel_worker_id` for results produced by this process. Read once per process; the setter takes precedence. |
|
|
160
|
+
|
|
161
|
+
Stamping is first-writer-wins. When ci-queue runs tests in-process (the normal
|
|
162
|
+
`minitest-queue` flow), results are stamped automatically as they are recorded.
|
|
163
|
+
Embedding environments that run tests in forked workers and transport results to
|
|
164
|
+
another process (e.g. over DRb to a central reporting server) must stamp in the
|
|
165
|
+
worker **before** sending:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
# in the forked worker, after running the test and before the DRb send
|
|
169
|
+
Minitest::Queue.stamp_parallel_worker_metadata(result)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Otherwise the automatic stamp during reporting would capture the reporting
|
|
173
|
+
process's pid and an arrival-order index interleaved across workers. Pre-stamped
|
|
174
|
+
results pass through reporting untouched.
|
|
175
|
+
|
|
176
|
+
Notes:
|
|
177
|
+
|
|
178
|
+
- `parallel_worker_id` identifies a forked test process *inside* one queue worker.
|
|
179
|
+
It is unrelated to ci-queue's own `--worker` / `CI::Queue::Configuration#worker_id`,
|
|
180
|
+
which identifies the whole queue worker (typically one CI job).
|
|
181
|
+
- The payload intentionally carries no build/job identity — like every other field
|
|
182
|
+
in `log/test_data.json`, scoping to a build/job (e.g. a `job_id` column making
|
|
183
|
+
`(job_id, parallel_worker_id, parallel_worker_pid)` unique across a build) is
|
|
184
|
+
expected to be attached by whatever pipeline ingests the file.
|
|
185
|
+
- Stamping is mutex-guarded, so indexes are unique and gap-free even when results
|
|
186
|
+
are recorded from multiple threads. However, per-worker order reconstruction is
|
|
187
|
+
only meaningful with process-based (forked) workers: under thread-based
|
|
188
|
+
parallelization all threads share one `(parallel_worker_id, parallel_worker_pid)`
|
|
189
|
+
partition and the index reflects record order across threads.
|
|
190
|
+
- This applies to minitest-queue only; rspec-queue does not emit these fields.
|
|
191
|
+
|
|
141
192
|
### RSpec [DEPRECATED]
|
|
142
193
|
|
|
143
194
|
The rspec-queue runner is deprecated. The minitest-queue runner continues to be supported and is actively being improved. At Shopify, we strongly recommend that new projects set up their test suite using Minitest rather than RSpec.
|
data/lib/ci/queue/version.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Minitest
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def test_duration
|
|
52
|
-
@test.time
|
|
52
|
+
@test.time || 0.0
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def test_start_timestamp
|
|
@@ -73,6 +73,22 @@ module Minitest
|
|
|
73
73
|
@test.source_location.last
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# The parallel_worker_* fields are stamped by
|
|
77
|
+
# Minitest::Queue.stamp_parallel_worker_metadata in the worker process
|
|
78
|
+
# that ran the test. They are nil for embedders that don't produce
|
|
79
|
+
# them (results lacking the accessors, or no worker id configured).
|
|
80
|
+
def parallel_worker_id
|
|
81
|
+
@test.parallel_worker_id if @test.respond_to?(:parallel_worker_id)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def parallel_worker_test_index
|
|
85
|
+
@test.parallel_worker_test_index if @test.respond_to?(:parallel_worker_test_index)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parallel_worker_pid
|
|
89
|
+
@test.parallel_worker_pid if @test.respond_to?(:parallel_worker_pid)
|
|
90
|
+
end
|
|
91
|
+
|
|
76
92
|
# Error class only considers failures wheras the other error fields also consider skips
|
|
77
93
|
def error_class
|
|
78
94
|
return nil unless @test.failure
|
|
@@ -119,6 +135,9 @@ module Minitest
|
|
|
119
135
|
test_finish_timestamp: test_finish_timestamp,
|
|
120
136
|
test_file_path: test_file_path,
|
|
121
137
|
test_file_line_number: test_file_line_number,
|
|
138
|
+
parallel_worker_id: parallel_worker_id,
|
|
139
|
+
parallel_worker_test_index: parallel_worker_test_index,
|
|
140
|
+
parallel_worker_pid: parallel_worker_pid,
|
|
122
141
|
error_class: error_class,
|
|
123
142
|
error_message: error_message,
|
|
124
143
|
error_file_path: error_file_path,
|
data/lib/minitest/queue.rb
CHANGED
|
@@ -111,10 +111,22 @@ module Minitest
|
|
|
111
111
|
attr_accessor :queue_id, :queue_entry
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# Per-worker-process execution metadata, stamped on each result in the
|
|
115
|
+
# process that ran the test (before any DRb send in embedding
|
|
116
|
+
# environments), so that per-process execution order is reconstructable
|
|
117
|
+
# downstream: PARTITION BY parallel_worker_id, parallel_worker_pid
|
|
118
|
+
# ORDER BY parallel_worker_test_index.
|
|
119
|
+
module ParallelWorkerMetadata
|
|
120
|
+
attr_accessor :parallel_worker_id, :parallel_worker_test_index, :parallel_worker_pid
|
|
121
|
+
end
|
|
122
|
+
|
|
114
123
|
module Queue
|
|
115
124
|
extend ::CI::Queue::OutputHelpers
|
|
116
125
|
attr_writer :run_command_formatter, :project_root
|
|
117
126
|
|
|
127
|
+
PARALLEL_WORKER_METADATA_MUTEX = Mutex.new
|
|
128
|
+
private_constant :PARALLEL_WORKER_METADATA_MUTEX
|
|
129
|
+
|
|
118
130
|
def run_command_formatter
|
|
119
131
|
@run_command_formatter ||= if defined?(Rails) && defined?(Rails::TestUnitRailtie)
|
|
120
132
|
RAILS_RUN_COMMAND_FORMATTER
|
|
@@ -155,6 +167,60 @@ module Minitest
|
|
|
155
167
|
end
|
|
156
168
|
|
|
157
169
|
class << self
|
|
170
|
+
# Identifies which parallel worker this process is (e.g. Rails'
|
|
171
|
+
# parallel testing worker number). Injected by the embedding
|
|
172
|
+
# environment via this setter or the CI_QUEUE_PARALLEL_WORKER_ID
|
|
173
|
+
# environment variable; nil when not applicable.
|
|
174
|
+
attr_writer :parallel_worker_id
|
|
175
|
+
|
|
176
|
+
def parallel_worker_id
|
|
177
|
+
return @parallel_worker_id if @parallel_worker_id
|
|
178
|
+
|
|
179
|
+
# Memoized per process: re-read after a fork, but not on every test.
|
|
180
|
+
if @parallel_worker_id_env_pid != Process.pid
|
|
181
|
+
@parallel_worker_id_env_pid = Process.pid
|
|
182
|
+
@parallel_worker_id_from_env = parallel_worker_id_from_env
|
|
183
|
+
end
|
|
184
|
+
@parallel_worker_id_from_env
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Stamps per-process execution metadata on a result. Must be called
|
|
188
|
+
# in the process that ran the test so pid and per-process test index
|
|
189
|
+
# are captured worker-side.
|
|
190
|
+
#
|
|
191
|
+
# First writer wins: results that were already stamped are left
|
|
192
|
+
# untouched. Embedding environments that run tests in forked workers
|
|
193
|
+
# and transport results to another process (e.g. over DRb) MUST call
|
|
194
|
+
# this in the worker before sending, otherwise the stamp applied
|
|
195
|
+
# during reporting would carry the reporting process's pid and an
|
|
196
|
+
# arrival-order index interleaved across workers.
|
|
197
|
+
#
|
|
198
|
+
# The counter is mutex-guarded: results recorded from multiple threads
|
|
199
|
+
# (e.g. a DRb server dispatching each call on its own thread) get
|
|
200
|
+
# unique, gap-free indexes reflecting record order in this process.
|
|
201
|
+
# Returns the result when it was stamped, nil when it was skipped.
|
|
202
|
+
def stamp_parallel_worker_metadata(result)
|
|
203
|
+
return unless result.respond_to?(:parallel_worker_pid=)
|
|
204
|
+
return if result.parallel_worker_pid # already stamped worker-side
|
|
205
|
+
|
|
206
|
+
PARALLEL_WORKER_METADATA_MUTEX.synchronize do
|
|
207
|
+
pid = Process.pid
|
|
208
|
+
if @parallel_worker_metadata_pid != pid
|
|
209
|
+
# Restart the per-process counter in forked workers so that
|
|
210
|
+
# (parallel_worker_id, parallel_worker_pid, parallel_worker_test_index)
|
|
211
|
+
# always reflects execution order within a single process.
|
|
212
|
+
@parallel_worker_metadata_pid = pid
|
|
213
|
+
@parallel_worker_next_test_index = 0
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
result.parallel_worker_id = parallel_worker_id
|
|
217
|
+
result.parallel_worker_pid = pid
|
|
218
|
+
result.parallel_worker_test_index = @parallel_worker_next_test_index
|
|
219
|
+
@parallel_worker_next_test_index += 1
|
|
220
|
+
end
|
|
221
|
+
result
|
|
222
|
+
end
|
|
223
|
+
|
|
158
224
|
def queue
|
|
159
225
|
Minitest.queue
|
|
160
226
|
end
|
|
@@ -179,6 +245,8 @@ module Minitest
|
|
|
179
245
|
end
|
|
180
246
|
|
|
181
247
|
def handle_test_result(reporter, example, result)
|
|
248
|
+
stamp_parallel_worker_metadata(result)
|
|
249
|
+
|
|
182
250
|
if result.respond_to?(:queue_id=)
|
|
183
251
|
result.queue_id = example.id
|
|
184
252
|
result.queue_entry = example.queue_entry if result.respond_to?(:queue_entry=)
|
|
@@ -213,6 +281,17 @@ module Minitest
|
|
|
213
281
|
|
|
214
282
|
private
|
|
215
283
|
|
|
284
|
+
def parallel_worker_id_from_env
|
|
285
|
+
value = ENV['CI_QUEUE_PARALLEL_WORKER_ID']
|
|
286
|
+
return nil if value.nil? || value.empty?
|
|
287
|
+
|
|
288
|
+
begin
|
|
289
|
+
Integer(value)
|
|
290
|
+
rescue ArgumentError
|
|
291
|
+
value
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
216
295
|
def report_load_stats(queue)
|
|
217
296
|
return unless CI::Queue.debug?
|
|
218
297
|
return unless queue.respond_to?(:file_loader)
|
|
@@ -441,6 +520,7 @@ module Minitest
|
|
|
441
520
|
if result
|
|
442
521
|
result.start_timestamp = start_timestamp
|
|
443
522
|
result.finish_timestamp = current_timestamp
|
|
523
|
+
result.time ||= (result.finish_timestamp - result.start_timestamp).to_f
|
|
444
524
|
end
|
|
445
525
|
end
|
|
446
526
|
|
|
@@ -608,11 +688,13 @@ if defined? Minitest::Result
|
|
|
608
688
|
Minitest::Result.prepend(Minitest::Flakiness)
|
|
609
689
|
Minitest::Result.prepend(Minitest::WithTimestamps)
|
|
610
690
|
Minitest::Result.prepend(Minitest::ResultMetadata)
|
|
691
|
+
Minitest::Result.prepend(Minitest::ParallelWorkerMetadata)
|
|
611
692
|
else
|
|
612
693
|
Minitest::Test.prepend(Minitest::Requeueing)
|
|
613
694
|
Minitest::Test.prepend(Minitest::Flakiness)
|
|
614
695
|
Minitest::Test.prepend(Minitest::WithTimestamps)
|
|
615
696
|
Minitest::Test.prepend(Minitest::ResultMetadata)
|
|
697
|
+
Minitest::Test.prepend(Minitest::ParallelWorkerMetadata)
|
|
616
698
|
|
|
617
699
|
module MinitestBackwardCompatibility
|
|
618
700
|
def source_location
|