Skip to main content

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(crate) mod versioned_network;
57
58#[cfg(stageleft_runtime)]
59#[doc(hidden)]
60pub mod runtime;
61
62#[cfg(stageleft_runtime)]
63#[doc(hidden)]
64pub use compiled::continue_if_impl;
65
66/// Continues the current simulation instance only if the given condition holds, otherwise
67/// stopping and discarding the instance.
68///
69/// This is the same concept as `assume` in verification tools and property-based testing
70/// libraries (e.g. `kani::assume` or proptest's `prop_assume!`). It is useful inside
71/// simulation tests ([`crate::sim::flow::SimFlow::fuzz`],
72/// [`crate::sim::flow::SimFlow::exhaustive`], and the corresponding
73/// [`crate::sim::compiled::CompiledSim`] APIs) to restrict exploration to executions that
74/// satisfy some precondition. When the condition is false, the current instance is stopped
75/// and discarded: it is **not** treated as a test failure (and will never be recorded as a
76/// fuzzing reproducer), and the fuzzer / exhaustive search simply moves on to the next
77/// instance. If logging is enabled (always during replays, or when `HYDRO_SIM_LOG=1`), the
78/// failed assumption is logged.
79///
80/// Like the standard `assert!` macro, an optional custom message with format arguments can be
81/// provided.
82///
83/// ```rust,ignore
84/// flow.sim().fuzz(async || {
85///     in_send.send_many([1, 2]);
86///     let all: Vec<u32> = out_recv.collect().await;
87///     hydro_lang::sim::continue_if!(all.len() == 2, "expected both values in one batch, got {:?}", all);
88///     // ... assertions that only make sense when the assumption holds ...
89/// });
90/// ```
91#[doc(hidden)]
92#[macro_export]
93macro_rules! continue_if {
94    ($cond:expr $(,)?) => {
95        $crate::sim::continue_if_impl(
96            $cond,
97            ::core::format_args!("{}", ::core::stringify!($cond)),
98        )
99    };
100    ($cond:expr, $($arg:tt)+) => {
101        $crate::sim::continue_if_impl($cond, ::core::format_args!($($arg)+))
102    };
103}
104
105#[doc(inline)]
106pub use crate::continue_if;
107
108#[cfg(test)]
109mod tests;