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