Diffstat (limited to 'tests/protocol_walker_hint.rs')
-rw-r--r--tests/protocol_walker_hint.rs313
1 files changed, 313 insertions, 0 deletions
diff --git a/tests/protocol_walker_hint.rs b/tests/protocol_walker_hint.rs
new file mode 100644
index 0000000..6f39590
--- /dev/null
+++ b/tests/protocol_walker_hint.rs
@@ -0,0 +1,313 @@
+use std::any::TypeId;
+
+use common::protocol::hint::MockHintWalker;
+use treaty::{
+ any::TypeNameId,
+ effect::{Blocking, Effect, Future, Spin},
+ hkt::higher_ranked_type,
+ protocol::{
+ walker::hint::{self, Hint, HintMeta, HintProto, Meta, MetaKnown}, DynVisitor
+ },
+ Flow,
+};
+
+use crate::common::{builder::MockBuilder, protocol::hint::KnownFactory};
+
+mod common;
+
+/// This tests for the hint protocol being able to give the known info and being able to hint.
+#[test]
+fn can_get_known_and_hint() {
+ // The protocol we will hint for.
+ struct MyProtocol;
+
+ // The known value for the protocol.
+ #[derive(Debug, PartialEq)]
+ struct Known(f32);
+
+ // Link the higher ranked type with the known type.
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for Known = Known;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known = Known;
+ }
+ }
+
+ #[derive(Debug, PartialEq)]
+ struct Hint(i32);
+
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for Hint = Hint;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint = Hint;
+ }
+ }
+
+ // Enroll the protocol in the hint system.
+ impl HintMeta for MyProtocol {
+ type Known = Known;
+ type Hint = Hint;
+ type Effect = Blocking;
+ }
+
+ let mut mock = MockHintWalker::<MyProtocol>::new();
+
+ mock.expect_known()
+ .once()
+ .return_const((|_, Hint(hint)| Ok(Known(*hint as f32))) as KnownFactory<MyProtocol>);
+
+ mock.expect_hint()
+ .once()
+ .withf(|_visitor, Hint(hint)| *hint == 123)
+ .return_const(Flow::Done);
+
+ // Get the mock as a hint protocol trait object.
+ let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
+
+ // We can call known to get what the walker knows about the protocol.
+ assert_eq!(walker.known(&Hint(42)).into_inner(), Ok(Known(42.0)));
+
+ {
+ // A hint needs the visitor for the walker to walk.
+ let mut mock = MockBuilder::<(), (), ()>::new();
+
+ // We can call hint to "commit" to the protocol and ask the walker to use it.
+ walker.hint(DynVisitor(&mut mock), Hint(123));
+ }
+}
+
+#[test]
+fn known_can_have_temp_mutable_borrow() {
+ struct MyProtocol;
+
+ struct KnownHrt;
+
+ #[derive(Debug, PartialEq)]
+ struct Known<'a>(&'a mut String);
+
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for KnownHrt = Known<'a>;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known<'a> = KnownHrt;
+ }
+ }
+
+ impl HintMeta for MyProtocol {
+ type Known = KnownHrt;
+ type Hint = ();
+ type Effect = Blocking;
+ }
+
+ struct Walker<'ctx>(&'ctx mut String);
+
+ impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> {
+ fn hint<'a>(
+ &'a mut self,
+ _visitor: DynVisitor<'a, 'ctx>,
+ _hint: <MyProtocol as HintMeta>::Hint,
+ ) -> Future<'a, Flow, Blocking> {
+ unreachable!()
+ }
+
+ fn known<'a>(
+ &'a mut self,
+ (): &'a <MyProtocol as HintMeta>::Hint,
+ ) -> Future<'a, Result<MetaKnown<'a, 'ctx, MyProtocol>, ()>, Blocking> {
+ self.0.push_str("test");
+
+ Blocking::<Spin>::ready(Ok(Known(self.0)))
+ }
+ }
+
+ let mut context = String::new();
+
+ {
+ let mut walker = Walker(&mut context);
+
+ // Get the mock as a hint protocol trait object.
+ let walker: &mut dyn Hint<MyProtocol> = &mut walker;
+
+ // We can call known to get what the walker knows about the protocol.
+ let mut x = String::from("test");
+ assert_eq!(walker.known(&()).into_inner(), Ok(Known(&mut x)));
+ }
+
+ drop(context);
+}
+
+#[test]
+fn known_can_have_context_borrow() {
+ struct MyProtocol;
+
+ struct KnownHrt;
+
+ #[derive(Debug, PartialEq)]
+ struct Known<'ctx>(&'ctx String);
+
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for KnownHrt = Known<'ctx>;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known<'ctx> = KnownHrt;
+ }
+ }
+
+ impl HintMeta for MyProtocol {
+ type Known = KnownHrt;
+ type Hint = ();
+ type Effect = Blocking;
+ }
+
+ struct Walker<'ctx>(&'ctx String);
+
+ impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> {
+ fn hint<'a>(
+ &'a mut self,
+ _visitor: DynVisitor<'a, 'ctx>,
+ _hint: <MyProtocol as HintMeta>::Hint,
+ ) -> Future<'a, Flow, Blocking> {
+ unreachable!()
+ }
+
+ fn known<'a>(
+ &'a mut self,
+ (): &'a <MyProtocol as HintMeta>::Hint,
+ ) -> Future<'a, Result<MetaKnown<'a, 'ctx, MyProtocol>, ()>, Blocking> {
+ Blocking::<Spin>::ready(Ok(Known(self.0)))
+ }
+ }
+
+ let mut context = String::from("test");
+
+ let ctx = {
+ let mut walker = Walker(&mut context);
+
+ // Get the mock as a hint protocol trait object.
+ let walker: &mut dyn Hint<MyProtocol> = &mut walker;
+
+ // We can call known to get what the walker knows about the protocol.
+ let Ok(Known(y)) = walker.known(&()).into_inner() else {
+ unreachable!()
+ };
+ y
+ };
+
+ assert_eq!(ctx, "test");
+
+ drop(context);
+}
+
+#[test]
+fn hint_can_have_temp_mutable_borrow() {
+ struct MyProtocol;
+
+ struct HintHrt;
+
+ #[derive(Debug, PartialEq)]
+ struct Hint<'a>(&'a mut String);
+
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for HintHrt = Hint<'a>;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint<'a> = HintHrt;
+ }
+ }
+
+ impl HintMeta for MyProtocol {
+ type Known = ();
+ type Hint = HintHrt;
+ type Effect = Blocking;
+ }
+
+ let mut mock = MockHintWalker::new();
+
+ mock.expect_hint().once().returning(|_, hint: Hint| {
+ hint.0.push_str("test");
+
+ Flow::Done
+ });
+
+ {
+ // Get the mock as a hint protocol trait object.
+ let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
+
+ let mut visitor = MockBuilder::<(), (), ()>::new();
+
+ let mut temp = String::new();
+
+ // We can call known to get what the walker knows about the protocol.
+ assert_eq!(
+ walker.hint(DynVisitor(&mut visitor), Hint(&mut temp)).into_inner(),
+ Flow::Done
+ );
+
+ assert_eq!(temp, "test");
+ }
+}
+
+#[test]
+fn hint_can_have_context_borrow() {
+ struct MyProtocol;
+
+ struct HintHrt;
+
+ #[derive(Debug, PartialEq)]
+ struct Hint<'ctx>(&'ctx String);
+
+ higher_ranked_type! {
+ impl Meta {
+ impl['a, 'ctx] type T['a, 'ctx] for HintHrt = Hint<'ctx>;
+ impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint<'ctx> = HintHrt;
+ }
+ }
+
+ impl HintMeta for MyProtocol {
+ type Known = ();
+ type Hint = HintHrt;
+ type Effect = Blocking;
+ }
+
+ let mut mock = MockHintWalker::new();
+
+ mock.expect_hint().once().returning(|_, hint: Hint| {
+ assert_eq!(hint.0, "test");
+
+ Flow::Done
+ });
+
+ let context = String::from("test");
+
+ {
+ // Get the mock as a hint protocol trait object.
+ let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
+
+ let mut visitor = MockBuilder::<(), (), ()>::new();
+
+ // We can call known to get what the walker knows about the protocol.
+ assert_eq!(
+ walker.hint(DynVisitor(&mut visitor), Hint(&context)).into_inner(),
+ Flow::Done
+ );
+ }
+
+ drop(context);
+}
+
+#[test]
+fn hint_proto() {
+ struct MyProtocol;
+
+ impl HintMeta for MyProtocol {
+ type Known = ();
+ type Hint = ();
+ type Effect = Blocking;
+ }
+
+ // The type id of the higher ranked type.
+ let id = TypeId::of::<HintProto<MyProtocol>>();
+
+ // The type id for the lifetime containing value protocol trait object.
+ let name_id = TypeNameId::of_lower::<dyn hint::Hint<MyProtocol> + Send + Sync>();
+
+ // They should be the same.
+ assert_eq!(id, name_id.into_type_id());
+}