Skip to content

Commit e475d79

Browse files
authored
Merge pull request #178 from ehuss/report-remaining
Report an estimate of remaining steps.
2 parents 5f22e35 + c6d8759 commit e475d79

File tree

2 files changed

+80
-16
lines changed

2 files changed

+80
-16
lines changed

Diff for: src/least_satisfying.rs

+67-15
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@ use std::fmt;
44
pub fn least_satisfying<T, P>(slice: &[T], mut predicate: P) -> usize
55
where
66
T: fmt::Display + fmt::Debug,
7-
P: FnMut(&T) -> Satisfies,
7+
P: FnMut(&T, usize, usize) -> Satisfies,
88
{
99
let mut cache = BTreeMap::new();
10-
let mut predicate = |idx: usize| *cache.entry(idx).or_insert_with(|| predicate(&slice[idx]));
10+
let mut predicate = |idx: usize, rm_no, lm_yes| {
11+
let range = lm_yes - rm_no + 1;
12+
// FIXME: This does not consider unknown_ranges.
13+
let remaining = range / 2;
14+
let estimate = estimate_steps(range);
15+
*cache
16+
.entry(idx)
17+
.or_insert_with(|| predicate(&slice[idx], remaining, estimate))
18+
};
1119
let mut unknown_ranges: Vec<(usize, usize)> = Vec::new();
1220
// presume that the slice starts with a no
1321
// this should be tested before call
@@ -40,7 +48,7 @@ where
4048
}
4149
}
4250

43-
let r = predicate(next);
51+
let r = predicate(next, rm_no, lm_yes);
4452
match r {
4553
Satisfies::Yes => {
4654
lm_yes = next;
@@ -52,11 +60,13 @@ where
5260
}
5361
Satisfies::Unknown => {
5462
let mut left = next;
55-
while left > 0 && predicate(left) == Satisfies::Unknown {
63+
while left > 0 && predicate(left, rm_no, lm_yes) == Satisfies::Unknown {
5664
left -= 1;
5765
}
5866
let mut right = next;
59-
while right + 1 < slice.len() && predicate(right) == Satisfies::Unknown {
67+
while right + 1 < slice.len()
68+
&& predicate(right, rm_no, lm_yes) == Satisfies::Unknown
69+
{
6070
right += 1;
6171
}
6272
unknown_ranges.push((left + 1, right - 1));
@@ -66,10 +76,33 @@ where
6676
}
6777
}
6878

79+
fn estimate_steps(range: usize) -> usize {
80+
// Replace with int_log when it is stabilized.
81+
let log2 = |mut n| {
82+
let mut r = 0;
83+
while n > 1 {
84+
r += 1;
85+
n >>= 1;
86+
}
87+
r
88+
};
89+
if range < 3 {
90+
return 0;
91+
}
92+
let n = log2(range);
93+
let e = 1 << n;
94+
let x = range - e;
95+
if e < 3 * x {
96+
n
97+
} else {
98+
n - 1
99+
}
100+
}
101+
69102
#[cfg(test)]
70103
mod tests {
71104
use super::Satisfies::{No, Unknown, Yes};
72-
use super::{least_satisfying, Satisfies};
105+
use super::{estimate_steps, least_satisfying, Satisfies};
73106
use quickcheck::{QuickCheck, TestResult};
74107

75108
fn prop(xs: Vec<Option<bool>>) -> TestResult {
@@ -89,59 +122,62 @@ mod tests {
89122
}
90123
}
91124

92-
let res = least_satisfying(&satisfies_v, |i| *i);
125+
let res = least_satisfying(&satisfies_v, |i, _, _| *i);
93126
let exp = first_yes.unwrap();
94127
TestResult::from_bool(res == exp)
95128
}
96129

97130
#[test]
98131
fn least_satisfying_1() {
99132
assert_eq!(
100-
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i| *i),
133+
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i, _, _| *i),
101134
4
102135
);
103136
}
104137

105138
#[test]
106139
fn least_satisfying_2() {
107140
assert_eq!(
108-
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i| *i),
141+
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i, _, _| *i),
109142
2
110143
);
111144
}
112145

113146
#[test]
114147
fn least_satisfying_3() {
115-
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i| *i), 4);
148+
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i, _, _| *i), 4);
116149
}
117150

118151
#[test]
119152
fn least_satisfying_4() {
120-
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i| *i), 2);
153+
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i, _, _| *i), 2);
121154
}
122155

123156
#[test]
124157
fn least_satisfying_5() {
125-
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i| *i), 1);
158+
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i, _, _| *i), 1);
126159
}
127160

128161
#[test]
129162
fn least_satisfying_6() {
130163
assert_eq!(
131-
least_satisfying(&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes], |i| *i),
164+
least_satisfying(
165+
&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes],
166+
|i, _, _| *i
167+
),
132168
1
133169
);
134170
}
135171

136172
#[test]
137173
fn least_satisfying_7() {
138-
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i| *i), 1);
174+
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i, _, _| *i), 1);
139175
}
140176

141177
#[test]
142178
fn least_satisfying_8() {
143179
assert_eq!(
144-
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i| *i),
180+
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i, _, _| *i),
145181
5
146182
);
147183
}
@@ -150,6 +186,22 @@ mod tests {
150186
fn qc_prop() {
151187
QuickCheck::new().quickcheck(prop as fn(_) -> _);
152188
}
189+
190+
#[test]
191+
fn estimates() {
192+
for (n, expect) in &[
193+
(0, 0),
194+
(1, 0),
195+
(2, 0),
196+
(3, 1),
197+
(4, 1),
198+
(5, 1),
199+
(6, 2),
200+
(150, 6),
201+
] {
202+
assert_eq!(estimate_steps(*n), *expect);
203+
}
204+
}
153205
}
154206

155207
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

Diff for: src/main.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,11 @@ impl Config {
677677
eprintln!("searched toolchains {} through {}", start, end);
678678

679679
if toolchains[*found] == *toolchains.last().unwrap() {
680+
// FIXME: Ideally the BisectionResult would contain the final result.
681+
// This ends up testing a toolchain that was already tested.
682+
// I believe this is one of the duplicates mentioned in
683+
// https://github.com/rust-lang/cargo-bisect-rustc/issues/85
684+
eprintln!("checking last toolchain to determine final result");
680685
let t = &toolchains[*found];
681686
let r = match t.install(&self.client, dl_spec) {
682687
Ok(()) => {
@@ -879,7 +884,10 @@ impl Config {
879884
}
880885

881886
fn bisect_to_regression(&self, toolchains: &[Toolchain], dl_spec: &DownloadParams) -> usize {
882-
least_satisfying(toolchains, |t| {
887+
least_satisfying(toolchains, |t, remaining, estimate| {
888+
eprintln!(
889+
"{remaining} versions remaining to test after this (roughly {estimate} steps)"
890+
);
883891
self.install_and_test(t, dl_spec)
884892
.unwrap_or(Satisfies::Unknown)
885893
})
@@ -965,6 +973,7 @@ impl Config {
965973
);
966974
}
967975

976+
eprintln!("checking the start range to find a passing nightly");
968977
match self.install_and_test(&t, &dl_spec) {
969978
Ok(r) => {
970979
// If Satisfies::No, then the regression was not identified in this nightly.
@@ -1011,6 +1020,7 @@ impl Config {
10111020
t_end.std_targets.sort();
10121021
t_end.std_targets.dedup();
10131022

1023+
eprintln!("checking the end range to verify it does not pass");
10141024
let result_nightly = self.install_and_test(&t_end, &dl_spec)?;
10151025
// The regression was not identified in this nightly.
10161026
if result_nightly == Satisfies::No {
@@ -1157,6 +1167,7 @@ impl Config {
11571167

11581168
if !toolchains.is_empty() {
11591169
// validate commit at start of range
1170+
eprintln!("checking the start range to verify it passes");
11601171
let start_range_result = self.install_and_test(&toolchains[0], &dl_spec)?;
11611172
if start_range_result == Satisfies::Yes {
11621173
bail!(
@@ -1166,6 +1177,7 @@ impl Config {
11661177
}
11671178

11681179
// validate commit at end of range
1180+
eprintln!("checking the end range to verify it does not pass");
11691181
let end_range_result =
11701182
self.install_and_test(&toolchains[toolchains.len() - 1], &dl_spec)?;
11711183
if end_range_result == Satisfies::No {

0 commit comments

Comments
 (0)