hydro_lang/sim/mod.rs
1//! Deterministic simulation testing support for Hydro programs.
2//!
3//! See [`crate::compile::builder::FlowBuilder::sim`] and [`crate::sim::flow::SimFlow`] for more details.
4
5use std::marker::PhantomData;
6
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9
10use crate::compile::builder::ExternalPortId;
11use crate::live_collections::stream::{Ordering, Retries};
12
13/// A receiver for an external bincode stream in a simulation.
14pub struct SimReceiver<T: Serialize + DeserializeOwned, O: Ordering, R: Retries>(
15 pub(crate) ExternalPortId,
16 pub(crate) PhantomData<(T, O, R)>,
17);
18
19/// A sender to an external bincode sink in a simulation.
20pub struct SimSender<T: Serialize + DeserializeOwned, O: Ordering, R: Retries>(
21 pub(crate) ExternalPortId,
22 pub(crate) PhantomData<(T, O, R)>,
23);
24
25/// A receiver for an external cluster stream in a simulation.
26///
27/// Each received value is a `(u32, T)` tuple where the `u32` is the raw
28/// cluster member ID that produced the value.
29pub struct SimClusterReceiver<T: Serialize + DeserializeOwned, O: Ordering, R: Retries>(
30 pub(crate) ExternalPortId,
31 pub(crate) PhantomData<(T, O, R)>,
32);
33
34/// A sender to an external cluster sink in a simulation.
35///
36/// Each sent value is a `(u32, T)` tuple where the `u32` is the raw
37/// cluster member ID that should receive the value.
38pub struct SimClusterSender<T: Serialize + DeserializeOwned, O: Ordering, R: Retries>(
39 pub(crate) ExternalPortId,
40 pub(crate) PhantomData<(T, O, R)>,
41);
42
43#[cfg(stageleft_runtime)]
44mod builder;
45
46#[cfg(stageleft_runtime)]
47pub mod compiled;
48
49#[cfg(stageleft_runtime)]
50pub(crate) mod graph;
51
52#[cfg(stageleft_runtime)]
53pub mod flow;
54
55#[cfg(stageleft_runtime)]
56pub mod hooks;
57
58#[cfg(stageleft_runtime)]
59pub(crate) mod versioned_network;
60
61#[cfg(stageleft_runtime)]
62#[doc(hidden)]
63pub mod runtime;
64
65#[cfg(stageleft_runtime)]
66#[doc(hidden)]
67pub use compiled::continue_if_impl;
68#[cfg(stageleft_runtime)]
69pub use compiled::quiesce;
70
71/// Continues the current simulation instance only if the given condition holds, otherwise
72/// stopping and discarding the instance.
73///
74/// This is the same concept as `assume` in verification tools and property-based testing
75/// libraries (e.g. `kani::assume` or proptest's `prop_assume!`). It is useful inside
76/// simulation tests ([`crate::sim::flow::SimFlow::fuzz`],
77/// [`crate::sim::flow::SimFlow::exhaustive`], and the corresponding
78/// [`crate::sim::compiled::CompiledSim`] APIs) to restrict exploration to executions that
79/// satisfy some precondition. When the condition is false, the current instance is stopped
80/// and discarded: it is **not** treated as a test failure (and will never be recorded as a
81/// fuzzing reproducer), and the fuzzer / exhaustive search simply moves on to the next
82/// instance. If logging is enabled (always during replays, or when `HYDRO_SIM_LOG=1`), the
83/// failed assumption is logged.
84///
85/// Like the standard `assert!` macro, an optional custom message with format arguments can be
86/// provided.
87///
88/// ```rust,ignore
89/// flow.sim().fuzz(async || {
90/// in_send.send_many([1, 2]);
91/// let all: Vec<u32> = out_recv.collect().await;
92/// hydro_lang::sim::continue_if!(all.len() == 2, "expected both values in one batch, got {:?}", all);
93/// // ... assertions that only make sense when the assumption holds ...
94/// });
95/// ```
96#[doc(hidden)]
97#[macro_export]
98macro_rules! continue_if {
99 ($cond:expr $(,)?) => {
100 $crate::sim::continue_if_impl(
101 $cond,
102 ::core::format_args!("{}", ::core::stringify!($cond)),
103 )
104 };
105 ($cond:expr, $($arg:tt)+) => {
106 $crate::sim::continue_if_impl($cond, ::core::format_args!($($arg)+))
107 };
108}
109
110#[doc(inline)]
111pub use crate::continue_if;
112
113#[cfg(test)]
114mod tests;