1
- RSpec ::Support . require_rspec_core "bisect/subset_enumerator"
2
-
3
1
module RSpec
4
2
module Core
5
3
module Bisect
@@ -18,20 +16,65 @@ def initialize(runner, reporter)
18
16
def find_minimal_repro
19
17
prep
20
18
21
- self . remaining_ids = non_failing_example_ids
19
+ _ , duration = track_duration do
20
+ bisect ( non_failing_example_ids )
21
+ end
22
+
23
+ notify ( :bisect_complete , :duration => duration ,
24
+ :original_non_failing_count => non_failing_example_ids . size ,
25
+ :remaining_count => remaining_ids . size )
26
+
27
+ remaining_ids + failed_example_ids
28
+ end
29
+
30
+ def bisect ( candidate_ids )
31
+ notify ( :bisect_dependency_check_started )
32
+ if get_expected_failures_for? ( [ ] )
33
+ notify ( :bisect_dependency_check_failed )
34
+ self . remaining_ids = [ ]
35
+ return
36
+ end
37
+ notify ( :bisect_dependency_check_passed )
38
+
39
+ bisect_over ( candidate_ids )
40
+ end
41
+
42
+ def bisect_over ( candidate_ids )
43
+ return if candidate_ids . one?
44
+
45
+ notify (
46
+ :bisect_round_started ,
47
+ :candidate_range => example_range ( candidate_ids ) ,
48
+ :candidates_count => candidate_ids . size
49
+ )
22
50
23
- each_bisect_round do |subsets |
24
- ids_to_ignore = subsets . find do |ids |
51
+ slice_size = ( candidate_ids . length / 2.0 ) . ceil
52
+ lhs , rhs = candidate_ids . each_slice ( slice_size ) . to_a
53
+
54
+ ids_to_ignore , duration = track_duration do
55
+ [ lhs , rhs ] . find do |ids |
25
56
get_expected_failures_for? ( remaining_ids - ids )
26
57
end
58
+ end
27
59
28
- next :done unless ids_to_ignore
29
-
60
+ if ids_to_ignore
30
61
self . remaining_ids -= ids_to_ignore
31
- notify ( :bisect_ignoring_ids , :ids_to_ignore => ids_to_ignore , :remaining_ids => remaining_ids )
62
+ notify (
63
+ :bisect_round_ignoring_ids ,
64
+ :ids_to_ignore => ids_to_ignore ,
65
+ :ignore_range => example_range ( ids_to_ignore ) ,
66
+ :remaining_ids => remaining_ids ,
67
+ :duration => duration
68
+ )
69
+ bisect_over ( candidate_ids - ids_to_ignore )
70
+ else
71
+ notify (
72
+ :bisect_round_detected_multiple_culprits ,
73
+ :duration => duration
74
+ )
75
+ bisect_over ( lhs )
76
+ bisect_over ( rhs )
32
77
end
33
-
34
- currently_needed_ids
35
78
end
36
79
37
80
def currently_needed_ids
@@ -43,15 +86,35 @@ def repro_command_for_currently_needed_ids
43
86
"(Not yet enough information to provide any repro command)"
44
87
end
45
88
89
+ # @private
90
+ # Convenience class for describing a subset of the candidate examples
91
+ ExampleRange = Struct . new ( :start , :finish ) do
92
+ def description
93
+ if start == finish
94
+ "example #{ start } "
95
+ else
96
+ "examples #{ start } -#{ finish } "
97
+ end
98
+ end
99
+ end
100
+
46
101
private
47
102
103
+ def example_range ( ids )
104
+ ExampleRange . new (
105
+ non_failing_example_ids . find_index ( ids . first ) + 1 ,
106
+ non_failing_example_ids . find_index ( ids . last ) + 1
107
+ )
108
+ end
109
+
48
110
def prep
49
111
notify ( :bisect_starting , :original_cli_args => runner . original_cli_args )
50
112
51
113
_ , duration = track_duration do
52
114
original_results = runner . original_results
53
115
@all_example_ids = original_results . all_example_ids
54
116
@failed_example_ids = original_results . failed_example_ids
117
+ @remaining_ids = non_failing_example_ids
55
118
end
56
119
57
120
if @failed_example_ids . empty?
@@ -70,7 +133,11 @@ def non_failing_example_ids
70
133
71
134
def get_expected_failures_for? ( ids )
72
135
ids_to_run = ids + failed_example_ids
73
- notify ( :bisect_individual_run_start , :command => runner . repro_command_from ( ids_to_run ) )
136
+ notify (
137
+ :bisect_individual_run_start ,
138
+ :command => runner . repro_command_from ( ids_to_run ) ,
139
+ :ids_to_run => ids_to_run
140
+ )
74
141
75
142
results , duration = track_duration { runner . run ( ids_to_run ) }
76
143
notify ( :bisect_individual_run_complete , :duration => duration , :results => results )
@@ -79,34 +146,6 @@ def get_expected_failures_for?(ids)
79
146
( failed_example_ids & results . failed_example_ids ) == failed_example_ids
80
147
end
81
148
82
- INFINITY = ( 1.0 / 0 ) # 1.8.7 doesn't define Float::INFINITY so we define our own...
83
-
84
- def each_bisect_round ( &block )
85
- last_round , duration = track_duration do
86
- 1 . upto ( INFINITY ) do |round |
87
- break if :done == bisect_round ( round , &block )
88
- end
89
- end
90
-
91
- notify ( :bisect_complete , :round => last_round , :duration => duration ,
92
- :original_non_failing_count => non_failing_example_ids . size ,
93
- :remaining_count => remaining_ids . size )
94
- end
95
-
96
- def bisect_round ( round )
97
- value , duration = track_duration do
98
- subsets = SubsetEnumerator . new ( remaining_ids )
99
- notify ( :bisect_round_started , :round => round ,
100
- :subset_size => subsets . subset_size ,
101
- :remaining_count => remaining_ids . size )
102
-
103
- yield subsets
104
- end
105
-
106
- notify ( :bisect_round_finished , :duration => duration , :round => round )
107
- value
108
- end
109
-
110
149
def track_duration
111
150
start = ::RSpec ::Core ::Time . now
112
151
[ yield , ::RSpec ::Core ::Time . now - start ]
0 commit comments