rspec-conductor 1.0.9 → 1.0.10
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 +4 -0
- data/lib/rspec/conductor/cli.rb +6 -0
- data/lib/rspec/conductor/formatters/base.rb +18 -10
- data/lib/rspec/conductor/formatters/ci.rb +8 -8
- data/lib/rspec/conductor/formatters/fancy.rb +7 -7
- data/lib/rspec/conductor/formatters/plain.rb +1 -1
- data/lib/rspec/conductor/server.rb +27 -34
- data/lib/rspec/conductor/{results.rb → suite_run.rb} +6 -3
- data/lib/rspec/conductor/version.rb +1 -1
- data/lib/rspec/conductor/worker_process.rb +31 -2
- data/lib/rspec/conductor.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9de36557f5b670ac37493d920d186e4dcbcaca0c958d59914448b91aa5e6e488
|
|
4
|
+
data.tar.gz: 9d2371b5598a12adbc6ac907d80dfc4fbbe5f9c7d1c2b0f75cb17337345f51e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb67874bb0e5f84176ac601588d7931c97d70e3f8f6caff111e7cc1607b15007fcbc17f0053ff2c92f8e438cdad343530c91667de4d99f4fc7b95ac504f868fd
|
|
7
|
+
data.tar.gz: 4cb10a52cf0a0a6f9948c36287f2b6c5d35183df0ce6370196f9342e67c29ce5332780d8da8172d585be264170958fcbb317d77f03f3c23cb4ff70394b6e4cbe
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## [1.0.10] - 2026-03-08
|
|
2
|
+
|
|
3
|
+
- Add --print-slowest cli param to display the slowest specs in the suite
|
|
4
|
+
|
|
1
5
|
## [1.0.9] - 2026-03-01
|
|
2
6
|
|
|
3
7
|
- Handle workers stdout/stderr better. It is no longer necessary to use --verbose to see worker output. Verbose now only controls whether you see the debug output of the workers
|
data/lib/rspec/conductor/cli.rb
CHANGED
|
@@ -15,6 +15,7 @@ module RSpec
|
|
|
15
15
|
display_retry_backtraces: false,
|
|
16
16
|
prefork_require: 'config/application.rb',
|
|
17
17
|
postfork_require: :spec_helper,
|
|
18
|
+
print_slowest_count: nil,
|
|
18
19
|
}.freeze
|
|
19
20
|
|
|
20
21
|
def self.run(argv)
|
|
@@ -97,6 +98,10 @@ module RSpec
|
|
|
97
98
|
@conductor_options[:display_retry_backtraces] = true
|
|
98
99
|
end
|
|
99
100
|
|
|
101
|
+
opts.on("--print-slowest COUNT", Integer, "Print slowest specs with their execution times") do |n|
|
|
102
|
+
@conductor_options[:print_slowest_count] = n
|
|
103
|
+
end
|
|
104
|
+
|
|
100
105
|
opts.on("--verbose", "Enable debug output") do
|
|
101
106
|
@conductor_options[:verbose] = true
|
|
102
107
|
end
|
|
@@ -120,6 +125,7 @@ module RSpec
|
|
|
120
125
|
rspec_args: @rspec_args,
|
|
121
126
|
formatter: @conductor_options[:formatter],
|
|
122
127
|
display_retry_backtraces: @conductor_options[:display_retry_backtraces],
|
|
128
|
+
print_slowest_count: @conductor_options[:print_slowest_count],
|
|
123
129
|
verbose: @conductor_options[:verbose],
|
|
124
130
|
).run
|
|
125
131
|
end
|
|
@@ -9,7 +9,7 @@ module RSpec
|
|
|
9
9
|
def initialize(**_kwargs)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def handle_worker_message(_worker_process, _message,
|
|
12
|
+
def handle_worker_message(_worker_process, _message, _suite_run)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def print_startup_banner(worker_count:, seed:, spec_files_count:)
|
|
@@ -17,15 +17,15 @@ module RSpec
|
|
|
17
17
|
puts "Running #{spec_files_count} spec files\n\n"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def print_summary(
|
|
20
|
+
def print_summary(suite_run, seed:, success:)
|
|
21
21
|
puts "\n\n"
|
|
22
22
|
puts "Randomized with seed #{seed}"
|
|
23
|
-
puts "#{colorize("#{
|
|
24
|
-
puts colorize("Worker crashes: #{
|
|
23
|
+
puts "#{colorize("#{suite_run.examples_passed} passed", :green)}, #{colorize("#{suite_run.examples_failed} failed", :red)}, #{colorize("#{suite_run.examples_pending} pending", :yellow)}"
|
|
24
|
+
puts colorize("Worker crashes: #{suite_run.worker_crashes}", :red) if suite_run.worker_crashes.positive?
|
|
25
25
|
|
|
26
|
-
if
|
|
26
|
+
if suite_run.errors.any?
|
|
27
27
|
puts "\nFailures:\n\n"
|
|
28
|
-
|
|
28
|
+
suite_run.errors.each_with_index do |error, i|
|
|
29
29
|
puts " #{i + 1}) #{error[:description]}"
|
|
30
30
|
puts colorize(" #{error[:message]}", :red) if error[:message]
|
|
31
31
|
puts colorize(" #{error[:location]}", :cyan)
|
|
@@ -37,14 +37,22 @@ module RSpec
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
puts "Specs took: #{
|
|
41
|
-
puts "Total runtime: #{
|
|
40
|
+
puts "Specs took: #{suite_run.specs_runtime.round(2)}s"
|
|
41
|
+
puts "Total runtime: #{suite_run.total_runtime.round(2)}s"
|
|
42
42
|
puts "Suite: #{success ? colorize("PASSED", :green) : colorize("FAILED", :red)}"
|
|
43
43
|
|
|
44
|
-
if
|
|
44
|
+
if suite_run.errors.any?
|
|
45
45
|
puts ""
|
|
46
46
|
puts "To rerun failed examples:"
|
|
47
|
-
puts " rspec #{
|
|
47
|
+
puts " rspec #{suite_run.errors.map { |e| e[:location] }.join(" ")}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def print_slowest(suite_run, n)
|
|
52
|
+
puts "\n\n"
|
|
53
|
+
puts "Slowest #{n} specs:"
|
|
54
|
+
suite_run.example_stats.sort_by { |e| -e[:run_time] }.take(n).each_with_index do |e, i|
|
|
55
|
+
puts "%3d. (%8.2fms) %s @ %s" % [i + 1, e[:run_time] * 1000, colorize(e[:description], :dim), colorize(e[:location], :cyan)]
|
|
48
56
|
end
|
|
49
57
|
end
|
|
50
58
|
|
|
@@ -14,22 +14,22 @@ module RSpec
|
|
|
14
14
|
super(**kwargs)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def handle_worker_message(_worker_process, message,
|
|
17
|
+
def handle_worker_message(_worker_process, message, suite_run)
|
|
18
18
|
public_send(message[:type], message) if respond_to?(message[:type])
|
|
19
|
-
print_status(
|
|
19
|
+
print_status(suite_run) if @last_printout + @printout_interval < Time.now
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def print_status(
|
|
22
|
+
def print_status(suite_run)
|
|
23
23
|
@last_printout = Time.now
|
|
24
|
-
pct =
|
|
24
|
+
pct = suite_run.spec_file_processed_percentage
|
|
25
25
|
|
|
26
26
|
puts "-" * tty_width
|
|
27
27
|
puts "Current status [#{Time.now.strftime("%H:%M:%S")}]:"
|
|
28
|
-
puts "Processed: #{
|
|
29
|
-
puts "#{
|
|
30
|
-
if
|
|
28
|
+
puts "Processed: #{suite_run.spec_files_processed} / #{suite_run.spec_files_total} (#{(pct * 100).floor}%)"
|
|
29
|
+
puts "#{suite_run.examples_passed} passed, #{suite_run.examples_failed} failed, #{suite_run.examples_pending} pending"
|
|
30
|
+
if suite_run.errors.any?
|
|
31
31
|
puts "Failures:\n"
|
|
32
|
-
|
|
32
|
+
suite_run.errors.each_with_index do |error, i|
|
|
33
33
|
puts " #{i + 1}) #{error[:description]}"
|
|
34
34
|
puts " #{error[:location]}"
|
|
35
35
|
puts " #{error[:message]}" if error[:message]
|
|
@@ -30,9 +30,9 @@ module RSpec
|
|
|
30
30
|
super(**kwargs)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def handle_worker_message(worker_process, message,
|
|
33
|
+
def handle_worker_message(worker_process, message, suite_run)
|
|
34
34
|
public_send(message[:type], worker_process, message) if respond_to?(message[:type])
|
|
35
|
-
redraw(worker_process,
|
|
35
|
+
redraw(worker_process, suite_run)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def example_passed(_worker_process, _message)
|
|
@@ -66,9 +66,9 @@ module RSpec
|
|
|
66
66
|
|
|
67
67
|
private
|
|
68
68
|
|
|
69
|
-
def redraw(worker_process,
|
|
69
|
+
def redraw(worker_process, suite_run)
|
|
70
70
|
update_worker_status_line(worker_process)
|
|
71
|
-
|
|
71
|
+
update_suite_run_line(suite_run)
|
|
72
72
|
update_errors_line
|
|
73
73
|
@terminal.redraw
|
|
74
74
|
@terminal.scroll_to_bottom
|
|
@@ -94,13 +94,13 @@ module RSpec
|
|
|
94
94
|
@worker_lines[worker_process.number].update(status, redraw: false)
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
def
|
|
98
|
-
pct =
|
|
97
|
+
def update_suite_run_line(suite_run)
|
|
98
|
+
pct = suite_run.spec_file_processed_percentage
|
|
99
99
|
bar_width = [tty_width - 20, 20].max
|
|
100
100
|
filled = (pct * bar_width).floor
|
|
101
101
|
empty = bar_width - filled
|
|
102
102
|
|
|
103
|
-
percentage = " %3d%% (%d/%d)" % [(pct * 100).floor,
|
|
103
|
+
percentage = " %3d%% (%d/%d)" % [(pct * 100).floor, suite_run.spec_files_processed, suite_run.spec_files_total]
|
|
104
104
|
bar = colorize("[", :reset) + colorize("▓", :green) * filled + colorize(" ", :reset) * empty + colorize("]", :reset)
|
|
105
105
|
|
|
106
106
|
@progress_bar_line.update(bar + percentage, redraw: false)
|
|
@@ -4,7 +4,7 @@ module RSpec
|
|
|
4
4
|
module Conductor
|
|
5
5
|
module Formatters
|
|
6
6
|
class Plain < Base
|
|
7
|
-
def handle_worker_message(_worker_process, message,
|
|
7
|
+
def handle_worker_message(_worker_process, message, _suite_run)
|
|
8
8
|
public_send(message[:type], message) if respond_to?(message[:type])
|
|
9
9
|
end
|
|
10
10
|
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "English"
|
|
4
|
-
require "socket"
|
|
5
|
-
require "json"
|
|
6
|
-
|
|
7
3
|
module RSpec
|
|
8
4
|
module Conductor
|
|
9
5
|
class Server
|
|
@@ -21,6 +17,7 @@ module RSpec
|
|
|
21
17
|
# @option formatter [String] Use a certain formatter
|
|
22
18
|
# @option verbose [Boolean] Use especially verbose output
|
|
23
19
|
# @option display_retry_backtraces [Boolean] Display backtraces for specs retried via rspec-retry
|
|
20
|
+
# @option print_slowest_count [Integer] Print slowest specs in the end of the suite
|
|
24
21
|
def initialize(worker_count:, rspec_args:, **opts)
|
|
25
22
|
@worker_count = worker_count
|
|
26
23
|
@worker_number_offset = opts.fetch(:worker_number_offset, 0)
|
|
@@ -30,11 +27,11 @@ module RSpec
|
|
|
30
27
|
@seed = opts[:seed] || (Random.new_seed % MAX_SEED)
|
|
31
28
|
@fail_fast_after = opts[:fail_fast_after]
|
|
32
29
|
@display_retry_backtraces = opts.fetch(:display_retry_backtraces, false)
|
|
30
|
+
@print_slowest_count = opts.fetch(:print_slowest_count, nil)
|
|
33
31
|
@verbose = opts.fetch(:verbose, false)
|
|
34
32
|
|
|
35
33
|
@rspec_args = rspec_args
|
|
36
34
|
@worker_processes = []
|
|
37
|
-
@dead_worker_processes = []
|
|
38
35
|
@spec_queue = []
|
|
39
36
|
@formatter_class = case opts[:formatter]
|
|
40
37
|
when "ci"
|
|
@@ -47,7 +44,7 @@ module RSpec
|
|
|
47
44
|
(!@verbose && Formatters::Fancy.recommended?) ? Formatters::Fancy : Formatters::Plain
|
|
48
45
|
end
|
|
49
46
|
@formatter = @formatter_class.new(worker_count: @worker_count)
|
|
50
|
-
@
|
|
47
|
+
@suite_run = SuiteRun.new
|
|
51
48
|
|
|
52
49
|
$stdout.sync = true
|
|
53
50
|
$stdin.echo = false if $stdin.tty?
|
|
@@ -65,9 +62,10 @@ module RSpec
|
|
|
65
62
|
start_workers
|
|
66
63
|
run_event_loop
|
|
67
64
|
wait_for_workers_to_exit
|
|
68
|
-
@
|
|
65
|
+
@suite_run.suite_complete
|
|
69
66
|
|
|
70
|
-
@formatter.print_summary(@
|
|
67
|
+
@formatter.print_summary(@suite_run, seed: @seed, success: success?)
|
|
68
|
+
@formatter.print_slowest(@suite_run, @print_slowest_count) if @print_slowest_count
|
|
71
69
|
exit_with_status
|
|
72
70
|
end
|
|
73
71
|
|
|
@@ -95,7 +93,7 @@ module RSpec
|
|
|
95
93
|
debug "RSpec config: #{config.inspect}"
|
|
96
94
|
debug "Files to run: #{config.files_to_run}"
|
|
97
95
|
@spec_queue = config.files_to_run.shuffle(random: Random.new(@seed))
|
|
98
|
-
@
|
|
96
|
+
@suite_run.spec_files_total = @spec_queue.size
|
|
99
97
|
end
|
|
100
98
|
|
|
101
99
|
def preload_application
|
|
@@ -127,21 +125,18 @@ module RSpec
|
|
|
127
125
|
@shutdown_status = :shutdown_messages_sent
|
|
128
126
|
@formatter.print_shutdown_banner
|
|
129
127
|
@worker_processes.select(&:running?).each do |worker_process|
|
|
130
|
-
worker_process.
|
|
128
|
+
worker_process.send_message({ type: :shutdown })
|
|
131
129
|
cleanup_worker_process(worker_process)
|
|
132
130
|
end
|
|
133
131
|
end
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
readable_ios, = IO.select(worker_processes_by_io.keys, nil, nil, 0)
|
|
137
|
-
readable_ios&.each { |io| handle_worker_message(worker_processes_by_io.fetch(io)) }
|
|
138
|
-
Util::ChildProcess.tick_all(@worker_processes.map(&:child_process))
|
|
133
|
+
WorkerProcess.tick_all(@worker_processes)
|
|
139
134
|
reap_workers
|
|
140
135
|
end
|
|
141
136
|
end
|
|
142
137
|
|
|
143
138
|
def wait_for_workers_to_exit
|
|
144
|
-
|
|
139
|
+
WorkerProcess.wait_all(@worker_processes)
|
|
145
140
|
end
|
|
146
141
|
|
|
147
142
|
def spawn_worker(worker_number)
|
|
@@ -150,6 +145,7 @@ module RSpec
|
|
|
150
145
|
worker_process = WorkerProcess.spawn(
|
|
151
146
|
number: worker_number,
|
|
152
147
|
test_env_number: (@first_is_1 || worker_number != 1) ? worker_number.to_s : "",
|
|
148
|
+
on_message: ->(worker_process, message) { handle_worker_message(worker_process, message) },
|
|
153
149
|
on_stdout: ->(string) { @formatter.handle_worker_stdout(worker_number, string) },
|
|
154
150
|
on_stderr: ->(string) { @formatter.handle_worker_stderr(worker_number, string) },
|
|
155
151
|
debug_io: @verbose ? $stderr : nil,
|
|
@@ -160,37 +156,34 @@ module RSpec
|
|
|
160
156
|
worker_process
|
|
161
157
|
end
|
|
162
158
|
|
|
163
|
-
def handle_worker_message(worker_process)
|
|
164
|
-
message = worker_process.socket.receive_message
|
|
165
|
-
return unless message
|
|
166
|
-
|
|
159
|
+
def handle_worker_message(worker_process, message)
|
|
167
160
|
debug "Worker #{worker_process.number}: #{message[:type]}"
|
|
168
161
|
|
|
169
162
|
case message[:type].to_sym
|
|
170
163
|
when :example_passed
|
|
171
|
-
@
|
|
164
|
+
@suite_run.example_passed(message)
|
|
172
165
|
when :example_failed
|
|
173
|
-
@
|
|
166
|
+
@suite_run.example_failed(message)
|
|
174
167
|
|
|
175
|
-
if @fail_fast_after && @
|
|
176
|
-
debug "Shutting down after #{@
|
|
168
|
+
if @fail_fast_after && @suite_run.examples_failed >= @fail_fast_after
|
|
169
|
+
debug "Shutting down after #{@suite_run.examples_failed} failures"
|
|
177
170
|
initiate_shutdown
|
|
178
171
|
end
|
|
179
172
|
when :example_pending
|
|
180
|
-
@
|
|
173
|
+
@suite_run.example_pending
|
|
181
174
|
when :example_retried
|
|
182
175
|
@formatter.print_retry_message(message) if @display_retry_backtraces
|
|
183
176
|
when :spec_complete
|
|
184
|
-
@
|
|
177
|
+
@suite_run.spec_file_complete
|
|
185
178
|
worker_process.current_spec = nil
|
|
186
179
|
assign_work(worker_process)
|
|
187
180
|
when :spec_error
|
|
188
|
-
@
|
|
181
|
+
@suite_run.spec_file_error(message)
|
|
189
182
|
debug "Spec error details: #{message[:error]}"
|
|
190
183
|
worker_process.current_spec = nil
|
|
191
184
|
assign_work(worker_process)
|
|
192
185
|
end
|
|
193
|
-
@formatter.handle_worker_message(worker_process, message, @
|
|
186
|
+
@formatter.handle_worker_message(worker_process, message, @suite_run)
|
|
194
187
|
end
|
|
195
188
|
|
|
196
189
|
def assign_work(worker_process)
|
|
@@ -198,21 +191,21 @@ module RSpec
|
|
|
198
191
|
|
|
199
192
|
if shutting_down? || !spec_file
|
|
200
193
|
debug "No more work for worker #{worker_process.number}, sending shutdown"
|
|
201
|
-
worker_process.
|
|
194
|
+
worker_process.send_message({ type: :shutdown })
|
|
202
195
|
cleanup_worker_process(worker_process)
|
|
203
196
|
else
|
|
204
|
-
@
|
|
197
|
+
@suite_run.spec_file_assigned
|
|
205
198
|
worker_process.current_spec = spec_file
|
|
206
199
|
debug "Assigning #{spec_file} to worker #{worker_process.number}"
|
|
207
200
|
message = { type: :worker_assigned_spec, file: spec_file }
|
|
208
|
-
worker_process.
|
|
209
|
-
@formatter.handle_worker_message(worker_process, message, @
|
|
201
|
+
worker_process.send_message(message)
|
|
202
|
+
@formatter.handle_worker_message(worker_process, message, @suite_run)
|
|
210
203
|
end
|
|
211
204
|
end
|
|
212
205
|
|
|
213
206
|
def cleanup_worker_process(worker_process, status: :shut_down)
|
|
214
207
|
worker_process.shut_down(status)
|
|
215
|
-
@formatter.handle_worker_message(worker_process, { type: :worker_shut_down }, @
|
|
208
|
+
@formatter.handle_worker_message(worker_process, { type: :worker_shut_down }, @suite_run)
|
|
216
209
|
end
|
|
217
210
|
|
|
218
211
|
def reap_workers
|
|
@@ -224,7 +217,7 @@ module RSpec
|
|
|
224
217
|
|
|
225
218
|
dead_worker_processes.each do |worker_process, exitstatus|
|
|
226
219
|
cleanup_worker_process(worker_process, status: :terminated)
|
|
227
|
-
@
|
|
220
|
+
@suite_run.worker_crashed
|
|
228
221
|
debug "Worker #{worker_process.number} exited with status #{exitstatus.exitstatus}, signal #{exitstatus.termsig}"
|
|
229
222
|
end
|
|
230
223
|
end
|
|
@@ -243,7 +236,7 @@ module RSpec
|
|
|
243
236
|
end
|
|
244
237
|
|
|
245
238
|
def success?
|
|
246
|
-
@
|
|
239
|
+
@suite_run.success? && !shutting_down?
|
|
247
240
|
end
|
|
248
241
|
|
|
249
242
|
def exit_with_status
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module RSpec
|
|
4
4
|
module Conductor
|
|
5
|
-
class
|
|
6
|
-
attr_accessor :examples_passed, :examples_failed, :examples_pending, :worker_crashes, :errors, :started_at, :spec_files_total, :spec_files_processed
|
|
5
|
+
class SuiteRun
|
|
6
|
+
attr_accessor :examples_passed, :examples_failed, :examples_pending, :worker_crashes, :errors, :started_at, :spec_files_total, :spec_files_processed, :example_stats
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@examples_passed = 0
|
|
@@ -16,17 +16,20 @@ module RSpec
|
|
|
16
16
|
@specs_completed_at = nil
|
|
17
17
|
@spec_files_total = 0
|
|
18
18
|
@spec_files_processed = 0
|
|
19
|
+
@example_stats = []
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def success?
|
|
22
23
|
@examples_failed.zero? && @errors.empty? && @worker_crashes.zero? && @spec_files_total == @spec_files_processed
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
def example_passed
|
|
26
|
+
def example_passed(message)
|
|
27
|
+
@example_stats << message.slice(:location, :run_time, :description)
|
|
26
28
|
@examples_passed += 1
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def example_failed(message)
|
|
32
|
+
@example_stats << message.slice(:location, :run_time, :description)
|
|
30
33
|
@examples_failed += 1
|
|
31
34
|
@errors << message
|
|
32
35
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "socket"
|
|
4
|
+
|
|
3
5
|
module RSpec
|
|
4
6
|
module Conductor
|
|
5
|
-
WorkerProcess = Struct.new(:pid, :child_process, :number, :status, :socket, :current_spec, keyword_init: true) do
|
|
6
|
-
def self.spawn(number:, test_env_number:, on_stdout: nil, on_stderr: nil, **worker_init_args)
|
|
7
|
+
WorkerProcess = Struct.new(:pid, :child_process, :number, :on_message, :status, :socket, :current_spec, keyword_init: true) do
|
|
8
|
+
def self.spawn(number:, test_env_number:, on_message:, on_stdout: nil, on_stderr: nil, **worker_init_args)
|
|
7
9
|
parent_socket, child_socket = Socket.pair(:UNIX, :STREAM, 0)
|
|
8
10
|
child_process = Util::ChildProcess.fork(on_stdout: on_stdout, on_stderr: on_stderr) do
|
|
9
11
|
ENV["TEST_ENV_NUMBER"] = test_env_number
|
|
@@ -19,6 +21,7 @@ module RSpec
|
|
|
19
21
|
new(
|
|
20
22
|
pid: child_process.pid,
|
|
21
23
|
child_process: child_process,
|
|
24
|
+
on_message: on_message,
|
|
22
25
|
number: number,
|
|
23
26
|
status: :running,
|
|
24
27
|
socket: Protocol::Socket.new(parent_socket),
|
|
@@ -26,6 +29,32 @@ module RSpec
|
|
|
26
29
|
)
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
def self.tick_all(worker_processes)
|
|
33
|
+
worker_processes_by_io = worker_processes.select(&:running?).to_h { |w| [w.socket.io, w] }
|
|
34
|
+
readable_ios, = IO.select(worker_processes_by_io.keys, nil, nil, 0)
|
|
35
|
+
readable_ios&.each { |io| worker_processes_by_io.fetch(io).handle_message }
|
|
36
|
+
Util::ChildProcess.tick_all(worker_processes.map(&:child_process))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.wait_all(worker_processes)
|
|
40
|
+
Util::ChildProcess.wait_all(worker_processes.map(&:child_process))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handle_message
|
|
44
|
+
message = receive_message
|
|
45
|
+
return unless message && on_message
|
|
46
|
+
|
|
47
|
+
on_message.call(self, message)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def send_message(message)
|
|
51
|
+
socket.send_message(message)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def receive_message
|
|
55
|
+
socket.receive_message
|
|
56
|
+
end
|
|
57
|
+
|
|
29
58
|
def shut_down(status)
|
|
30
59
|
return unless running?
|
|
31
60
|
|
data/lib/rspec/conductor.rb
CHANGED
|
@@ -35,7 +35,7 @@ require_relative "conductor/version"
|
|
|
35
35
|
require_relative "conductor/protocol"
|
|
36
36
|
require_relative "conductor/server"
|
|
37
37
|
require_relative "conductor/worker"
|
|
38
|
-
require_relative "conductor/
|
|
38
|
+
require_relative "conductor/suite_run"
|
|
39
39
|
require_relative "conductor/worker_process"
|
|
40
40
|
require_relative "conductor/cli"
|
|
41
41
|
require_relative "conductor/rspec_subscriber"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspec-conductor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark Abramov
|
|
@@ -48,9 +48,9 @@ files:
|
|
|
48
48
|
- lib/rspec/conductor/formatters/plain.rb
|
|
49
49
|
- lib/rspec/conductor/protocol.rb
|
|
50
50
|
- lib/rspec/conductor/railtie.rb
|
|
51
|
-
- lib/rspec/conductor/results.rb
|
|
52
51
|
- lib/rspec/conductor/rspec_subscriber.rb
|
|
53
52
|
- lib/rspec/conductor/server.rb
|
|
53
|
+
- lib/rspec/conductor/suite_run.rb
|
|
54
54
|
- lib/rspec/conductor/util/ansi.rb
|
|
55
55
|
- lib/rspec/conductor/util/child_process.rb
|
|
56
56
|
- lib/rspec/conductor/util/screen_buffer.rb
|