Diffstat (limited to 'tests/protocol_visitor_value.rs')
-rw-r--r--tests/protocol_visitor_value.rs270
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 })
+ );
+ }
+}