veecle_os_test/
execute.rs

1/// Makes a [cons-lists](https://en.wikipedia.org/wiki/Cons#Lists) of types/identifiers to allow the `validation`
2/// "function" below to support an arbitrary number of input arguments.
3#[doc(hidden)]
4#[macro_export]
5macro_rules! __make_tuple_cons {
6    () => {
7        ()
8    };
9
10    (mut $first:ident, $($rest:tt)* ) => {
11        (mut $first, $crate::__make_tuple_cons!($($rest)*))
12    };
13
14    ($first:ty, $($rest:tt)* ) => {
15        ($first, $crate::__make_tuple_cons!($($rest)*))
16    };
17}
18
19/// Execute a test case with a set of actors.
20///
21/// This macro's syntax mirrors that of `veecle_os::runtime::execute!` with an extra `validation` argument.
22/// The argument should be an async closure that runs any needed validations on the actors behaviors.
23///
24/// Any store lifetimes in the `validation` argument should be replaced with `'a`.
25///
26/// ```rust
27/// use core::convert::Infallible;
28/// use veecle_os::runtime::{Reader, Writer, Storable};
29///
30/// #[derive(Clone, Copy, Debug, Eq, PartialEq, Storable)]
31/// pub struct Data(u32);
32///
33/// #[derive(Debug, Storable)]
34/// pub struct Trigger;
35///
36/// #[veecle_os::runtime::actor]
37/// async fn incrementor(mut writer: Writer<'_, Data>, mut trigger: Reader<'_, Trigger>) -> Infallible {
38///     loop {
39///         trigger.wait_for_update().await;
40///         writer.modify(|data| {
41///             *data = Some(data.map_or(Data(0), |data| Data(data.0 + 1)));
42///         }).await;
43///     }
44/// }
45///
46/// veecle_os_test::block_on_future(
47///     veecle_os_test::execute! {
48///         store: [Data, Trigger],
49///
50///         actors: [Incrementor],
51///
52///         validation: async |mut reader: Reader<'a, Data>, mut trigger: Writer<'a, Trigger>| {
53///             trigger.write(Trigger).await;
54///             assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(0)));
55///             trigger.write(Trigger).await;
56///             assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(1)));
57///             trigger.write(Trigger).await;
58///             assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(2)));
59///         },
60///     }
61/// );
62/// ```
63#[macro_export]
64macro_rules! execute {
65    (
66        store: [
67            $($data_type:ty),* $(,)?
68        ],
69
70        actors: [
71            $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
72        ],
73
74        validation: async |$(mut $arg_pat:ident : $arg_ty:ty),* $(,)?| $validation_body:block $(,)?
75    ) => {{
76        struct Validator<'a> {
77            store_request: $crate::__make_tuple_cons!($($arg_ty,)*),
78            complete: $crate::__exports::futures::channel::oneshot::Sender<()>,
79            // In case the validation body doesn't use readers/writers, we need to use the lifetime
80            // to avoid the compiler complaining about unused lifetimes.
81            _phantom: core::marker::PhantomData<&'a ()>,
82        }
83
84        impl<'a> $crate::__exports::veecle_os_runtime::Actor<'a> for Validator<'a> {
85            type StoreRequest = $crate::__make_tuple_cons!($($arg_ty,)*);
86            type InitContext = $crate::__exports::futures::channel::oneshot::Sender<()>;
87            type Error = core::convert::Infallible;
88
89            fn new(store_request: Self::StoreRequest, complete: Self::InitContext) -> Self {
90                Self {
91                    store_request,
92                    complete,
93                    _phantom: core::marker::PhantomData
94                }
95            }
96
97            async fn run(self) -> Result<core::convert::Infallible, Self::Error> {
98                let $crate::__make_tuple_cons!($(mut $arg_pat,)*) = self.store_request;
99                $validation_body;
100                self.complete.send(()).unwrap();
101                core::future::pending().await
102            }
103        }
104
105        async {
106            let (complete_tx, complete_rx) =
107                $crate::__exports::futures::channel::oneshot::channel::<()>();
108
109            let executor = core::pin::pin!(
110                $crate::__exports::veecle_os_runtime::execute! {
111                    store: [ $($data_type,)* ],
112
113                    actors: [
114                        $($actor_type $(: $init_context)? ,)*
115
116                        Validator: complete_tx,
117                    ],
118                }
119            );
120
121            $crate::__exports::futures::future::select(executor, complete_rx).await;
122        }
123    }};
124
125    // The previous arm doesn't support `validation: async ||` (no space between first `|´ and second ´|´) for some reason.
126    // To avoid forcing users to add whitespace between `||`, we add this arm.
127    (
128        store: [
129            $($data_type:ty),* $(,)?
130        ],
131
132        actors: [
133            $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
134        ],
135
136        validation: async || $validation_body:block $(,)?
137    ) => {{
138        $crate::execute!(
139            store: [
140            $($data_type),*
141        ],
142
143        actors: [
144            $($actor_type $(: $init_context)? ),*
145        ],
146
147        validation: async | | $validation_body
148        )
149    }};
150}
151
152#[cfg(test)]
153mod tests {
154    #[veecle_os_runtime::actor]
155    async fn contextual_actor<T: core::fmt::Debug>(
156        #[init_context] _context: T,
157    ) -> core::convert::Infallible {
158        std::future::pending().await
159    }
160
161    #[test]
162    fn local_context() {
163        let local = vec![1];
164        futures::executor::block_on(crate::execute! {
165            store: [],
166
167            actors: [
168                ContextualActor<_>: &local,
169            ],
170                validation: async || {}
171        });
172        dbg!(&local);
173    }
174}