veecle_os_test/execute.rs
1/// Execute a test case with a set of actors.
2///
3/// This macro's syntax mirrors that of `veecle_os::runtime::execute!` with an extra `validation` argument.
4/// The argument should be an async closure that runs any needed validations on the actors behaviors.
5///
6/// Any store lifetimes in the `validation` argument should use `'_` as a placeholder.
7///
8/// ```rust
9/// use veecle_os::runtime::{Never, Reader, Writer, Storable};
10///
11/// #[derive(Clone, Copy, Debug, Eq, PartialEq, Storable)]
12/// pub struct Data(u32);
13///
14/// #[derive(Debug, Storable)]
15/// pub struct Trigger;
16///
17/// #[veecle_os::runtime::actor]
18/// async fn incrementor(mut writer: Writer<'_, Data>, mut trigger: Reader<'_, Trigger>) -> Never {
19/// loop {
20/// trigger.wait_for_update().await;
21/// writer.modify(|data| {
22/// *data = Some(data.map_or(Data(0), |data| Data(data.0 + 1)));
23/// }).await;
24/// }
25/// }
26///
27/// veecle_os_test::block_on_future(
28/// veecle_os_test::execute! {
29/// actors: [Incrementor],
30///
31/// validation: async |mut reader: Reader<'_, Data>, mut trigger: Writer<'_, Trigger>| {
32/// trigger.write(Trigger).await;
33/// assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(0)));
34/// trigger.write(Trigger).await;
35/// assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(1)));
36/// trigger.write(Trigger).await;
37/// assert_eq!(reader.wait_for_update().await.read_cloned(), Some(Data(2)));
38/// },
39/// }
40/// );
41/// ```
42#[macro_export]
43macro_rules! execute {
44 (
45 actors: [
46 $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
47 ],
48
49 validation: async |$(mut $arg_pat:ident : $arg_ty:ty),* $(,)?| $validation_body:block $(,)?
50 ) => {{
51 #[$crate::__exports::veecle_os_runtime::actor(crate = $crate::__exports::veecle_os_runtime)]
52 async fn veecle_os_test_validator_generated_actor(
53 $(mut $arg_pat : $arg_ty,)*
54 #[init_context] __complete: $crate::__exports::futures::channel::oneshot::Sender<()>,
55 ) -> $crate::__exports::veecle_os_runtime::Never {
56 $validation_body;
57 __complete.send(()).unwrap();
58 core::future::pending().await
59 }
60
61 async {
62 let (complete_tx, complete_rx) =
63 $crate::__exports::futures::channel::oneshot::channel::<()>();
64
65 let executor = core::pin::pin!(
66 $crate::__exports::veecle_os_runtime::execute! {
67 actors: [
68 $($actor_type $(: $init_context)? ,)*
69 VeecleOsTestValidatorGeneratedActor: complete_tx,
70 ],
71 }
72 );
73
74 $crate::__exports::futures::future::select(executor, complete_rx).await;
75 }
76 }};
77
78 // The previous arm doesn't support `validation: async ||` (no space between first `|´ and second ´|´) for some reason.
79 // To avoid forcing users to add whitespace between `||`, we add this arm.
80 (
81 actors: [
82 $($actor_type:ty $(: $init_context:expr )? ),* $(,)?
83 ],
84
85 validation: async || $validation_body:block $(,)?
86 ) => {{
87 $crate::execute!(
88 actors: [
89 $($actor_type $(: $init_context)? ),*
90 ],
91
92 validation: async | | $validation_body
93 )
94 }};
95}
96
97#[cfg(test)]
98#[cfg_attr(coverage_nightly, coverage(off))]
99mod tests {
100 #[veecle_os_runtime::actor]
101 async fn contextual_actor<T: core::fmt::Debug>(
102 #[init_context] _context: T,
103 ) -> veecle_os_runtime::Never {
104 std::future::pending().await
105 }
106
107 #[test]
108 fn local_context() {
109 let local = vec![1];
110 futures::executor::block_on(crate::execute! {
111 actors: [
112 ContextualActor<&Vec<i32>>: &local,
113 ],
114 validation: async || {}
115 });
116 dbg!(&local);
117 }
118}