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