class RSpec::Core::Bisect::ForkRunner
@private
a ‘before(:suite)` hook, or use the slower `ShellRunner`.
problems. The solution is for users to move the bootstrapping logic into
`RunDispatcher` boots the application environment. This might cause
but for `ForkRunner`, such logic will only get run once, when the
bootstrapping logic will happen for each run of any subset of the suite,
Redis bootstrapping to happen on every spec run. With `ShellRunner`, the
in `spec_helper` to boot a Redis server for the test suite, intending the
hook. For example, consider a project that relies on some top-level logic
level in a file loaded by `–require`, rather than in a `before(:suite)`
bootstrapping logic that should run on every spec run directly at the top
it can cause problems for some projects that put side-effectful spec
that do not support forking (e.g. Windows), it cannot be used. In addition,
However, not all projects can use `ForkRunner`. Obviously, on platforms
a subset. In contrast, `ForkRunner` pays that cost only once.
pays the cost of booting RSpec and the app environment on every run of
`ShellRunner` will finish significantly faster, because the `ShellRunner`
For most projects, bisections that use `ForkRunner` instead of
not loaded in the main process.
spec run happens in a forked process, ensuring that the spec files are
that the individual spec runs do not have to re-pay that cost. Each
environment (including preloading files specified via `–require`) so
sub-processes. The master process bootstraps RSpec and the application
A Bisect runner that runs requested subsets of the suite by forking
def self.name
def self.name :fork end
def self.start(shell_command, spec_runner)
def self.start(shell_command, spec_runner) instance = new(shell_command, spec_runner) yield instance ensure instance.shutdown end
def dispatch_run(run_descriptor)
def dispatch_run(run_descriptor) @run_dispatcher.dispatch_specs(run_descriptor) @channel.receive.tap do |result| if result.is_a?(String) raise BisectFailedError.for_failed_spec_run(result) end end end
def initialize(shell_command, spec_runner)
def initialize(shell_command, spec_runner) @shell_command = shell_command @channel = Channel.new @run_dispatcher = RunDispatcher.new(spec_runner, @channel) end
def original_results
def original_results @original_results ||= dispatch_run(ExampleSetDescriptor.new( @shell_command.original_locations, [])) end
def run(locations)
def run(locations) run_descriptor = ExampleSetDescriptor.new(locations, original_results.failed_example_ids) dispatch_run(run_descriptor) end
def shutdown
def shutdown @channel.close end