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}