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