blob: e20466572a26f43d1c4785954022e6fda610b0f4 [file] [log] [blame]
use super::*;
use std::fmt;
use std::marker::PhantomData;
impl<'a> super::ForestObligation for &'a str {
type Predicate = &'a str;
fn as_predicate(&self) -> &Self::Predicate {
self
}
}
struct ClosureObligationProcessor<OF, BF, O, E> {
process_obligation: OF,
_process_backedge: BF,
marker: PhantomData<(O, E)>,
}
#[allow(non_snake_case)]
fn C<OF, BF, O>(of: OF, bf: BF) -> ClosureObligationProcessor<OF, BF, O, &'static str>
where OF: FnMut(&mut O) -> ProcessResult<O, &'static str>,
BF: FnMut(&[O])
{
ClosureObligationProcessor {
process_obligation: of,
_process_backedge: bf,
marker: PhantomData
}
}
impl<OF, BF, O, E> ObligationProcessor for ClosureObligationProcessor<OF, BF, O, E>
where O: super::ForestObligation + fmt::Debug,
E: fmt::Debug,
OF: FnMut(&mut O) -> ProcessResult<O, E>,
BF: FnMut(&[O])
{
type Obligation = O;
type Error = E;
fn process_obligation(&mut self,
obligation: &mut Self::Obligation)
-> ProcessResult<Self::Obligation, Self::Error>
{
(self.process_obligation)(obligation)
}
fn process_backedge<'c, I>(&mut self, _cycle: I,
_marker: PhantomData<&'c Self::Obligation>)
where I: Clone + Iterator<Item=&'c Self::Obligation>
{
}
}
#[test]
fn push_pop() {
let mut forest = ObligationForest::new();
forest.register_obligation("A");
forest.register_obligation("B");
forest.register_obligation("C");
// first round, B errors out, A has subtasks, and C completes, creating this:
// A |-> A.1
// |-> A.2
// |-> A.3
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
"B" => ProcessResult::Error("B is for broken"),
"C" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["C"]);
assert_eq!(err,
vec![Error {
error: "B is for broken",
backtrace: vec!["B"],
}]);
// second round: two delays, one success, creating an uneven set of subtasks:
// A |-> A.1
// |-> A.2
// |-> A.3 |-> A.3.i
// D |-> D.1
// |-> D.2
forest.register_obligation("D");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.1" => ProcessResult::Unchanged,
"A.2" => ProcessResult::Unchanged,
"A.3" => ProcessResult::Changed(vec!["A.3.i"]),
"D" => ProcessResult::Changed(vec!["D.1", "D.2"]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), Vec::<&'static str>::new());
assert_eq!(err, Vec::new());
// third round: ok in A.1 but trigger an error in A.2. Check that it
// propagates to A, but not D.1 or D.2.
// D |-> D.1 |-> D.1.i
// |-> D.2 |-> D.2.i
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Error("A is for apple"),
"A.3.i" => ProcessResult::Changed(vec![]),
"D.1" => ProcessResult::Changed(vec!["D.1.i"]),
"D.2" => ProcessResult::Changed(vec!["D.2.i"]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["A.3", "A.1", "A.3.i"]);
assert_eq!(err,
vec![Error {
error: "A is for apple",
backtrace: vec!["A.2", "A"],
}]);
// fourth round: error in D.1.i
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D.1.i" => ProcessResult::Error("D is for dumb"),
"D.2.i" => ProcessResult::Changed(vec![]),
_ => panic!("unexpected obligation {:?}", obligation),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["D.2.i", "D.2"]);
assert_eq!(err,
vec![Error {
error: "D is for dumb",
backtrace: vec!["D.1.i", "D.1", "D"],
}]);
}
// Test that if a tree with grandchildren succeeds, everything is
// reported as expected:
// A
// A.1
// A.2
// A.2.i
// A.2.ii
// A.3
#[test]
fn success_in_grandchildren() {
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert!(ok.unwrap().is_empty());
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.1" => ProcessResult::Changed(vec![]),
"A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]),
"A.3" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["A.3", "A.1"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
"A.2.ii" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.2.i.a" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_| {}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["A.2.i.a", "A.2.i", "A.2", "A"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}),
DoCompleted::Yes);
assert!(ok.unwrap().is_empty());
assert!(err.is_empty());
}
#[test]
fn to_errors_no_throw() {
// check that converting multiple children with common parent (A)
// yields to correct errors (and does not panic, in particular).
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let errors = forest.to_errors(());
assert_eq!(errors[0].backtrace, vec!["A.1", "A"]);
assert_eq!(errors[1].backtrace, vec!["A.2", "A"]);
assert_eq!(errors[2].backtrace, vec!["A.3", "A"]);
assert_eq!(errors.len(), 3);
}
#[test]
fn diamond() {
// check that diamond dependencies are handled correctly
let mut forest = ObligationForest::new();
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A.1" => ProcessResult::Changed(vec!["D"]),
"A.2" => ProcessResult::Changed(vec!["D"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D" => { d_count += 1; ProcessResult::Changed(vec![]) },
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(d_count, 1);
assert_eq!(ok.unwrap(), vec!["D", "A.2", "A.1", "A"]);
assert_eq!(err.len(), 0);
let errors = forest.to_errors(());
assert_eq!(errors.len(), 0);
forest.register_obligation("A'");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
"A'.2" => ProcessResult::Changed(vec!["D'"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let mut d_count = 0;
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D'" => { d_count += 1; ProcessResult::Error("operation failed") },
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(d_count, 1);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error {
error: "operation failed",
backtrace: vec!["D'", "A'.1", "A'"]
}]);
let errors = forest.to_errors(());
assert_eq!(errors.len(), 0);
}
#[test]
fn done_dependency() {
// check that the local cache works
let mut forest = ObligationForest::new();
forest.register_obligation("A: Sized");
forest.register_obligation("B: Sized");
forest.register_obligation("C: Sized");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["C: Sized", "B: Sized", "A: Sized"]);
assert_eq!(err.len(), 0);
forest.register_obligation("(A,B,C): Sized");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"(A,B,C): Sized" => ProcessResult::Changed(vec![
"A: Sized",
"B: Sized",
"C: Sized"
]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]);
assert_eq!(err.len(), 0);
}
#[test]
fn orphan() {
// check that orphaned nodes are handled correctly
let mut forest = ObligationForest::new();
forest.register_obligation("A");
forest.register_obligation("B");
forest.register_obligation("C1");
forest.register_obligation("C2");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Changed(vec!["D", "E"]),
"B" => ProcessResult::Unchanged,
"C1" => ProcessResult::Changed(vec![]),
"C2" => ProcessResult::Changed(vec![]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap(), vec!["C2", "C1"]);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D" | "E" => ProcessResult::Unchanged,
"B" => ProcessResult::Changed(vec!["D"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err.len(), 0);
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D" => ProcessResult::Unchanged,
"E" => ProcessResult::Error("E is for error"),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error {
error: "E is for error",
backtrace: vec!["E", "A"]
}]);
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"D" => ProcessResult::Error("D is dead"),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error {
error: "D is dead",
backtrace: vec!["D"]
}]);
let errors = forest.to_errors(());
assert_eq!(errors.len(), 0);
}
#[test]
fn simultaneous_register_and_error() {
// check that registering a failed obligation works correctly
let mut forest = ObligationForest::new();
forest.register_obligation("A");
forest.register_obligation("B");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error {
error: "An error",
backtrace: vec!["A"]
}]);
let mut forest = ObligationForest::new();
forest.register_obligation("B");
forest.register_obligation("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations(&mut C(|obligation| {
match *obligation {
"A" => ProcessResult::Error("An error"),
"B" => ProcessResult::Changed(vec!["A"]),
_ => unreachable!(),
}
}, |_|{}), DoCompleted::Yes);
assert_eq!(ok.unwrap().len(), 0);
assert_eq!(err, vec![super::Error {
error: "An error",
backtrace: vec!["A"]
}]);
}