sspec-core 3.8.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 +7 -0
- data/.document +5 -0
- data/.yardopts +8 -0
- data/Changelog.md +2232 -0
- data/LICENSE.md +26 -0
- data/README.md +384 -0
- data/exe/rspec +4 -0
- data/lib/rspec/autorun.rb +3 -0
- data/lib/rspec/core.rb +185 -0
- data/lib/rspec/core/backtrace_formatter.rb +65 -0
- 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 +134 -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 +58 -0
- data/lib/rspec/core/configuration.rb +2289 -0
- data/lib/rspec/core/configuration_options.rb +233 -0
- data/lib/rspec/core/drb.rb +113 -0
- data/lib/rspec/core/dsl.rb +98 -0
- data/lib/rspec/core/example.rb +653 -0
- data/lib/rspec/core/example_group.rb +885 -0
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +231 -0
- data/lib/rspec/core/flat_map.rb +20 -0
- data/lib/rspec/core/formatters.rb +269 -0
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +70 -0
- data/lib/rspec/core/formatters/base_text_formatter.rb +75 -0
- 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 +68 -0
- data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
- data/lib/rspec/core/formatters/documentation_formatter.rb +70 -0
- data/lib/rspec/core/formatters/exception_presenter.rb +508 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +110 -0
- data/lib/rspec/core/formatters/html_formatter.rb +153 -0
- data/lib/rspec/core/formatters/html_printer.rb +414 -0
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +102 -0
- data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
- data/lib/rspec/core/formatters/progress_formatter.rb +29 -0
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +134 -0
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/hooks.rb +624 -0
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +554 -0
- data/lib/rspec/core/metadata.rb +499 -0
- data/lib/rspec/core/metadata_filter.rb +255 -0
- data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
- data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
- data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
- data/lib/rspec/core/mocking_adapters/null.rb +14 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
- data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
- data/lib/rspec/core/notifications.rb +521 -0
- data/lib/rspec/core/option_parser.rb +309 -0
- data/lib/rspec/core/ordering.rb +158 -0
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +165 -0
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer.rb +48 -0
- data/lib/rspec/core/project_initializer/.rspec +1 -0
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +100 -0
- data/lib/rspec/core/rake_task.rb +168 -0
- data/lib/rspec/core/reporter.rb +257 -0
- data/lib/rspec/core/ruby_project.rb +53 -0
- data/lib/rspec/core/runner.rb +199 -0
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_context.rb +55 -0
- data/lib/rspec/core/shared_example_group.rb +269 -0
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
- data/lib/rspec/core/version.rb +9 -0
- data/lib/rspec/core/warnings.rb +40 -0
- data/lib/rspec/core/world.rb +275 -0
- metadata +257 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Core
|
3
|
+
# @private
|
4
|
+
class BacktraceFormatter
|
5
|
+
# @private
|
6
|
+
attr_accessor :exclusion_patterns, :inclusion_patterns
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@full_backtrace = false
|
10
|
+
|
11
|
+
patterns = %w[ /lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle: ]
|
12
|
+
patterns << "org/jruby/" if RUBY_PLATFORM == 'java'
|
13
|
+
patterns.map! { |s| Regexp.new(s.gsub("/", File::SEPARATOR)) }
|
14
|
+
|
15
|
+
@exclusion_patterns = [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *patterns)]
|
16
|
+
@inclusion_patterns = []
|
17
|
+
|
18
|
+
return unless matches?(@exclusion_patterns, File.join(Dir.getwd, "lib", "foo.rb:13"))
|
19
|
+
inclusion_patterns << Regexp.new(Dir.getwd)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_writer :full_backtrace
|
23
|
+
|
24
|
+
def full_backtrace?
|
25
|
+
@full_backtrace || exclusion_patterns.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def filter_gem(gem_name)
|
29
|
+
sep = File::SEPARATOR
|
30
|
+
exclusion_patterns << /#{sep}#{gem_name}(-[^#{sep}]+)?#{sep}/
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_backtrace(backtrace, options={})
|
34
|
+
return [] unless backtrace
|
35
|
+
return backtrace if options[:full_backtrace] || backtrace.empty?
|
36
|
+
|
37
|
+
backtrace.map { |l| backtrace_line(l) }.compact.
|
38
|
+
tap do |filtered|
|
39
|
+
if filtered.empty?
|
40
|
+
filtered.concat backtrace
|
41
|
+
filtered << ""
|
42
|
+
filtered << " Showing full backtrace because every line was filtered out."
|
43
|
+
filtered << " See docs for RSpec::Configuration#backtrace_exclusion_patterns and"
|
44
|
+
filtered << " RSpec::Configuration#backtrace_inclusion_patterns for more information."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def backtrace_line(line)
|
50
|
+
Metadata.relative_path(line) unless exclude?(line)
|
51
|
+
end
|
52
|
+
|
53
|
+
def exclude?(line)
|
54
|
+
return false if @full_backtrace
|
55
|
+
matches?(exclusion_patterns, line) && !matches?(inclusion_patterns, line)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def matches?(patterns, line)
|
61
|
+
patterns.any? { |p| line =~ p }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
RSpec::Support.require_rspec_core "bisect/shell_command"
|
2
|
+
RSpec::Support.require_rspec_core "bisect/example_minimizer"
|
3
|
+
RSpec::Support.require_rspec_core "bisect/utilities"
|
4
|
+
RSpec::Support.require_rspec_core "formatters/bisect_progress_formatter"
|
5
|
+
|
6
|
+
module RSpec
|
7
|
+
module Core
|
8
|
+
module Bisect
|
9
|
+
# The main entry point into the bisect logic. Coordinates among:
|
10
|
+
# - Bisect::ShellCommand: Generates shell commands to run spec subsets
|
11
|
+
# - Bisect::ExampleMinimizer: Contains the core bisect logic.
|
12
|
+
# - A bisect runner: runs a set of examples and returns the results.
|
13
|
+
# - A bisect formatter: provides progress updates to the user.
|
14
|
+
# @private
|
15
|
+
class Coordinator
|
16
|
+
def self.bisect_with(spec_runner, original_cli_args, formatter)
|
17
|
+
new(spec_runner, original_cli_args, formatter).bisect
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(spec_runner, original_cli_args, formatter)
|
21
|
+
@spec_runner = spec_runner
|
22
|
+
@shell_command = ShellCommand.new(original_cli_args)
|
23
|
+
@notifier = Bisect::Notifier.new(formatter)
|
24
|
+
end
|
25
|
+
|
26
|
+
def bisect
|
27
|
+
repro = start_bisect_runner do |runner|
|
28
|
+
minimizer = ExampleMinimizer.new(@shell_command, runner, @notifier)
|
29
|
+
|
30
|
+
gracefully_abort_on_sigint(minimizer)
|
31
|
+
minimizer.find_minimal_repro
|
32
|
+
minimizer.repro_command_for_currently_needed_ids
|
33
|
+
end
|
34
|
+
|
35
|
+
@notifier.publish(:bisect_repro_command, :repro => repro)
|
36
|
+
|
37
|
+
true
|
38
|
+
rescue BisectFailedError => e
|
39
|
+
@notifier.publish(:bisect_failed, :failure_explanation => e.message)
|
40
|
+
false
|
41
|
+
ensure
|
42
|
+
@notifier.publish(:close)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def start_bisect_runner(&block)
|
48
|
+
klass = @spec_runner.configuration.bisect_runner_class
|
49
|
+
klass.start(@shell_command, @spec_runner, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def gracefully_abort_on_sigint(minimizer)
|
53
|
+
trap('INT') do
|
54
|
+
repro = minimizer.repro_command_for_currently_needed_ids
|
55
|
+
@notifier.publish(:bisect_aborted, :repro => repro)
|
56
|
+
exit(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
RSpec::Support.require_rspec_core "bisect/utilities"
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Core
|
5
|
+
module Bisect
|
6
|
+
# @private
|
7
|
+
# Contains the core bisect logic. Searches for examples we can ignore by
|
8
|
+
# repeatedly running different subsets of the suite.
|
9
|
+
class ExampleMinimizer
|
10
|
+
attr_reader :shell_command, :runner, :all_example_ids, :failed_example_ids
|
11
|
+
attr_accessor :remaining_ids
|
12
|
+
|
13
|
+
def initialize(shell_command, runner, notifier)
|
14
|
+
@shell_command = shell_command
|
15
|
+
@runner = runner
|
16
|
+
@notifier = notifier
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_minimal_repro
|
20
|
+
prep
|
21
|
+
|
22
|
+
_, duration = track_duration do
|
23
|
+
bisect(non_failing_example_ids)
|
24
|
+
end
|
25
|
+
|
26
|
+
notify(:bisect_complete, :duration => duration,
|
27
|
+
:original_non_failing_count => non_failing_example_ids.size,
|
28
|
+
:remaining_count => remaining_ids.size)
|
29
|
+
|
30
|
+
remaining_ids + failed_example_ids
|
31
|
+
end
|
32
|
+
|
33
|
+
def bisect(candidate_ids)
|
34
|
+
notify(:bisect_dependency_check_started)
|
35
|
+
if get_expected_failures_for?([])
|
36
|
+
notify(:bisect_dependency_check_failed)
|
37
|
+
self.remaining_ids = []
|
38
|
+
return
|
39
|
+
end
|
40
|
+
notify(:bisect_dependency_check_passed)
|
41
|
+
|
42
|
+
bisect_over(candidate_ids)
|
43
|
+
end
|
44
|
+
|
45
|
+
def bisect_over(candidate_ids)
|
46
|
+
return if candidate_ids.one?
|
47
|
+
|
48
|
+
notify(
|
49
|
+
:bisect_round_started,
|
50
|
+
:candidate_range => example_range(candidate_ids),
|
51
|
+
:candidates_count => candidate_ids.size
|
52
|
+
)
|
53
|
+
|
54
|
+
slice_size = (candidate_ids.length / 2.0).ceil
|
55
|
+
lhs, rhs = candidate_ids.each_slice(slice_size).to_a
|
56
|
+
|
57
|
+
ids_to_ignore, duration = track_duration do
|
58
|
+
[lhs, rhs].find do |ids|
|
59
|
+
get_expected_failures_for?(remaining_ids - ids)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if ids_to_ignore
|
64
|
+
self.remaining_ids -= ids_to_ignore
|
65
|
+
notify(
|
66
|
+
:bisect_round_ignoring_ids,
|
67
|
+
:ids_to_ignore => ids_to_ignore,
|
68
|
+
:ignore_range => example_range(ids_to_ignore),
|
69
|
+
:remaining_ids => remaining_ids,
|
70
|
+
:duration => duration
|
71
|
+
)
|
72
|
+
bisect_over(candidate_ids - ids_to_ignore)
|
73
|
+
else
|
74
|
+
notify(
|
75
|
+
:bisect_round_detected_multiple_culprits,
|
76
|
+
:duration => duration
|
77
|
+
)
|
78
|
+
bisect_over(lhs)
|
79
|
+
bisect_over(rhs)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def currently_needed_ids
|
84
|
+
remaining_ids + failed_example_ids
|
85
|
+
end
|
86
|
+
|
87
|
+
def repro_command_for_currently_needed_ids
|
88
|
+
return shell_command.repro_command_from(currently_needed_ids) if remaining_ids
|
89
|
+
"(Not yet enough information to provide any repro command)"
|
90
|
+
end
|
91
|
+
|
92
|
+
# @private
|
93
|
+
# Convenience class for describing a subset of the candidate examples
|
94
|
+
ExampleRange = Struct.new(:start, :finish) do
|
95
|
+
def description
|
96
|
+
if start == finish
|
97
|
+
"example #{start}"
|
98
|
+
else
|
99
|
+
"examples #{start}-#{finish}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def example_range(ids)
|
107
|
+
ExampleRange.new(
|
108
|
+
non_failing_example_ids.find_index(ids.first) + 1,
|
109
|
+
non_failing_example_ids.find_index(ids.last) + 1
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
def prep
|
114
|
+
notify(:bisect_starting, :original_cli_args => shell_command.original_cli_args,
|
115
|
+
:bisect_runner => runner.class.name)
|
116
|
+
|
117
|
+
_, duration = track_duration do
|
118
|
+
original_results = runner.original_results
|
119
|
+
@all_example_ids = original_results.all_example_ids
|
120
|
+
@failed_example_ids = original_results.failed_example_ids
|
121
|
+
@remaining_ids = non_failing_example_ids
|
122
|
+
end
|
123
|
+
|
124
|
+
if @failed_example_ids.empty?
|
125
|
+
raise BisectFailedError, "\n\nNo failures found. Bisect only works " \
|
126
|
+
"in the presence of one or more failing examples."
|
127
|
+
else
|
128
|
+
notify(:bisect_original_run_complete, :failed_example_ids => failed_example_ids,
|
129
|
+
:non_failing_example_ids => non_failing_example_ids,
|
130
|
+
:duration => duration)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def non_failing_example_ids
|
135
|
+
@non_failing_example_ids ||= all_example_ids - failed_example_ids
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_expected_failures_for?(ids)
|
139
|
+
ids_to_run = ids + failed_example_ids
|
140
|
+
notify(
|
141
|
+
:bisect_individual_run_start,
|
142
|
+
:command => shell_command.repro_command_from(ids_to_run),
|
143
|
+
:ids_to_run => ids_to_run
|
144
|
+
)
|
145
|
+
|
146
|
+
results, duration = track_duration { runner.run(ids_to_run) }
|
147
|
+
notify(:bisect_individual_run_complete, :duration => duration, :results => results)
|
148
|
+
|
149
|
+
abort_if_ordering_inconsistent(results)
|
150
|
+
(failed_example_ids & results.failed_example_ids) == failed_example_ids
|
151
|
+
end
|
152
|
+
|
153
|
+
def track_duration
|
154
|
+
start = ::RSpec::Core::Time.now
|
155
|
+
[yield, ::RSpec::Core::Time.now - start]
|
156
|
+
end
|
157
|
+
|
158
|
+
def abort_if_ordering_inconsistent(results)
|
159
|
+
expected_order = all_example_ids & results.all_example_ids
|
160
|
+
return if expected_order == results.all_example_ids
|
161
|
+
|
162
|
+
raise BisectFailedError, "\n\nThe example ordering is inconsistent. " \
|
163
|
+
"`--bisect` relies upon consistent ordering (e.g. by passing " \
|
164
|
+
"`--seed` if you're using random ordering) to work properly."
|
165
|
+
end
|
166
|
+
|
167
|
+
def notify(*args)
|
168
|
+
@notifier.publish(*args)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,134 @@
|
|
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 master 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
|
+
Process.waitpid(pid)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def run_specs(run_descriptor)
|
101
|
+
$stdout = $stderr = @spec_output
|
102
|
+
formatter = CaptureFormatter.new(run_descriptor.failed_example_ids)
|
103
|
+
|
104
|
+
@runner.configuration.tap do |c|
|
105
|
+
c.files_or_directories_to_run = run_descriptor.all_example_ids
|
106
|
+
c.formatter = formatter
|
107
|
+
c.load_spec_files
|
108
|
+
end
|
109
|
+
|
110
|
+
# `announce_filters` has the side effect of implementing the logic
|
111
|
+
# that honors `config.run_all_when_everything_filtered` so we need
|
112
|
+
# to call it here. When we remove `run_all_when_everything_filtered`
|
113
|
+
# (slated for RSpec 4), we can remove this call to `announce_filters`.
|
114
|
+
@runner.world.announce_filters
|
115
|
+
|
116
|
+
@runner.run_specs(@runner.world.ordered_example_groups)
|
117
|
+
latest_run_results = formatter.results
|
118
|
+
|
119
|
+
if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
|
120
|
+
@channel.send(@spec_output.string)
|
121
|
+
else
|
122
|
+
@channel.send(latest_run_results)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class CaptureFormatter < Formatters::BaseBisectFormatter
|
128
|
+
attr_accessor :results
|
129
|
+
alias_method :notify_results, :results=
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
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 ])
|
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
|