rspec-conductor 1.0.6 → 1.0.7
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 +5 -0
- data/README.md +2 -0
- data/lib/rspec/conductor/formatters/base.rb +67 -0
- data/lib/rspec/conductor/formatters/ci.rb +4 -4
- data/lib/rspec/conductor/formatters/fancy.rb +5 -5
- data/lib/rspec/conductor/formatters/plain.rb +2 -8
- data/lib/rspec/conductor/server.rb +20 -47
- data/lib/rspec/conductor/version.rb +1 -1
- data/lib/rspec/conductor/worker.rb +11 -5
- data/lib/rspec/conductor.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6cb890370ce6bc38b652f6c998ca34fa8974c73677bb53794d5adddeda060db2
|
|
4
|
+
data.tar.gz: 3b2124f2014a5ae4c7f5e7c3b5aebeb8b6c1b624cab65d29b31d0219d23ac358
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a68df5d5e08d98b6c1b24a9e843432fa78e54c594dc0ff360bb49294de55d6f6eb730788c8d4386e2aa57e0856fdd18536560289f4dd2a22a228a67c1e9f5a8f
|
|
7
|
+
data.tar.gz: a88156e67f821bbeb2ef00bce93e2b6b327d5000fe2876a34c422a324294879417d63a30d260990775f6cd405c74811f03f62747ea782fc4757bcbcb75241062
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [1.0.7] - 2026-02-16
|
|
2
|
+
|
|
3
|
+
- Move all output code into the formatter base class (lay some groundwork to address some minor issues with the fancy formatter)
|
|
4
|
+
- Disable rspec's --require command line parameter. Use --prefork-require and --postfork-require in the rspec-conductor cli instead (reported by @cb341)
|
|
5
|
+
|
|
1
6
|
## [1.0.6] - 2026-02-14
|
|
2
7
|
|
|
3
8
|
- Better RSpec arguments handling, for example, --pattern and --exclude-pattern should be supported better (reported by @cb341)
|
data/README.md
CHANGED
|
@@ -95,6 +95,8 @@ I will also try my best to keep supporting as many rubies / rspec versions as I
|
|
|
95
95
|
* This is a common issue with ruby code, compiled libraries and forking. Set `OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES` environment variable to work around this
|
|
96
96
|
* Something gets loaded that shouldn't get loaded, or in a different order
|
|
97
97
|
* There are two simple ways to hook into preloads, exposed as CLI flags, `--prefork-require` (defaults to `config/application.rb`) and `--postfork-require` (defaults to either `rails_helper.rb` or `spec_helper.rb`, whichever is present on your machine). You can set any of those to whatever you need and control the load order
|
|
98
|
+
* `--require` options are ignored in command-line arguments or my `.rspec`
|
|
99
|
+
* rspec-conductor uses the full rspec configuration machinery to parse all the params. That includes `.rspec`, which, by default, includes `--require spec_helper` or `--require rails_helper` in most setups. If you need tighter control over what you load, use either `--prefork-require` or `--postfork-require`, these are actual ruby files, you can put any code or any `require` you need there.
|
|
98
100
|
|
|
99
101
|
## FAQ
|
|
100
102
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Conductor
|
|
5
|
+
class Base
|
|
6
|
+
include Util::ANSI
|
|
7
|
+
|
|
8
|
+
def initialize(**_kwargs)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def handle_worker_message(_worker_process, _message, _results)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def print_startup_banner(worker_count:, seed:, spec_files_count:)
|
|
15
|
+
puts "RSpec Conductor starting with #{worker_count} workers (seed: #{seed})"
|
|
16
|
+
puts "Running #{spec_files_count} spec files\n\n"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def print_summary(results, seed:)
|
|
20
|
+
puts "\n\n"
|
|
21
|
+
puts "Randomized with seed #{seed}"
|
|
22
|
+
puts "#{colorize("#{results.passed} passed", :green)}, #{colorize("#{results.failed} failed", :red)}, #{colorize("#{results.pending} pending", :yellow)}"
|
|
23
|
+
puts colorize("Worker crashes: #{results.worker_crashes}", :red) if results.worker_crashes.positive?
|
|
24
|
+
|
|
25
|
+
if results.errors.any?
|
|
26
|
+
puts "\nFailures:\n\n"
|
|
27
|
+
results.errors.each_with_index do |error, i|
|
|
28
|
+
puts " #{i + 1}) #{error[:description]}"
|
|
29
|
+
puts colorize(" #{error[:message]}", :red) if error[:message]
|
|
30
|
+
puts colorize(" #{error[:location]}", :cyan)
|
|
31
|
+
if error[:backtrace]&.any?
|
|
32
|
+
puts " Backtrace:"
|
|
33
|
+
error[:backtrace].each { |line| puts " #{line}" }
|
|
34
|
+
end
|
|
35
|
+
puts
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts "Specs took: #{results.specs_runtime.round(2)}s"
|
|
40
|
+
puts "Total runtime: #{results.total_runtime.round(2)}s"
|
|
41
|
+
puts "Suite: #{results.success? ? colorize("PASSED", :green) : colorize("FAILED", :red)}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def print_debug(string)
|
|
45
|
+
$stderr.puts string
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def print_retry_message(message)
|
|
49
|
+
puts <<~EOM
|
|
50
|
+
\nRetried: #{message[:description]}
|
|
51
|
+
#{message[:location]}
|
|
52
|
+
#{message[:exception_class]}: #{message[:message]}
|
|
53
|
+
Backtrace:
|
|
54
|
+
#{message[:backtrace].map { " #{_1}" }.join("\n")}
|
|
55
|
+
EOM
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def print_shut_down_banner
|
|
59
|
+
puts "Shutting down..."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def colorize(string, colors, **kwargs)
|
|
63
|
+
$stdout.tty? ? super(string, colors, **kwargs) : string
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
module RSpec
|
|
4
4
|
module Conductor
|
|
5
5
|
module Formatters
|
|
6
|
-
class CI
|
|
7
|
-
include Util::ANSI
|
|
8
|
-
|
|
6
|
+
class CI < Base
|
|
9
7
|
DEFAULT_PRINTOUT_INTERVAL = 10
|
|
10
8
|
|
|
11
9
|
# @option printout_interval how often a printout happens, in seconds
|
|
12
|
-
def initialize(printout_interval: DEFAULT_PRINTOUT_INTERVAL)
|
|
10
|
+
def initialize(printout_interval: DEFAULT_PRINTOUT_INTERVAL, **kwargs)
|
|
13
11
|
@printout_interval = printout_interval
|
|
14
12
|
@last_printout = Time.now
|
|
13
|
+
|
|
14
|
+
super(**kwargs)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def handle_worker_message(_worker_process, message, results)
|
|
@@ -5,14 +5,12 @@ require "pathname"
|
|
|
5
5
|
module RSpec
|
|
6
6
|
module Conductor
|
|
7
7
|
module Formatters
|
|
8
|
-
class Fancy
|
|
9
|
-
include Util::ANSI
|
|
10
|
-
|
|
8
|
+
class Fancy < Base
|
|
11
9
|
def self.recommended?
|
|
12
10
|
$stdout.tty? && $stdout.winsize[0] >= 30 && $stdout.winsize[1] >= 80
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
def initialize(worker_count
|
|
13
|
+
def initialize(worker_count:, **kwargs)
|
|
16
14
|
@worker_processes = {}
|
|
17
15
|
@terminal = Util::Terminal.new
|
|
18
16
|
@last_rendered_lines = []
|
|
@@ -27,6 +25,8 @@ module RSpec
|
|
|
27
25
|
@dots_line = @terminal.line(truncate: false)
|
|
28
26
|
@terminal.puts
|
|
29
27
|
@last_error_line = @terminal.line(truncate: false)
|
|
28
|
+
|
|
29
|
+
super(**kwargs)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def handle_worker_message(worker_process, message, results)
|
|
@@ -68,7 +68,7 @@ module RSpec
|
|
|
68
68
|
|
|
69
69
|
def update_worker_status_line(worker_process)
|
|
70
70
|
status = colorize("Worker #{worker_process.number}: ", :cyan)
|
|
71
|
-
status
|
|
71
|
+
status += if worker_process.status == :shut_down
|
|
72
72
|
"(finished)"
|
|
73
73
|
elsif worker_process.status == :terminated
|
|
74
74
|
colorize("(terminated)", :red)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module RSpec
|
|
2
2
|
module Conductor
|
|
3
3
|
module Formatters
|
|
4
|
-
class Plain
|
|
5
|
-
include Util::ANSI
|
|
6
|
-
|
|
4
|
+
class Plain < Base
|
|
7
5
|
def handle_worker_message(_worker_process, message, _results)
|
|
8
6
|
public_send(message[:type], message) if respond_to?(message[:type])
|
|
9
7
|
end
|
|
@@ -27,11 +25,7 @@ module RSpec
|
|
|
27
25
|
private
|
|
28
26
|
|
|
29
27
|
def print(string, color)
|
|
30
|
-
|
|
31
|
-
$stdout.print(colorize(string, color))
|
|
32
|
-
else
|
|
33
|
-
$stdout.print(string)
|
|
34
|
-
end
|
|
28
|
+
$stdout.print(colorize(string, color))
|
|
35
29
|
end
|
|
36
30
|
end
|
|
37
31
|
end
|
|
@@ -35,19 +35,21 @@ module RSpec
|
|
|
35
35
|
@rspec_args = rspec_args
|
|
36
36
|
@worker_processes = {}
|
|
37
37
|
@spec_queue = []
|
|
38
|
-
@
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
@formatter_class = case opts[:formatter]
|
|
39
|
+
when "ci"
|
|
40
|
+
Formatters::CI
|
|
41
|
+
when "fancy"
|
|
42
|
+
Formatters::Fancy
|
|
43
|
+
when "plain"
|
|
44
|
+
Formatters::Plain
|
|
45
|
+
else
|
|
46
|
+
(!@verbose && Formatters::Fancy.recommended?) ? Formatters::Fancy : Formatters::Plain
|
|
47
|
+
end
|
|
48
|
+
@formatter = @formatter_class.new(worker_count: @worker_count)
|
|
48
49
|
@results = Results.new
|
|
49
50
|
|
|
50
51
|
Dir.chdir(Conductor.root)
|
|
52
|
+
ENV['PARALLEL_TEST_GROUPS'] = worker_count.to_s # parallel_tests backward-compatibility
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def run
|
|
@@ -56,14 +58,13 @@ module RSpec
|
|
|
56
58
|
preload_application
|
|
57
59
|
|
|
58
60
|
$stdout.sync = true
|
|
59
|
-
|
|
60
|
-
puts "Running #{@spec_queue.size} spec files\n\n"
|
|
61
|
+
@formatter.print_startup_banner(worker_count: @worker_count, seed: @seed, spec_files_count: @spec_queue.size)
|
|
61
62
|
|
|
62
63
|
start_workers
|
|
63
64
|
run_event_loop
|
|
64
65
|
@results.suite_complete
|
|
65
66
|
|
|
66
|
-
print_summary
|
|
67
|
+
@formatter.print_summary(@results, seed: @seed)
|
|
67
68
|
exit_with_status
|
|
68
69
|
end
|
|
69
70
|
|
|
@@ -99,12 +100,15 @@ module RSpec
|
|
|
99
100
|
return if @results.shutting_down?
|
|
100
101
|
|
|
101
102
|
@results.shut_down
|
|
102
|
-
|
|
103
|
+
@formatter.print_shut_down_banner
|
|
103
104
|
@worker_processes.each_value { |w| w.socket&.send_message({ type: :shutdown }) }
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
def build_spec_queue
|
|
107
108
|
config_options = RSpec::Core::ConfigurationOptions.new(@rspec_args)
|
|
109
|
+
# a bit of a hack, but if they want to require something explicitly, they should use either --prefork-require or --postfork-require,
|
|
110
|
+
# as it is now, it messes with the preloads
|
|
111
|
+
config_options.options.delete(:requires)
|
|
108
112
|
if config_options.options[:files_or_directories_to_run].empty?
|
|
109
113
|
config_options.options[:files_or_directories_to_run] = ["spec"]
|
|
110
114
|
end
|
|
@@ -187,9 +191,7 @@ module RSpec
|
|
|
187
191
|
when :example_pending
|
|
188
192
|
@results.example_pending
|
|
189
193
|
when :example_retried
|
|
190
|
-
if @display_retry_backtraces
|
|
191
|
-
puts "\nExample #{message[:description]} retried:\n #{message[:location]}\n #{message[:exception_class]}: #{message[:message]}\n#{message[:backtrace].map { " #{_1}" }.join("\n")}\n"
|
|
192
|
-
end
|
|
194
|
+
@formatter.print_retry_message(message) if @display_retry_backtraces
|
|
193
195
|
when :spec_complete
|
|
194
196
|
@results.spec_file_complete
|
|
195
197
|
worker_process.current_spec = nil
|
|
@@ -248,35 +250,6 @@ module RSpec
|
|
|
248
250
|
nil
|
|
249
251
|
end
|
|
250
252
|
|
|
251
|
-
def print_summary
|
|
252
|
-
puts "\n\n"
|
|
253
|
-
puts "Randomized with seed #{@seed}"
|
|
254
|
-
puts "#{colorize("#{@results.passed} passed", :green)}, #{colorize("#{@results.failed} failed", :red)}, #{colorize("#{@results.pending} pending", :yellow)}"
|
|
255
|
-
puts colorize("Worker crashes: #{@results.worker_crashes}", :red) if @results.worker_crashes.positive?
|
|
256
|
-
|
|
257
|
-
if @results.errors.any?
|
|
258
|
-
puts "\nFailures:\n\n"
|
|
259
|
-
@results.errors.each_with_index do |error, i|
|
|
260
|
-
puts " #{i + 1}) #{error[:description]}"
|
|
261
|
-
puts " #{error[:location]}"
|
|
262
|
-
puts " #{error[:message]}" if error[:message]
|
|
263
|
-
if error[:backtrace]&.any?
|
|
264
|
-
puts " Backtrace:"
|
|
265
|
-
error[:backtrace].each { |line| puts " #{line}" }
|
|
266
|
-
end
|
|
267
|
-
puts
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
puts "Specs took: #{@results.specs_runtime.round(2)}s"
|
|
272
|
-
puts "Total runtime: #{@results.total_runtime.round(2)}s"
|
|
273
|
-
puts "Suite: #{@results.success? ? colorize("PASSED", :green) : colorize("FAILED", :red)}"
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def colorize(string, color)
|
|
277
|
-
$stdout.tty? ? Util::ANSI.colorize(string, color) : string
|
|
278
|
-
end
|
|
279
|
-
|
|
280
253
|
def exit_with_status
|
|
281
254
|
Kernel.exit(@results.success? ? 0 : 1)
|
|
282
255
|
end
|
|
@@ -284,7 +257,7 @@ module RSpec
|
|
|
284
257
|
def debug(message)
|
|
285
258
|
return unless @verbose
|
|
286
259
|
|
|
287
|
-
|
|
260
|
+
@formatter.print_debug("[conductor] #{message}")
|
|
288
261
|
end
|
|
289
262
|
end
|
|
290
263
|
end
|
|
@@ -62,11 +62,17 @@ module RSpec
|
|
|
62
62
|
@default_path = RSpec.configuration.default_path || "spec"
|
|
63
63
|
@default_full_path = File.expand_path(@default_path)
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
add_load_path(File.expand_path("lib"))
|
|
66
|
+
add_load_path(@default_full_path)
|
|
67
|
+
|
|
68
|
+
debug "Load path: #{$LOAD_PATH.inspect}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def add_load_path(path)
|
|
72
|
+
return unless File.directory?(path)
|
|
73
|
+
return if $LOAD_PATH.include?(path)
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
$LOAD_PATH.unshift(path)
|
|
70
76
|
end
|
|
71
77
|
|
|
72
78
|
def suppress_output
|
|
@@ -162,7 +168,7 @@ module RSpec
|
|
|
162
168
|
end
|
|
163
169
|
|
|
164
170
|
def parsed_options
|
|
165
|
-
@parsed_options ||= RSpec::Core::ConfigurationOptions.new(@rspec_args)
|
|
171
|
+
@parsed_options ||= RSpec::Core::ConfigurationOptions.new(@rspec_args).tap { |co| co.options.delete(:requires) }
|
|
166
172
|
end
|
|
167
173
|
|
|
168
174
|
def debug(message)
|
data/lib/rspec/conductor.rb
CHANGED
|
@@ -37,6 +37,7 @@ require_relative "conductor/results"
|
|
|
37
37
|
require_relative "conductor/worker_process"
|
|
38
38
|
require_relative "conductor/cli"
|
|
39
39
|
require_relative "conductor/rspec_subscriber"
|
|
40
|
+
require_relative "conductor/formatters/base"
|
|
40
41
|
require_relative "conductor/formatters/plain"
|
|
41
42
|
require_relative "conductor/formatters/ci"
|
|
42
43
|
require_relative "conductor/formatters/fancy"
|
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.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark Abramov
|
|
@@ -42,6 +42,7 @@ files:
|
|
|
42
42
|
- lib/rspec/conductor.rb
|
|
43
43
|
- lib/rspec/conductor/cli.rb
|
|
44
44
|
- lib/rspec/conductor/ext/rspec.rb
|
|
45
|
+
- lib/rspec/conductor/formatters/base.rb
|
|
45
46
|
- lib/rspec/conductor/formatters/ci.rb
|
|
46
47
|
- lib/rspec/conductor/formatters/fancy.rb
|
|
47
48
|
- lib/rspec/conductor/formatters/plain.rb
|