Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a3a929c
commit ebc8615
Showing
4 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
require 'drb/drb' | ||
|
||
module RSpec | ||
module Core | ||
# @private | ||
module Bisect | ||
# @private | ||
# A DRb server that receives run results from a separate RSpec process | ||
# started by the bisect process. | ||
class Server | ||
def self.run | ||
server = new | ||
server.start | ||
yield server | ||
ensure | ||
server.stop | ||
end | ||
|
||
def capture_run_results(abort_after_example_id=nil) | ||
self.abort_after_example_id = abort_after_example_id | ||
yield | ||
latest_run_results | ||
end | ||
|
||
def start | ||
# We pass `nil` as the first arg to allow it to pick a DRb port. | ||
@drb = DRb.start_service(nil, self) | ||
end | ||
|
||
def stop | ||
@drb.stop_service | ||
end | ||
|
||
def drb_port | ||
@drb_port ||= Integer(@drb.uri[/\d+$/]) | ||
end | ||
|
||
# Fetched via DRb by the BisectFormatter to determine when to abort. | ||
attr_accessor :abort_after_example_id | ||
|
||
# Set via DRb by the BisectFormatter with the results of the run. | ||
attr_accessor :latest_run_results | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
require 'drb/drb' | ||
|
||
module RSpec | ||
module Core | ||
module Formatters | ||
# Used by `--bisect`. When it shells out and runs a portion of the suite, it uses | ||
# this formatter as a means to have the status reported back to it, via DRb. | ||
# | ||
# Note that since DRb calls carry considerable overhead compared to normal | ||
# method calls, we try to minimize the number of DRb calls for perf reasons, | ||
# opting to communicate only at the start and the end of the run, rather than | ||
# after each example. | ||
# @private | ||
class BisectFormatter | ||
Formatters.register self, :start, :start_dump, :example_started, | ||
:example_failed, :example_passed, :example_pending | ||
|
||
def initialize(_output) | ||
port = RSpec.configuration.drb_port | ||
drb_uri = "druby://127.0.0.1:#{port}" | ||
@all_example_ids = [] | ||
@failed_example_ids = [] | ||
@bisect_server = DRbObject.new_with_uri(drb_uri) | ||
@abort_after_id = nil | ||
end | ||
|
||
def start(_notification) | ||
@abort_after_id = @bisect_server.abort_after_example_id | ||
end | ||
|
||
def example_started(notification) | ||
@all_example_ids << notification.example.id | ||
end | ||
|
||
def example_failed(notification) | ||
@failed_example_ids << notification.example.id | ||
example_finished(notification) | ||
end | ||
|
||
def example_passed(notification) | ||
example_finished(notification) | ||
end | ||
|
||
def example_pending(notification) | ||
example_finished(notification) | ||
end | ||
|
||
def start_dump(_notification) | ||
@bisect_server.latest_run_results = RunResults.new( | ||
@all_example_ids, @failed_example_ids | ||
) | ||
end | ||
|
||
RunResults = Struct.new(:all_example_ids_in_execution_order, | ||
:failed_example_ids) | ||
|
||
private | ||
|
||
def example_finished(notification) | ||
return unless notification.example.id == @abort_after_id | ||
RSpec.world.wants_to_quit = true | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
require 'rspec/core/bisect/server' | ||
require 'support/formatter_support' | ||
|
||
module RSpec::Core | ||
RSpec.describe Bisect::Server do | ||
RSpec::Matchers.define :have_running_server do | ||
match do |drb| | ||
begin | ||
drb.current_server.alive? | ||
rescue DRb::DRbServerNotFound | ||
false | ||
end | ||
end | ||
end | ||
|
||
it 'always stops the server, even if an error occurs while yielding' do | ||
expect(DRb).not_to have_running_server | ||
|
||
expect { | ||
Bisect::Server.run do | ||
expect(DRb).to have_running_server | ||
raise "boom" | ||
end | ||
}.to raise_error("boom") | ||
|
||
expect(DRb).not_to have_running_server | ||
end | ||
|
||
context "when used in combination with the BisectFormatter", :slow do | ||
include FormatterSupport | ||
|
||
attr_reader :server | ||
|
||
around do |ex| | ||
Bisect::Server.run do |the_server| | ||
@server = the_server | ||
ex.run | ||
end | ||
end | ||
|
||
def run_formatter_specs | ||
RSpec.configuration.drb_port = server.drb_port | ||
run_example_specs_with_formatter("bisect") | ||
end | ||
|
||
it 'receives suite results' do | ||
results = server.capture_run_results do | ||
run_formatter_specs | ||
end | ||
|
||
expect(results).to have_attributes( | ||
:all_example_ids_in_execution_order => %w[ | ||
./spec/rspec/core/resources/formatter_specs.rb[1:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[2:1:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[2:2:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[3:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[4:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[5:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[5:2] | ||
], | ||
:failed_example_ids => %w[ | ||
./spec/rspec/core/resources/formatter_specs.rb[2:2:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[4:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[5:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[5:2] | ||
] | ||
) | ||
end | ||
|
||
it 'can abort the run early (e.g. when it is not interested in later examples)' do | ||
results = server.capture_run_results("./spec/rspec/core/resources/formatter_specs.rb[2:2:1]") do | ||
run_formatter_specs | ||
end | ||
|
||
expect(results).to have_attributes( | ||
:all_example_ids_in_execution_order => %w[ | ||
./spec/rspec/core/resources/formatter_specs.rb[1:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[2:1:1] | ||
./spec/rspec/core/resources/formatter_specs.rb[2:2:1] | ||
], | ||
:failed_example_ids => %w[ | ||
./spec/rspec/core/resources/formatter_specs.rb[2:2:1] | ||
] | ||
) | ||
end | ||
|
||
# TODO: test aborting after pending vs failed vs passing example if we keep this feature. | ||
end | ||
end | ||
end |