diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 2b81879..6671c8a 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -384,17 +384,17 @@ mod tests { graph.set_output(Double(doubled)); assert_eq!(graph.freeze().unwrap().evaluate(), 168); } + struct Inc(i32); + impl Rule for Inc { + fn visit_inputs(&mut self, _visitor: &mut impl InputVisitor) {} + fn evaluate(&mut self) -> i32 { + self.0 += 1; + return self.0; + } + } #[test] fn invalidatable_rule() { - struct Inc(i32); - impl Rule for Inc { - fn visit_inputs(&mut self, _visitor: &mut impl InputVisitor) {} - fn evaluate(&mut self) -> i32 { - self.0 += 1; - return self.0; - } - } let mut graph = Graph::new(); let mut invalidate = None; let input = graph.add_invalidatable_rule(|inv| { @@ -410,4 +410,78 @@ mod tests { invalidate.as_ref().unwrap().invalidate(); assert_eq!(frozen.evaluate(), 6); } + + struct Add(Input, Input); + impl Rule for Add { + fn visit_inputs(&mut self, visitor: &mut impl InputVisitor) { + visitor.visit(&mut self.0); + visitor.visit(&mut self.1); + } + fn evaluate(&mut self) -> i32 { + self.0.value() + self.1.value() + } + } + + #[test] + fn rule_with_multiple_inputs() { + let mut graph = Graph::new(); + let a = graph.add_value(2); + let b = graph.add_value(3); + graph.set_output(Add(a, b)); + assert_eq!(graph.freeze().unwrap().evaluate(), 5); + } + + #[test] + fn rule_with_invalidatable_inputs() { + let mut graph = Graph::new(); + let mut invalidate = None; + let a = graph.add_invalidatable_rule(|inv| { + invalidate = Some(inv); + Inc(0) + }); + let b = graph.add_rule(Inc(0)); + graph.set_output(Add(a, b)); + let mut frozen = graph.freeze().unwrap(); + assert_eq!(frozen.evaluate(), 2); + invalidate.as_ref().unwrap().invalidate(); + assert_eq!(frozen.evaluate(), 3); + assert_eq!(frozen.evaluate(), 3); + } + + #[test] + fn cant_freeze_no_output() { + let graph = Graph::::new(); + match graph.freeze() { + Err(GraphFreezeError::NoOutput) => (), + Err(e) => assert!(false, "unexpected error {:?}", e), + Ok(_) => assert!(false, "shouldn't have frozen graph"), + } + } + + #[test] + fn cant_freeze_cycle() { + let mut graph = Graph::new(); + struct DeferredInput(Rc>>>); + impl Rule for DeferredInput { + fn visit_inputs(&mut self, visitor: &mut impl InputVisitor) { + let mut borrowed = self.0.borrow_mut(); + let input = borrowed.as_mut().unwrap(); + visitor.visit(input); + } + fn evaluate(&mut self) -> i32 { + 0 + } + } + let a_input = Rc::new(RefCell::new(None)); + let a = graph.add_rule(DeferredInput(Rc::clone(&a_input))); + let b_input = Rc::new(RefCell::new(Some(a))); + let b = graph.add_rule(DeferredInput(b_input)); + *a_input.borrow_mut() = Some(b); + graph.set_output(Inc(0)); + match graph.freeze() { + Err(GraphFreezeError::Cyclic(_)) => (), + Err(e) => assert!(false, "unexpected error {:?}", e), + Ok(_) => assert!(false, "shouldn't have frozen graph"), + } + } }