Skip to content

Commit

Permalink
Merge pull request #178 from ehuss/report-remaining
Browse files Browse the repository at this point in the history
Report an estimate of remaining steps.
  • Loading branch information
oli-obk authored Jul 29, 2022
2 parents 5f22e35 + c6d8759 commit e475d79
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 16 deletions.
82 changes: 67 additions & 15 deletions src/least_satisfying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ use std::fmt;
pub fn least_satisfying<T, P>(slice: &[T], mut predicate: P) -> usize
where
T: fmt::Display + fmt::Debug,
P: FnMut(&T) -> Satisfies,
P: FnMut(&T, usize, usize) -> Satisfies,
{
let mut cache = BTreeMap::new();
let mut predicate = |idx: usize| *cache.entry(idx).or_insert_with(|| predicate(&slice[idx]));
let mut predicate = |idx: usize, rm_no, lm_yes| {
let range = lm_yes - rm_no + 1;
// FIXME: This does not consider unknown_ranges.
let remaining = range / 2;
let estimate = estimate_steps(range);
*cache
.entry(idx)
.or_insert_with(|| predicate(&slice[idx], remaining, estimate))
};
let mut unknown_ranges: Vec<(usize, usize)> = Vec::new();
// presume that the slice starts with a no
// this should be tested before call
Expand Down Expand Up @@ -40,7 +48,7 @@ where
}
}

let r = predicate(next);
let r = predicate(next, rm_no, lm_yes);
match r {
Satisfies::Yes => {
lm_yes = next;
Expand All @@ -52,11 +60,13 @@ where
}
Satisfies::Unknown => {
let mut left = next;
while left > 0 && predicate(left) == Satisfies::Unknown {
while left > 0 && predicate(left, rm_no, lm_yes) == Satisfies::Unknown {
left -= 1;
}
let mut right = next;
while right + 1 < slice.len() && predicate(right) == Satisfies::Unknown {
while right + 1 < slice.len()
&& predicate(right, rm_no, lm_yes) == Satisfies::Unknown
{
right += 1;
}
unknown_ranges.push((left + 1, right - 1));
Expand All @@ -66,10 +76,33 @@ where
}
}

fn estimate_steps(range: usize) -> usize {
// Replace with int_log when it is stabilized.
let log2 = |mut n| {
let mut r = 0;
while n > 1 {
r += 1;
n >>= 1;
}
r
};
if range < 3 {
return 0;
}
let n = log2(range);
let e = 1 << n;
let x = range - e;
if e < 3 * x {
n
} else {
n - 1
}
}

#[cfg(test)]
mod tests {
use super::Satisfies::{No, Unknown, Yes};
use super::{least_satisfying, Satisfies};
use super::{estimate_steps, least_satisfying, Satisfies};
use quickcheck::{QuickCheck, TestResult};

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

let res = least_satisfying(&satisfies_v, |i| *i);
let res = least_satisfying(&satisfies_v, |i, _, _| *i);
let exp = first_yes.unwrap();
TestResult::from_bool(res == exp)
}

#[test]
fn least_satisfying_1() {
assert_eq!(
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i| *i),
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i, _, _| *i),
4
);
}

#[test]
fn least_satisfying_2() {
assert_eq!(
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i| *i),
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i, _, _| *i),
2
);
}

#[test]
fn least_satisfying_3() {
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i| *i), 4);
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i, _, _| *i), 4);
}

#[test]
fn least_satisfying_4() {
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i| *i), 2);
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i, _, _| *i), 2);
}

#[test]
fn least_satisfying_5() {
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i| *i), 1);
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i, _, _| *i), 1);
}

#[test]
fn least_satisfying_6() {
assert_eq!(
least_satisfying(&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes], |i| *i),
least_satisfying(
&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes],
|i, _, _| *i
),
1
);
}

#[test]
fn least_satisfying_7() {
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i| *i), 1);
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i, _, _| *i), 1);
}

#[test]
fn least_satisfying_8() {
assert_eq!(
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i| *i),
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i, _, _| *i),
5
);
}
Expand All @@ -150,6 +186,22 @@ mod tests {
fn qc_prop() {
QuickCheck::new().quickcheck(prop as fn(_) -> _);
}

#[test]
fn estimates() {
for (n, expect) in &[
(0, 0),
(1, 0),
(2, 0),
(3, 1),
(4, 1),
(5, 1),
(6, 2),
(150, 6),
] {
assert_eq!(estimate_steps(*n), *expect);
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand Down
14 changes: 13 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,11 @@ impl Config {
eprintln!("searched toolchains {} through {}", start, end);

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

fn bisect_to_regression(&self, toolchains: &[Toolchain], dl_spec: &DownloadParams) -> usize {
least_satisfying(toolchains, |t| {
least_satisfying(toolchains, |t, remaining, estimate| {
eprintln!(
"{remaining} versions remaining to test after this (roughly {estimate} steps)"
);
self.install_and_test(t, dl_spec)
.unwrap_or(Satisfies::Unknown)
})
Expand Down Expand Up @@ -965,6 +973,7 @@ impl Config {
);
}

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

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

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

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

0 comments on commit e475d79

Please sign in to comment.