rspec-core 3.0.4 → 3.12.2
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +2 -1
- data/Changelog.md +888 -2
- data/{License.txt → LICENSE.md} +6 -5
- data/README.md +165 -24
- data/lib/rspec/autorun.rb +1 -0
- data/lib/rspec/core/backtrace_formatter.rb +19 -20
- data/lib/rspec/core/bisect/coordinator.rb +62 -0
- data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
- data/lib/rspec/core/bisect/fork_runner.rb +138 -0
- data/lib/rspec/core/bisect/server.rb +61 -0
- data/lib/rspec/core/bisect/shell_command.rb +126 -0
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +69 -0
- data/lib/rspec/core/configuration.rb +1287 -246
- data/lib/rspec/core/configuration_options.rb +95 -35
- data/lib/rspec/core/did_you_mean.rb +46 -0
- data/lib/rspec/core/drb.rb +21 -12
- data/lib/rspec/core/dsl.rb +10 -6
- data/lib/rspec/core/example.rb +305 -113
- data/lib/rspec/core/example_group.rb +431 -223
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +86 -115
- data/lib/rspec/core/flat_map.rb +6 -4
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +14 -116
- data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
- data/lib/rspec/core/formatters/console_codes.rb +29 -18
- data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
- data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
- data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
- data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +45 -15
- data/lib/rspec/core/formatters/html_formatter.rb +33 -28
- data/lib/rspec/core/formatters/html_printer.rb +30 -20
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +18 -9
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
- data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/formatters.rb +81 -41
- data/lib/rspec/core/hooks.rb +314 -244
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +161 -51
- data/lib/rspec/core/metadata.rb +132 -61
- data/lib/rspec/core/metadata_filter.rb +224 -64
- data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
- data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
- data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
- data/lib/rspec/core/mocking_adapters/null.rb +2 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
- data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
- data/lib/rspec/core/notifications.rb +192 -206
- data/lib/rspec/core/option_parser.rb +174 -69
- data/lib/rspec/core/ordering.rb +48 -35
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +25 -33
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer/.rspec +0 -2
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
- data/lib/rspec/core/project_initializer.rb +5 -3
- data/lib/rspec/core/rake_task.rb +99 -55
- data/lib/rspec/core/reporter.rb +128 -15
- data/lib/rspec/core/ruby_project.rb +14 -6
- data/lib/rspec/core/runner.rb +96 -45
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_example_group.rb +133 -43
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +6 -6
- data/lib/rspec/core/world.rb +172 -68
- data/lib/rspec/core.rb +66 -21
- data.tar.gz.sig +0 -0
- metadata +93 -69
- metadata.gz.sig +0 -0
- data/lib/rspec/core/backport_random.rb +0 -336
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
RSpec::Support.require_rspec_core "formatters/base_bisect_formatter"
|
|
3
|
+
RSpec::Support.require_rspec_core "bisect/utilities"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
module Core
|
|
7
|
+
module Bisect
|
|
8
|
+
# A Bisect runner that runs requested subsets of the suite by forking
|
|
9
|
+
# sub-processes. The main process bootstraps RSpec and the application
|
|
10
|
+
# environment (including preloading files specified via `--require`) so
|
|
11
|
+
# that the individual spec runs do not have to re-pay that cost. Each
|
|
12
|
+
# spec run happens in a forked process, ensuring that the spec files are
|
|
13
|
+
# not loaded in the main process.
|
|
14
|
+
#
|
|
15
|
+
# For most projects, bisections that use `ForkRunner` instead of
|
|
16
|
+
# `ShellRunner` will finish significantly faster, because the `ShellRunner`
|
|
17
|
+
# pays the cost of booting RSpec and the app environment on _every_ run of
|
|
18
|
+
# a subset. In contrast, `ForkRunner` pays that cost only once.
|
|
19
|
+
#
|
|
20
|
+
# However, not all projects can use `ForkRunner`. Obviously, on platforms
|
|
21
|
+
# that do not support forking (e.g. Windows), it cannot be used. In addition,
|
|
22
|
+
# it can cause problems for some projects that put side-effectful spec
|
|
23
|
+
# bootstrapping logic that should run on every spec run directly at the top
|
|
24
|
+
# level in a file loaded by `--require`, rather than in a `before(:suite)`
|
|
25
|
+
# hook. For example, consider a project that relies on some top-level logic
|
|
26
|
+
# in `spec_helper` to boot a Redis server for the test suite, intending the
|
|
27
|
+
# Redis bootstrapping to happen on every spec run. With `ShellRunner`, the
|
|
28
|
+
# bootstrapping logic will happen for each run of any subset of the suite,
|
|
29
|
+
# but for `ForkRunner`, such logic will only get run once, when the
|
|
30
|
+
# `RunDispatcher` boots the application environment. This might cause
|
|
31
|
+
# problems. The solution is for users to move the bootstrapping logic into
|
|
32
|
+
# a `before(:suite)` hook, or use the slower `ShellRunner`.
|
|
33
|
+
#
|
|
34
|
+
# @private
|
|
35
|
+
class ForkRunner
|
|
36
|
+
def self.start(shell_command, spec_runner)
|
|
37
|
+
instance = new(shell_command, spec_runner)
|
|
38
|
+
yield instance
|
|
39
|
+
ensure
|
|
40
|
+
instance.shutdown
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.name
|
|
44
|
+
:fork
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize(shell_command, spec_runner)
|
|
48
|
+
@shell_command = shell_command
|
|
49
|
+
@channel = Channel.new
|
|
50
|
+
@run_dispatcher = RunDispatcher.new(spec_runner, @channel)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def run(locations)
|
|
54
|
+
run_descriptor = ExampleSetDescriptor.new(locations, original_results.failed_example_ids)
|
|
55
|
+
dispatch_run(run_descriptor)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def original_results
|
|
59
|
+
@original_results ||= dispatch_run(ExampleSetDescriptor.new(
|
|
60
|
+
@shell_command.original_locations, []))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def shutdown
|
|
64
|
+
@channel.close
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def dispatch_run(run_descriptor)
|
|
70
|
+
@run_dispatcher.dispatch_specs(run_descriptor)
|
|
71
|
+
@channel.receive.tap do |result|
|
|
72
|
+
if result.is_a?(String)
|
|
73
|
+
raise BisectFailedError.for_failed_spec_run(result)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @private
|
|
79
|
+
class RunDispatcher
|
|
80
|
+
def initialize(runner, channel)
|
|
81
|
+
@runner = runner
|
|
82
|
+
@channel = channel
|
|
83
|
+
|
|
84
|
+
@spec_output = StringIO.new
|
|
85
|
+
|
|
86
|
+
runner.configuration.tap do |c|
|
|
87
|
+
c.reset_reporter
|
|
88
|
+
c.output_stream = @spec_output
|
|
89
|
+
c.error_stream = @spec_output
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def dispatch_specs(run_descriptor)
|
|
94
|
+
pid = fork { run_specs(run_descriptor) }
|
|
95
|
+
# We don't use Process.waitpid here as it was causing bisects to
|
|
96
|
+
# block due to the file descriptor limit on OSX / Linux. We need
|
|
97
|
+
# to detach the process to avoid having zombie processes
|
|
98
|
+
# consuming slots in the kernel process table during bisect runs.
|
|
99
|
+
Process.detach(pid)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def run_specs(run_descriptor)
|
|
105
|
+
$stdout = $stderr = @spec_output
|
|
106
|
+
formatter = CaptureFormatter.new(run_descriptor.failed_example_ids)
|
|
107
|
+
|
|
108
|
+
@runner.configuration.tap do |c|
|
|
109
|
+
c.files_or_directories_to_run = run_descriptor.all_example_ids
|
|
110
|
+
c.formatter = formatter
|
|
111
|
+
c.load_spec_files
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# `announce_filters` has the side effect of implementing the logic
|
|
115
|
+
# that honors `config.run_all_when_everything_filtered` so we need
|
|
116
|
+
# to call it here. When we remove `run_all_when_everything_filtered`
|
|
117
|
+
# (slated for RSpec 4), we can remove this call to `announce_filters`.
|
|
118
|
+
@runner.world.announce_filters
|
|
119
|
+
|
|
120
|
+
@runner.run_specs(@runner.world.ordered_example_groups)
|
|
121
|
+
latest_run_results = formatter.results
|
|
122
|
+
|
|
123
|
+
if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
|
|
124
|
+
@channel.send(@spec_output.string)
|
|
125
|
+
else
|
|
126
|
+
@channel.send(latest_run_results)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class CaptureFormatter < Formatters::BaseBisectFormatter
|
|
132
|
+
attr_accessor :results
|
|
133
|
+
alias_method :notify_results, :results=
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'drb/drb'
|
|
2
|
+
require 'drb/acl'
|
|
3
|
+
RSpec::Support.require_rspec_core "bisect/utilities"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
module Core
|
|
7
|
+
# @private
|
|
8
|
+
module Bisect
|
|
9
|
+
# @private
|
|
10
|
+
# A DRb server that receives run results from a separate RSpec process
|
|
11
|
+
# started by the bisect process.
|
|
12
|
+
class Server
|
|
13
|
+
def self.run
|
|
14
|
+
server = new
|
|
15
|
+
server.start
|
|
16
|
+
yield server
|
|
17
|
+
ensure
|
|
18
|
+
server.stop
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def capture_run_results(files_or_directories_to_run=[], expected_failures=[])
|
|
22
|
+
self.expected_failures = expected_failures
|
|
23
|
+
self.files_or_directories_to_run = files_or_directories_to_run
|
|
24
|
+
self.latest_run_results = nil
|
|
25
|
+
run_output = yield
|
|
26
|
+
|
|
27
|
+
if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
|
|
28
|
+
raise BisectFailedError.for_failed_spec_run(run_output)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
latest_run_results
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def start
|
|
35
|
+
# Only allow remote DRb requests from this machine.
|
|
36
|
+
DRb.install_acl ACL.new(%w[ deny all allow localhost allow 127.0.0.1 allow ::1 ])
|
|
37
|
+
|
|
38
|
+
# We pass `nil` as the first arg to allow it to pick a DRb port.
|
|
39
|
+
@drb = DRb.start_service(nil, self)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def stop
|
|
43
|
+
@drb.stop_service
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def drb_port
|
|
47
|
+
@drb_port ||= Integer(@drb.uri[/\d+$/])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Fetched via DRb by the BisectDRbFormatter to determine when to abort.
|
|
51
|
+
attr_accessor :expected_failures
|
|
52
|
+
|
|
53
|
+
# Set via DRb by the BisectDRbFormatter with the results of the run.
|
|
54
|
+
attr_accessor :latest_run_results
|
|
55
|
+
|
|
56
|
+
# Fetched via DRb to tell clients which files to run
|
|
57
|
+
attr_accessor :files_or_directories_to_run
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_core "shell_escape"
|
|
2
|
+
require 'shellwords'
|
|
3
|
+
|
|
4
|
+
module RSpec
|
|
5
|
+
module Core
|
|
6
|
+
module Bisect
|
|
7
|
+
# Provides an API to generate shell commands to run the suite for a
|
|
8
|
+
# set of locations, using the given bisect server to capture the results.
|
|
9
|
+
# @private
|
|
10
|
+
class ShellCommand
|
|
11
|
+
attr_reader :original_cli_args
|
|
12
|
+
|
|
13
|
+
def initialize(original_cli_args)
|
|
14
|
+
@original_cli_args = original_cli_args.reject { |arg| arg.start_with?("--bisect") }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def command_for(locations, server)
|
|
18
|
+
parts = []
|
|
19
|
+
|
|
20
|
+
parts << RUBY << load_path
|
|
21
|
+
parts << open3_safe_escape(RSpec::Core.path_to_executable)
|
|
22
|
+
|
|
23
|
+
parts << "--format" << "bisect-drb"
|
|
24
|
+
parts << "--drb-port" << server.drb_port
|
|
25
|
+
|
|
26
|
+
parts.concat(reusable_cli_options)
|
|
27
|
+
parts.concat(locations.map { |l| open3_safe_escape(l) })
|
|
28
|
+
|
|
29
|
+
parts.join(" ")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def repro_command_from(locations)
|
|
33
|
+
parts = []
|
|
34
|
+
|
|
35
|
+
parts.concat environment_repro_parts
|
|
36
|
+
parts << "rspec"
|
|
37
|
+
parts.concat Formatters::Helpers.organize_ids(locations)
|
|
38
|
+
parts.concat original_cli_args_without_locations
|
|
39
|
+
|
|
40
|
+
parts.join(" ")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def original_locations
|
|
44
|
+
parsed_original_cli_options.fetch(:files_or_directories_to_run)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def bisect_environment_hash
|
|
48
|
+
if ENV.key?('SPEC_OPTS')
|
|
49
|
+
{ 'SPEC_OPTS' => spec_opts_without_bisect }
|
|
50
|
+
else
|
|
51
|
+
{}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def spec_opts_without_bisect
|
|
56
|
+
Shellwords.join(
|
|
57
|
+
Shellwords.split(ENV.fetch('SPEC_OPTS', '')).reject do |arg|
|
|
58
|
+
arg =~ /^--bisect/
|
|
59
|
+
end
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
include RSpec::Core::ShellEscape
|
|
66
|
+
# On JRuby, Open3.popen3 does not handle shellescaped args properly:
|
|
67
|
+
# https://github.com/jruby/jruby/issues/2767
|
|
68
|
+
if RSpec::Support::Ruby.jruby?
|
|
69
|
+
# :nocov:
|
|
70
|
+
alias open3_safe_escape quote
|
|
71
|
+
# :nocov:
|
|
72
|
+
else
|
|
73
|
+
alias open3_safe_escape escape
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def environment_repro_parts
|
|
77
|
+
bisect_environment_hash.map do |k, v|
|
|
78
|
+
%Q(#{k}="#{v}")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def reusable_cli_options
|
|
83
|
+
@reusable_cli_options ||= begin
|
|
84
|
+
opts = original_cli_args_without_locations
|
|
85
|
+
|
|
86
|
+
if (port = parsed_original_cli_options[:drb_port])
|
|
87
|
+
opts -= %W[ --drb-port #{port} ]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
parsed_original_cli_options.fetch(:formatters) { [] }.each do |(name, out)|
|
|
91
|
+
opts -= %W[ --format #{name} -f -f#{name} ]
|
|
92
|
+
opts -= %W[ --out #{out} -o -o#{out} ]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
opts
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def original_cli_args_without_locations
|
|
100
|
+
@original_cli_args_without_locations ||= begin
|
|
101
|
+
files_or_dirs = parsed_original_cli_options.fetch(:files_or_directories_to_run)
|
|
102
|
+
@original_cli_args - files_or_dirs
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def parsed_original_cli_options
|
|
107
|
+
@parsed_original_cli_options ||= Parser.parse(@original_cli_args)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def load_path
|
|
111
|
+
@load_path ||= "-I#{$LOAD_PATH.map { |p| open3_safe_escape(p) }.join(':')}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Path to the currently running Ruby executable, borrowed from Rake:
|
|
115
|
+
# https://github.com/ruby/rake/blob/v10.4.2/lib/rake/file_utils.rb#L8-L12
|
|
116
|
+
# Note that we skip `ENV['RUBY']` because we don't have to deal with running
|
|
117
|
+
# RSpec from within a MRI source repository:
|
|
118
|
+
# https://github.com/ruby/rake/commit/968682759b3b65e42748cd2befb2ff3e982272d9
|
|
119
|
+
RUBY = File.join(
|
|
120
|
+
RbConfig::CONFIG['bindir'],
|
|
121
|
+
RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']).
|
|
122
|
+
sub(/.*\s.*/m, '"\&"')
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
RSpec::Support.require_rspec_core "bisect/server"
|
|
3
|
+
|
|
4
|
+
module RSpec
|
|
5
|
+
module Core
|
|
6
|
+
module Bisect
|
|
7
|
+
# Provides an API to run the suite for a set of locations, using
|
|
8
|
+
# the given bisect server to capture the results.
|
|
9
|
+
#
|
|
10
|
+
# Sets of specs are run by shelling out.
|
|
11
|
+
# @private
|
|
12
|
+
class ShellRunner
|
|
13
|
+
def self.start(shell_command, _spec_runner)
|
|
14
|
+
Server.run do |server|
|
|
15
|
+
yield new(server, shell_command)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.name
|
|
20
|
+
:shell
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(server, shell_command)
|
|
24
|
+
@server = server
|
|
25
|
+
@shell_command = shell_command
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run(locations)
|
|
29
|
+
run_locations(locations, original_results.failed_example_ids)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def original_results
|
|
33
|
+
@original_results ||= run_locations(@shell_command.original_locations)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def run_locations(*capture_args)
|
|
39
|
+
@server.capture_run_results(*capture_args) do
|
|
40
|
+
run_command @shell_command.command_for([], @server)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# `Open3.capture2e` does not work on JRuby:
|
|
45
|
+
# https://github.com/jruby/jruby/issues/2766
|
|
46
|
+
if Open3.respond_to?(:capture2e) && !RSpec::Support::Ruby.jruby?
|
|
47
|
+
def run_command(cmd)
|
|
48
|
+
Open3.capture2e(@shell_command.bisect_environment_hash, cmd).first
|
|
49
|
+
end
|
|
50
|
+
else # for 1.8.7
|
|
51
|
+
# :nocov:
|
|
52
|
+
def run_command(cmd)
|
|
53
|
+
out = err = nil
|
|
54
|
+
|
|
55
|
+
original_spec_opts = ENV['SPEC_OPTS']
|
|
56
|
+
ENV['SPEC_OPTS'] = @shell_command.spec_opts_without_bisect
|
|
57
|
+
|
|
58
|
+
Open3.popen3(cmd) do |_, stdout, stderr|
|
|
59
|
+
# Reading the streams blocks until the process is complete
|
|
60
|
+
out = stdout.read
|
|
61
|
+
err = stderr.read
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
"Stdout:\n#{out}\n\nStderr:\n#{err}"
|
|
65
|
+
ensure
|
|
66
|
+
ENV['SPEC_OPTS'] = original_spec_opts
|
|
67
|
+
end
|
|
68
|
+
# :nocov:
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Core
|
|
3
|
+
module Bisect
|
|
4
|
+
# @private
|
|
5
|
+
ExampleSetDescriptor = Struct.new(:all_example_ids, :failed_example_ids)
|
|
6
|
+
|
|
7
|
+
# @private
|
|
8
|
+
class BisectFailedError < StandardError
|
|
9
|
+
def self.for_failed_spec_run(spec_output)
|
|
10
|
+
new("Failed to get results from the spec run. Spec run output:\n\n" +
|
|
11
|
+
spec_output)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Wraps a `formatter` providing a simple means to notify it in place
|
|
16
|
+
# of an `RSpec::Core::Reporter`, without involving configuration in
|
|
17
|
+
# any way.
|
|
18
|
+
# @private
|
|
19
|
+
class Notifier
|
|
20
|
+
def initialize(formatter)
|
|
21
|
+
@formatter = formatter
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def publish(event, *args)
|
|
25
|
+
return unless @formatter.respond_to?(event)
|
|
26
|
+
notification = Notifications::CustomNotification.for(*args)
|
|
27
|
+
@formatter.__send__(event, notification)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Wraps a pipe to support sending objects between a child and
|
|
32
|
+
# parent process. Where supported, encoding is explicitly
|
|
33
|
+
# set to ensure binary data is able to pass from child to
|
|
34
|
+
# parent.
|
|
35
|
+
# @private
|
|
36
|
+
class Channel
|
|
37
|
+
if String.method_defined?(:encoding)
|
|
38
|
+
MARSHAL_DUMP_ENCODING = Marshal.dump("").encoding
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def initialize
|
|
42
|
+
@read_io, @write_io = IO.pipe
|
|
43
|
+
|
|
44
|
+
if defined?(MARSHAL_DUMP_ENCODING) && IO.method_defined?(:set_encoding)
|
|
45
|
+
# Ensure the pipe can send any content produced by Marshal.dump
|
|
46
|
+
@write_io.set_encoding MARSHAL_DUMP_ENCODING
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def send(message)
|
|
51
|
+
packet = Marshal.dump(message)
|
|
52
|
+
@write_io.write("#{packet.bytesize}\n#{packet}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# rubocop:disable Security/MarshalLoad
|
|
56
|
+
def receive
|
|
57
|
+
packet_size = Integer(@read_io.gets)
|
|
58
|
+
Marshal.load(@read_io.read(packet_size))
|
|
59
|
+
end
|
|
60
|
+
# rubocop:enable Security/MarshalLoad
|
|
61
|
+
|
|
62
|
+
def close
|
|
63
|
+
@read_io.close
|
|
64
|
+
@write_io.close
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|