Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 32045fd

Browse files
committed
When bisecting, exit each run as soon as possible.
- When an expected failure passes (or is pending) we don’t care about any other results. - When the last expected failure finishes, we don’t care about any later examples.
1 parent c002258 commit 32045fd

File tree

7 files changed

+122
-29
lines changed

7 files changed

+122
-29
lines changed

lib/rspec/core/bisect/runner.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ def initialize(server, original_cli_args)
1717
end
1818

1919
def run(locations)
20-
@server.capture_run_results do
21-
system command_for(locations)
22-
end
20+
run_locations(locations, original_results.failed_example_ids)
2321
end
2422

2523
def command_for(locations)
@@ -47,11 +45,17 @@ def repro_command_from(locations)
4745
end
4846

4947
def original_results
50-
@original_results ||= run(original_locations)
48+
@original_results ||= run_locations(original_locations)
5149
end
5250

5351
private
5452

53+
def run_locations(locations, *capture_args)
54+
@server.capture_run_results(*capture_args) do
55+
system command_for(locations)
56+
end
57+
end
58+
5559
def reusable_cli_options
5660
@reusable_cli_options ||= begin
5761
opts = original_cli_args_without_locations

lib/rspec/core/bisect/server.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ def self.run
1616
server.stop
1717
end
1818

19-
def capture_run_results(abort_after_example_id=nil)
20-
self.abort_after_example_id = abort_after_example_id
19+
def capture_run_results(expected_failures=[])
20+
self.expected_failures = expected_failures
2121
yield
2222
latest_run_results
2323
end
@@ -36,7 +36,7 @@ def drb_port
3636
end
3737

3838
# Fetched via DRb by the BisectFormatter to determine when to abort.
39-
attr_accessor :abort_after_example_id
39+
attr_accessor :expected_failures
4040

4141
# Set via DRb by the BisectFormatter with the results of the run.
4242
attr_accessor :latest_run_results

lib/rspec/core/formatters/bisect_formatter.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ def initialize(_output)
2121
@all_example_ids = []
2222
@failed_example_ids = []
2323
@bisect_server = DRbObject.new_with_uri(drb_uri)
24-
@abort_after_id = nil
24+
@remaining_failures = []
2525
end
2626

2727
def start(_notification)
28-
@abort_after_id = @bisect_server.abort_after_example_id
28+
@remaining_failures = Set.new(@bisect_server.expected_failures)
2929
end
3030

3131
def example_started(notification)
@@ -34,15 +34,15 @@ def example_started(notification)
3434

3535
def example_failed(notification)
3636
@failed_example_ids << notification.example.id
37-
example_finished(notification)
37+
example_finished(notification, :failed)
3838
end
3939

4040
def example_passed(notification)
41-
example_finished(notification)
41+
example_finished(notification, :passed)
4242
end
4343

4444
def example_pending(notification)
45-
example_finished(notification)
45+
example_finished(notification, :pending)
4646
end
4747

4848
def start_dump(_notification)
@@ -55,8 +55,11 @@ def start_dump(_notification)
5555

5656
private
5757

58-
def example_finished(notification)
59-
return unless notification.example.id == @abort_after_id
58+
def example_finished(notification, status)
59+
return unless @remaining_failures.include?(notification.example.id)
60+
@remaining_failures.delete(notification.example.id)
61+
62+
return if status == :failed && !@remaining_failures.empty?
6063
RSpec.world.wants_to_quit = true
6164
end
6265
end

lib/rspec/core/set.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ def initialize(array=[])
1616
merge(array)
1717
end
1818

19+
def empty?
20+
@values.empty?
21+
end
22+
1923
def <<(key)
2024
@values[key] = true
2125
self
2226
end
2327

28+
def delete(key)
29+
@values.delete(key)
30+
end
31+
2432
def each(&block)
2533
@values.keys.each(&block)
2634
self

spec/rspec/core/bisect/runner_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
require 'rspec/core/bisect/runner'
2+
require 'rspec/core/formatters/bisect_formatter'
23

34
module RSpec::Core
45
RSpec.describe Bisect::Runner do
56
let(:server) { instance_double("RSpec::Core::Bisect::Server", :drb_port => 1234) }
67
let(:runner) { described_class.new(server, original_cli_args) }
78

9+
describe "#run" do
10+
let(:original_cli_args) { %w[ spec/1_spec.rb ] }
11+
12+
it "passes the failed examples from the original run as the expected failures so the runs can abort early" do
13+
original_results = Formatters::BisectFormatter::RunResults.new(
14+
[], %w[ spec/failure_spec.rb[1:1] spec/failure_spec.rb[1:2] ]
15+
)
16+
17+
expect(server).to receive(:capture_run_results).
18+
with(no_args).
19+
ordered.
20+
and_return(original_results)
21+
22+
expect(server).to receive(:capture_run_results).
23+
with(original_results.failed_example_ids).
24+
ordered
25+
26+
runner.run(%w[ spec/1_spec.rb[1:1] spec/1_spec.rb[1:2] ])
27+
end
28+
end
29+
830
describe "#command_for" do
931
def command_for(locations, options={})
1032
load_path = options.fetch(:load_path) { [] }

spec/rspec/core/bisect/server_spec.rb

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,24 +67,67 @@ def run_formatter_specs
6767
)
6868
end
6969

70-
it 'can abort the run early (e.g. when it is not interested in later examples)' do
71-
results = server.capture_run_results("./spec/rspec/core/resources/formatter_specs.rb[2:2:1]") do
72-
run_formatter_specs
73-
end
74-
75-
expect(results).to have_attributes(
76-
:all_example_ids => %w[
77-
./spec/rspec/core/resources/formatter_specs.rb[1:1]
78-
./spec/rspec/core/resources/formatter_specs.rb[2:1:1]
79-
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
80-
],
81-
:failed_example_ids => %w[
70+
describe "aborting the run early" do
71+
it "aborts as soon as the last expected failure finishes, since we don't care about what happens after that" do
72+
expected_failures = %w[
8273
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
74+
./spec/rspec/core/resources/formatter_specs.rb[4:1]
8375
]
84-
)
85-
end
8676

87-
# TODO: test aborting after pending vs failed vs passing example if we keep this feature.
77+
results = server.capture_run_results(expected_failures) do
78+
run_formatter_specs
79+
end
80+
81+
expect(results).to have_attributes(
82+
:all_example_ids => %w[
83+
./spec/rspec/core/resources/formatter_specs.rb[1:1]
84+
./spec/rspec/core/resources/formatter_specs.rb[2:1:1]
85+
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
86+
./spec/rspec/core/resources/formatter_specs.rb[3:1]
87+
./spec/rspec/core/resources/formatter_specs.rb[4:1]
88+
],
89+
:failed_example_ids => %w[
90+
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
91+
./spec/rspec/core/resources/formatter_specs.rb[4:1]
92+
]
93+
)
94+
end
95+
96+
it 'aborts after an expected failure passes instead, even when there are remaining failing examples' do
97+
passing_example = "./spec/rspec/core/resources/formatter_specs.rb[3:1]"
98+
later_failing_example = "./spec/rspec/core/resources/formatter_specs.rb[4:1]"
99+
100+
results = server.capture_run_results([passing_example, later_failing_example]) do
101+
run_formatter_specs
102+
end
103+
104+
expect(results).to have_attributes(
105+
:all_example_ids => %w[
106+
./spec/rspec/core/resources/formatter_specs.rb[1:1]
107+
./spec/rspec/core/resources/formatter_specs.rb[2:1:1]
108+
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
109+
./spec/rspec/core/resources/formatter_specs.rb[3:1]
110+
],
111+
:failed_example_ids => %w[
112+
./spec/rspec/core/resources/formatter_specs.rb[2:2:1]
113+
]
114+
)
115+
end
116+
117+
it 'aborts after an expected failure is pending instead, even when there are remaining failing examples' do
118+
pending_example = "./spec/rspec/core/resources/formatter_specs.rb[1:1]"
119+
later_failing_example = "./spec/rspec/core/resources/formatter_specs.rb[4:1]"
120+
121+
results = server.capture_run_results([pending_example, later_failing_example]) do
122+
run_formatter_specs
123+
end
124+
125+
expect(results).to have_attributes(
126+
:all_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[1:1] ],
127+
:failed_example_ids => %w[]
128+
)
129+
end
130+
end
88131
end
89132
end
90133
end

spec/rspec/core/set_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,17 @@
2020
expect(set).to be_an Enumerable
2121
expect { |p| set.each(&p) }.to yield_successive_args(1, 2, 3)
2222
end
23+
24+
it 'supports deletions' do
25+
expect {
26+
set.delete(1)
27+
}.to change { set.include?(1) }.from(true).to(false)
28+
end
29+
30+
it 'indicates if it is empty' do
31+
set = RSpec::Core::Set.new
32+
expect {
33+
set << 1
34+
}.to change { set.empty? }.from(true).to(false)
35+
end
2336
end

0 commit comments

Comments
 (0)