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

Commit b909be7

Browse files
Basic recursive bisect formatter
This modifies BisectProgressFormatter to more closely model the recursive bisection algorithm, fixing the associated BisectCoordinator specs.
1 parent 6dc1e1a commit b909be7

File tree

5 files changed

+76
-51
lines changed

5 files changed

+76
-51
lines changed

features/command_line/bisect.feature

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ Feature: Bisect
5454
Bisect started using options: "--seed 1234"
5555
Running suite to find failures... (0.16755 seconds)
5656
Starting bisect with 1 failing example and 9 non-failing examples.
57+
Checking that failures are order-dependent... failure is order-dependent
5758
58-
Round 1: searching for 5 non-failing examples (of 9) to ignore: .. (0.30166 seconds)
59-
Round 2: searching for 3 non-failing examples (of 5) to ignore: .. (0.30306 seconds)
60-
Round 3: searching for 2 non-failing examples (of 3) to ignore: .. (0.33292 seconds)
61-
Round 4: searching for 1 non-failing example (of 2) to ignore: . (0.16476 seconds)
62-
Round 5: searching for 1 non-failing example (of 1) to ignore: . (0.15329 seconds)
59+
Bisecting over non-failing examples 1-9 .. ignoring examples 6-9
60+
Bisecting over non-failing examples 1-5 .. ignoring examples 4-5
61+
Bisecting over non-failing examples 1-3 .. ignoring example 3
62+
Bisecting over non-failing examples 1-2 . ignoring example 1
6363
Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 1.26 seconds.
6464
6565
The minimal reproduction command is:
@@ -75,10 +75,11 @@ Feature: Bisect
7575
Bisect started using options: "--seed 1234"
7676
Running suite to find failures... (0.17102 seconds)
7777
Starting bisect with 1 failing example and 9 non-failing examples.
78+
Checking that failures are order-dependent... failure is order-dependent
7879
79-
Round 1: searching for 5 non-failing examples (of 9) to ignore: .. (0.32943 seconds)
80-
Round 2: searching for 3 non-failing examples (of 5) to ignore: .. (0.3154 seconds)
81-
Round 3: searching for 2 non-failing examples (of 3) to ignore: ..
80+
Bisecting over non-failing examples 1-9 .. ignoring examples 6-9
81+
Bisecting over non-failing examples 1-5 .. ignoring examples 4-5
82+
Bisecting over non-failing examples 1-3 .. ignoring example 3
8283
8384
Bisect aborted!
8485
@@ -106,8 +107,10 @@ Feature: Bisect
106107
- ./spec/calculator_7_spec.rb[1:1]
107108
- ./spec/calculator_8_spec.rb[1:1]
108109
- ./spec/calculator_9_spec.rb[1:1]
110+
Checking that failures are order-dependent..
111+
- Running: rspec ./spec/calculator_1_spec.rb[1:1] --seed 1234 (n.nnnn seconds) failure is order-dependent
109112
110-
Round 1: searching for 5 non-failing examples (of 9) to ignore:
113+
Bisecting over non-failing examples 1-9
111114
- Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_6_spec.rb[1:1] ./spec/calculator_7_spec.rb[1:1] ./spec/calculator_8_spec.rb[1:1] ./spec/calculator_9_spec.rb[1:1] --seed 1234 (0.15302 seconds)
112115
- Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] ./spec/calculator_4_spec.rb[1:1] ./spec/calculator_5_spec.rb[1:1] --seed 1234 (0.19708 seconds)
113116
- Examples we can safely ignore (4):
@@ -121,8 +124,7 @@ Feature: Bisect
121124
- ./spec/calculator_3_spec.rb[1:1]
122125
- ./spec/calculator_4_spec.rb[1:1]
123126
- ./spec/calculator_5_spec.rb[1:1]
124-
- Round finished (0.35172 seconds)
125-
Round 2: searching for 3 non-failing examples (of 5) to ignore:
127+
Bisecting over non-failing examples 1-5
126128
- Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_4_spec.rb[1:1] ./spec/calculator_5_spec.rb[1:1] --seed 1234 (0.15836 seconds)
127129
- Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234 (0.19065 seconds)
128130
- Examples we can safely ignore (2):
@@ -132,26 +134,20 @@ Feature: Bisect
132134
- ./spec/calculator_10_spec.rb[1:1]
133135
- ./spec/calculator_2_spec.rb[1:1]
134136
- ./spec/calculator_3_spec.rb[1:1]
135-
- Round finished (0.35022 seconds)
136-
Round 3: searching for 2 non-failing examples (of 3) to ignore:
137+
Bisecting over non-failing examples 1-3
137138
- Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] --seed 1234 (0.21028 seconds)
138139
- Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234 (0.1975 seconds)
139140
- Examples we can safely ignore (1):
140141
- ./spec/calculator_2_spec.rb[1:1]
141142
- Remaining non-failing examples (2):
142143
- ./spec/calculator_10_spec.rb[1:1]
143144
- ./spec/calculator_3_spec.rb[1:1]
144-
- Round finished (0.40882 seconds)
145-
Round 4: searching for 1 non-failing example (of 2) to ignore:
145+
Bisecting over non-failing examples 1-2
146146
- Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234 (0.17173 seconds)
147147
- Examples we can safely ignore (1):
148148
- ./spec/calculator_3_spec.rb[1:1]
149149
- Remaining non-failing examples (1):
150150
- ./spec/calculator_10_spec.rb[1:1]
151-
- Round finished (0.17234 seconds)
152-
Round 5: searching for 1 non-failing example (of 1) to ignore:
153-
- Running: rspec ./spec/calculator_1_spec.rb[1:1] --seed 1234 (0.18279 seconds)
154-
- Round finished (0.18312 seconds)
155151
Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 1.47 seconds.
156152
157153
The minimal reproduction command is:

features/support/send_sigint_during_bisect.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ module RSpec::Core::Formatters
55
BisectProgressFormatter = Class.new(remove_const :BisectProgressFormatter) do
66
RSpec::Core::Formatters.register self
77

8-
def bisect_round_finished(notification)
9-
return super unless notification.round == 3
8+
def bisect_round_started(notification)
9+
return super unless notification.candidates_range == (1..2)
1010

1111
Process.kill("INT", Process.pid)
1212
# Process.kill is not a synchronous call, so to ensure the output

lib/rspec/core/bisect/example_minimizer.rb

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,37 @@ def find_minimal_repro
2020
bisect_over(non_failing_example_ids)
2121
end
2222

23-
notify(:bisect_complete, :round => 10, :duration => duration,
23+
notify(:bisect_complete, :duration => duration,
2424
:original_non_failing_count => non_failing_example_ids.size,
2525
:remaining_count => remaining_ids.size)
2626

2727
remaining_ids + failed_example_ids
2828
end
2929

3030
def bisect_over(candidate_ids)
31+
notify(:bisect_dependency_check_started)
3132
if get_expected_failures_for?([])
33+
notify(:bisect_dependency_check_failed)
3234
self.eliminated_ids = non_failing_example_ids
3335
return
3436
end
37+
notify(:bisect_dependency_check_passed)
3538

3639
bisect_over_recursive(candidate_ids)
3740
end
3841

3942
def bisect_over_recursive(candidate_ids)
4043
return if candidate_ids.one?
4144

42-
slice_size = (candidate_ids.length / 2.0).ceil
43-
lhs, rhs = candidate_ids.each_slice(slice_size).to_a
44-
4545
notify(
4646
:bisect_round_started,
47-
:round => 10,
48-
:subset_size => slice_size,
49-
:remaining_count => candidate_ids.size
47+
:candidates_range => candidates_range(candidate_ids),
48+
:candidates_count => candidate_ids.size
5049
)
5150

51+
slice_size = (candidate_ids.length / 2.0).ceil
52+
lhs, rhs = candidate_ids.each_slice(slice_size).to_a
53+
5254
ids_to_ignore = [lhs, rhs].find do |ids|
5355
get_expected_failures_for?(remaining_ids - ids)
5456
end
@@ -58,6 +60,7 @@ def bisect_over_recursive(candidate_ids)
5860
notify(
5961
:bisect_ignoring_ids,
6062
:ids_to_ignore => ids_to_ignore,
63+
:ignore_range => candidates_range(ids_to_ignore),
6164
:remaining_ids => remaining_ids
6265
)
6366
bisect_over_recursive(candidate_ids - ids_to_ignore)
@@ -82,6 +85,13 @@ def repro_command_for_currently_needed_ids
8285

8386
private
8487

88+
def candidates_range(ids)
89+
Range.new(
90+
non_failing_example_ids.find_index(ids.first) + 1,
91+
non_failing_example_ids.find_index(ids.last) + 1
92+
)
93+
end
94+
8595
def prep
8696
notify(:bisect_starting, :original_cli_args => runner.original_cli_args)
8797

lib/rspec/core/formatters/bisect_progress_formatter.rb

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ class BisectProgressFormatter < BaseTextFormatter
1010
Formatters.register self, :bisect_starting, :bisect_original_run_complete,
1111
:bisect_round_started, :bisect_individual_run_complete,
1212
:bisect_round_finished, :bisect_complete, :bisect_repro_command,
13-
:bisect_failed, :bisect_aborted
13+
:bisect_failed, :bisect_aborted, :bisect_ignoring_ids,
14+
:bisect_dependency_check_started, :bisect_dependency_check_passed,
15+
:bisect_dependency_check_failed
1416

1517
def bisect_starting(notification)
1618
options = notification.original_cli_args.join(' ')
@@ -26,18 +28,38 @@ def bisect_original_run_complete(notification)
2628
output.puts "Starting bisect with #{failures} and #{non_failures}."
2729
end
2830

31+
def bisect_dependency_check_started(notification)
32+
output.print "Checking that failures are order-dependent.."
33+
end
34+
35+
def bisect_dependency_check_passed(notification)
36+
output.puts " failure is order-dependent"
37+
end
38+
39+
def bisect_dependency_check_failed(notification)
40+
output.puts " failure is not order-dependent!"
41+
end
42+
2943
def bisect_round_started(notification, include_trailing_space=true)
30-
search_desc = Helpers.pluralize(
31-
notification.subset_size, "non-failing example"
32-
)
44+
range_desc = [
45+
notification.candidates_range.begin,
46+
notification.candidates_range.end,
47+
].join("-")
3348

34-
output.print "\nRound #{notification.round}: searching for #{search_desc}" \
35-
" (of #{notification.remaining_count}) to ignore:"
49+
output.print "\nBisecting over non-failing examples #{range_desc}"
3650
output.print " " if include_trailing_space
3751
end
3852

39-
def bisect_round_finished(notification)
40-
output.print " (#{Helpers.format_duration(notification.duration)})"
53+
def bisect_ignoring_ids(notification)
54+
ignore_desc =
55+
if notification.ignore_range.one?
56+
"example #{notification.ignore_range.begin}"
57+
else
58+
"examples #{notification.ignore_range.begin}-" \
59+
"#{notification.ignore_range.end}"
60+
end
61+
62+
output.print " ignoring #{ignore_desc}"
4163
end
4264

4365
def bisect_individual_run_complete(_)

spec/rspec/core/bisect/coordinator_spec.rb

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter)
3434
|Bisect started using options: ""
3535
|Running suite to find failures... (n.nnnn seconds)
3636
|Starting bisect with 2 failing examples and 6 non-failing examples.
37+
|Checking that failures are order-dependent... failure is order-dependent
3738
|
38-
|Round 1: searching for 3 non-failing examples (of 6) to ignore: .. (n.nnnn seconds)
39-
|Round 2: searching for 2 non-failing examples (of 3) to ignore: . (n.nnnn seconds)
40-
|Round 3: searching for 1 non-failing example (of 1) to ignore: . (n.nnnn seconds)
39+
|Bisecting over non-failing examples 1-6 .. ignoring examples 4-6
40+
|Bisecting over non-failing examples 1-3 . ignoring examples 1-2
4141
|Bisect complete! Reduced necessary non-failing examples from 6 to 1 in n.nnnn seconds.
4242
|
4343
|The minimal reproduction command is:
@@ -63,8 +63,10 @@ def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter)
6363
| - 6.rb[1:1]
6464
| - 7.rb[1:1]
6565
| - 8.rb[1:1]
66+
|Checking that failures are order-dependent..
67+
| - Running: rspec 2.rb[1:1] 5.rb[1:1] (n.nnnn seconds) failure is order-dependent
6668
|
67-
|Round 1: searching for 3 non-failing examples (of 6) to ignore:
69+
|Bisecting over non-failing examples 1-6
6870
| - Running: rspec 2.rb[1:1] 5.rb[1:1] 6.rb[1:1] 7.rb[1:1] 8.rb[1:1] (n.nnnn seconds)
6971
| - Running: rspec 1.rb[1:1] 2.rb[1:1] 3.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds)
7072
| - Examples we can safely ignore (3):
@@ -75,18 +77,13 @@ def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter)
7577
| - 1.rb[1:1]
7678
| - 3.rb[1:1]
7779
| - 4.rb[1:1]
78-
| - Round finished (n.nnnn seconds)
79-
|Round 2: searching for 2 non-failing examples (of 3) to ignore:
80+
|Bisecting over non-failing examples 1-3
8081
| - Running: rspec 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds)
8182
| - Examples we can safely ignore (2):
8283
| - 1.rb[1:1]
8384
| - 3.rb[1:1]
8485
| - Remaining non-failing examples (1):
8586
| - 4.rb[1:1]
86-
| - Round finished (n.nnnn seconds)
87-
|Round 3: searching for 1 non-failing example (of 1) to ignore:
88-
| - Running: rspec 2.rb[1:1] 5.rb[1:1] (n.nnnn seconds)
89-
| - Round finished (n.nnnn seconds)
9087
|Bisect complete! Reduced necessary non-failing examples from 6 to 1 in n.nnnn seconds.
9188
|
9289
|The minimal reproduction command is:
@@ -99,8 +96,8 @@ def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter)
9996
Class.new(Formatters::BisectProgressFormatter) do
10097
Formatters.register self
10198

102-
def bisect_round_finished(notification)
103-
return super unless notification.round == 2
99+
def bisect_round_started(notification)
100+
return super unless notification.candidates_range == (1..3)
104101

105102
Process.kill("INT", Process.pid)
106103
# Process.kill is not a synchronous call, so to ensure the output
@@ -128,14 +125,14 @@ def bisect_round_finished(notification)
128125
|Bisect started using options: ""
129126
|Running suite to find failures... (n.nnnn seconds)
130127
|Starting bisect with 2 failing examples and 6 non-failing examples.
128+
|Checking that failures are order-dependent... failure is order-dependent
131129
|
132-
|Round 1: searching for 3 non-failing examples (of 6) to ignore: .. (n.nnnn seconds)
133-
|Round 2: searching for 2 non-failing examples (of 3) to ignore: .
130+
|Bisecting over non-failing examples 1-6 .. ignoring examples 4-6
134131
|
135132
|Bisect aborted!
136133
|
137134
|The most minimal reproduction command discovered so far is:
138-
| rspec 2.rb[1:1] 4.rb[1:1] 5.rb[1:1]
135+
| rspec 1.rb[1:1] 2.rb[1:1] 3.rb[1:1] 4.rb[1:1] 5.rb[1:1]
139136
EOS
140137
end
141138
end

0 commit comments

Comments
 (0)