Diffstat (limited to 'tests/protocol_visitor_value.rs')
| -rw-r--r-- | tests/protocol_visitor_value.rs | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/tests/protocol_visitor_value.rs b/tests/protocol_visitor_value.rs index 8b13789..ff17fc4 100644 --- a/tests/protocol_visitor_value.rs +++ b/tests/protocol_visitor_value.rs @@ -1 +1,271 @@ +use std::any::TypeId; +use common::protocol::{ + hint::{KnownFactory, MockHintVisitor}, + visitor::MockValueVisitor, +}; +use mockall::predicate::eq; +use treaty::{ + any::{ + BorrowedStatic, BorrowedStaticHrt, OwnedStatic, TempBorrowedMutStatic, + TempBorrowedMutStaticHrt, TypeNameId, + }, + effect::Blocking, + protocol::{ + visitor::{Value, ValueKnown, ValueProto, VisitResult}, + walker::hint::Hint, + }, + Flow, +}; + +mod common; + +/// Tests support for custom type support in the value protocol. +/// +/// This is a core feature of treaty so it better work. +#[test] +fn custom_value_type() { + // The value we want to visit in the value visitor. + #[derive(PartialEq, Debug, Clone)] + struct MyValue; + + let mut mock = MockValueVisitor::<OwnedStatic<MyValue>, Blocking>::new(); + + // Expect the visit method to be called once with the custom type. + mock.expect_visit() + .once() + .with(eq(OwnedStatic(MyValue))) + .return_const(VisitResult::Control(Flow::Done)); + + // Cast to a trait object for the value protocol. + // This shows the visit method is going through the trait. + let visitor: &mut dyn Value<OwnedStatic<MyValue>, Blocking> = &mut mock; + + // Visit the value. + let result = visitor.visit(OwnedStatic(MyValue)).into_innter(); + + // The mock returns that it is done. + assert_eq!(result, VisitResult::Control(Flow::Done)); +} + +/// Tests that a value with a lifetime can be given to the value protocol. +/// +/// This allows for treaty's zero copy capabilities. +/// Also this is what allows treaty to short circuit things like structs and enums if the visitor +/// supports it. +#[test] +fn borrowed_value() { + // A value with a lifetime longer than the visitor. + let context = String::from("test"); + + // Scope that doesn't live as long as the context. + { + // We borrow the context, this is what we pass to the visitor. + let value = &context; + + let mut mock = MockValueVisitor::<BorrowedStaticHrt<String>, Blocking>::new(); + + // Expect the visit method to be called once with the borrowed value. + mock.expect_visit() + .once() + .withf(|BorrowedStatic(value)| *value == "test") + .return_const(VisitResult::Control(Flow::Done)); + + // Cast to a trait object for the value protocol. + let visitor: &mut dyn Value<BorrowedStaticHrt<String>, Blocking> = &mut mock; + + // Visit the borrowed value. + visitor.visit(BorrowedStatic(value)); + } + + // Make sure the compiler doesn't do anything funny with the lifetime. + assert_eq!(context, "test"); +} + +/// Tests that a value with a temp lifetime can be given to the value protocol. +/// +/// This is useful for passing borrows of things inside a walker to a visitor. +/// The visitor can't keep the borrow as the temp lifetime is shorter than the context. +/// +/// If you replaced the [`TempBorrowedMutStaticHrt`] with [`BorrowedMutStaticHrt`] +/// this would fail to compile. +#[test] +fn temp_borrowed_value() { + // A value with a lifetime longer than the visitor. + let mut context = String::from("test"); + + // Scope that doesn't live as long as the context. + { + // We borrow the context, this is what we pass to the visitor. + let value = &mut context; + + let mut mock = MockValueVisitor::<TempBorrowedMutStaticHrt<String>, Blocking>::new(); + + // Expect the visit method to be called once with the borrowed value. + mock.expect_visit() + .times(2) + .withf(|TempBorrowedMutStatic(value)| *value == "test") + .return_const(VisitResult::Control(Flow::Done)); + + // Cast to a trait object for the value protocol. + // We definitly need this for this test so the lifetime is invariant. + let visitor: &mut dyn Value<TempBorrowedMutStaticHrt<String>, Blocking> = &mut mock; + + // Visit the context to show we can shorten the lifetime. + // This would also force the lifetime to be to long if this wasn't the Temp form. + visitor.visit(TempBorrowedMutStatic(value)); + + // Temporary scope (smaller than the context we set above). + { + // A temporary value. + let mut value = String::from("test"); + + // Visit the temp value. + visitor.visit(TempBorrowedMutStatic(&mut value)); + } + + // Force the visitor to outlive the temporary scope. + let _ = visitor; + } + + // Make sure the compiler doesn't do anything funny with the lifetime. + assert_eq!(context, "test"); +} + +/// Tests for the control flow returns the value protocol visit can return. +#[test] +fn all_visit_results() { + let mut mock = MockValueVisitor::<OwnedStatic<i32>, Blocking>::new(); + + mock.expect_visit() + .once() + .with(eq(OwnedStatic(0))) + .return_const(VisitResult::Control(Flow::Done)); + + mock.expect_visit() + .once() + .with(eq(OwnedStatic(1))) + .return_const(VisitResult::Control(Flow::Err)); + + mock.expect_visit() + .once() + .with(eq(OwnedStatic(2))) + .return_const(VisitResult::Control(Flow::Continue)); + + mock.expect_visit() + .once() + .with(eq(OwnedStatic(3))) + .return_const(VisitResult::Skipped(())); + + let visitor: &mut dyn Value<OwnedStatic<i32>, Blocking> = &mut mock; + + // Visit can return a done. + assert_eq!( + visitor.visit(OwnedStatic(0)).into_innter(), + VisitResult::Control(Flow::Done) + ); + + // Visit can return an error signal. + assert_eq!( + visitor.visit(OwnedStatic(1)).into_innter(), + VisitResult::Control(Flow::Err) + ); + + // Visit can return a continue. + assert_eq!( + visitor.visit(OwnedStatic(2)).into_innter(), + VisitResult::Control(Flow::Continue) + ); + + // A visit can be skipped. + // This is for runtime visit support checking. + // The value should be given back, but that's not forced. + assert_eq!( + visitor.visit(OwnedStatic(3)).into_innter(), + VisitResult::Skipped(OwnedStatic(3)) + ); +} + +/// Tests that the higher ranked name for the value protocol exists. +#[test] +fn value_proto() { + // The type id of the higher ranked type. + let id = TypeId::of::<ValueProto<OwnedStatic<i32>, Blocking>>(); + + // The type id for the lifetime containing value protocol trait object. + let name_id = TypeNameId::of_lower::<dyn Value<OwnedStatic<i32>, Blocking> + Send + Sync>(); + + // They should be the same. + assert_eq!(id, name_id.into_type_id()); +} + +/// Tests that the value protocol can be given as a hint to a walker. +/// +/// The value protocol allows a hint of a preview of the value. +#[test] +fn as_hint() { + { + let mut mock = MockHintVisitor::<ValueProto<OwnedStatic<i32>, Blocking>, Blocking>::new(); + + mock.expect_known().once().return_const( + (|_, _hint| { + Ok(ValueKnown { + preview: Some(&OwnedStatic(42)), + }) + }) as KnownFactory<ValueProto<OwnedStatic<i32>, Blocking>>, + ); + + let walker: &mut dyn Hint<ValueProto<OwnedStatic<i32>, Blocking>, Blocking> = &mut mock; + + // The value protocol has no hint data, and it has no known data. + assert_eq!( + walker.known(&()).into_innter(), + Ok(ValueKnown { + preview: Some(&OwnedStatic(42)) + }) + ); + } + + { + let mut mock = + MockHintVisitor::<ValueProto<BorrowedStaticHrt<i32>, Blocking>, Blocking>::new(); + + mock.expect_known().once().return_const( + (|_, _hint| { + Ok(ValueKnown { + preview: Some(&BorrowedStatic(&42)), + }) + }) as KnownFactory<ValueProto<BorrowedStaticHrt<i32>, Blocking>>, + ); + + let walker: &mut dyn Hint<ValueProto<BorrowedStaticHrt<i32>, Blocking>, Blocking> = + &mut mock; + + // The value protocol has no hint data, and it has no known data. + assert_eq!( + walker.known(&()).into_innter(), + Ok(ValueKnown { + preview: Some(&BorrowedStatic(&42)) + }) + ); + } + + { + let mut mock = + MockHintVisitor::<ValueProto<TempBorrowedMutStaticHrt<i32>, Blocking>, Blocking>::new(); + + mock.expect_known().once().return_const( + (|_, _hint| Ok(ValueKnown { preview: None })) + as KnownFactory<ValueProto<TempBorrowedMutStaticHrt<i32>, Blocking>>, + ); + + let walker: &mut dyn Hint<ValueProto<TempBorrowedMutStaticHrt<i32>, Blocking>, Blocking> = + &mut mock; + + // The value protocol has no hint data, and it has no known data. + assert_eq!( + walker.known(&()).into_innter(), + Ok(ValueKnown { preview: None }) + ); + } +} |