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