use std::any::TypeId;
use common::protocol::{
hint::{KnownFactory, MockHintWalker},
value::MockValueVisitor,
};
use mockall::predicate::eq;
use treaty::{
any::{
AnyTrait, BorrowedStatic, BorrowedStaticHrt, OwnedStatic, TempBorrowedMutStatic,
TempBorrowedMutStaticHrt, TypeNameId,
},
effect::blocking::Blocking,
protocol::{
visitor::{visit_value, Value, ValueKnown, ValueProto, VisitResult},
walker::hint::Hint,
DynVisitor,
},
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()
.times(2)
.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)).value();
// The mock returns that it is done.
assert_eq!(result, VisitResult::Control(Flow::Done));
let visitor: &mut (dyn AnyTrait + Send + Sync) = &mut mock;
assert_eq!(
visit_value::<_, Blocking>(DynVisitor(visitor), OwnedStatic(MyValue)).value(),
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()
.times(2)
.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.
assert_eq!(
visitor.visit(BorrowedStatic(value)).value(),
Flow::Done.into()
);
let visitor: &mut (dyn AnyTrait + Send + Sync) = &mut mock;
assert_eq!(
visit_value::<_, Blocking>(DynVisitor(visitor), BorrowedStatic(value)).value(),
VisitResult::Control(Flow::Done)
);
}
// 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.
assert_eq!(
visitor.visit(TempBorrowedMutStatic(value)).value(),
Flow::Done.into()
);
// Temporary scope (smaller than the context we set above).
{
// A temporary value.
let mut value = String::from("test");
// Visit the temp value.
assert_eq!(
visitor.visit(TempBorrowedMutStatic(&mut value)).value(),
Flow::Done.into()
);
}
// 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)).value(),
VisitResult::Control(Flow::Done)
);
// Visit can return an error signal.
assert_eq!(
visitor.visit(OwnedStatic(1)).value(),
VisitResult::Control(Flow::Err)
);
// Visit can return a continue.
assert_eq!(
visitor.visit(OwnedStatic(2)).value(),
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)).value(),
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 = MockHintWalker::<ValueProto<OwnedStatic<i32>, 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>> = &mut mock;
// The value protocol has no hint data, and it has no known data.
assert_eq!(
walker.known(&()).value(),
Ok(ValueKnown {
preview: Some(&OwnedStatic(42))
})
);
}
{
let mut mock = MockHintWalker::<ValueProto<BorrowedStaticHrt<i32>, 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>> = &mut mock;
// The value protocol has no hint data, and it has no known data.
assert_eq!(
walker.known(&()).value(),
Ok(ValueKnown {
preview: Some(&BorrowedStatic(&42))
})
);
}
{
let mut mock = MockHintWalker::<ValueProto<TempBorrowedMutStaticHrt<i32>, 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>> = &mut mock;
// The value protocol has no hint data, and it has no known data.
assert_eq!(walker.known(&()).value(), Ok(ValueKnown { preview: None }));
}
}