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