1mod jidoka;
27mod scheduler;
28mod stress;
29mod visual;
30
31pub use visual::{
33 BufferRenderer, ColorPalette, GoldenBaseline, PixelDiffResult, Rgb, VisualRegressionConfig,
34};
35
36pub use stress::{
38 StressAnomaly, StressAnomalyKind, StressResult, StressTestConfig, StressThresholds,
39};
40
41pub use jidoka::{JidokaAction, JidokaCondition, JidokaError, JidokaGuard};
43
44pub use scheduler::{
46 BackendCategory, BackendSelector, BackendTolerance, HeijunkaScheduler, NeedsSeed, Ready,
47 SimTestConfig, SimTestConfigBuilder, SimulationTest,
48};
49
50#[cfg(test)]
52pub use simular::engine::rng::SimRng;
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use crate::Backend;
58
59 #[test]
60 fn test_simrng_reproducibility() {
61 let mut rng1 = SimRng::new(42);
63 let mut rng2 = SimRng::new(42);
64
65 let seq1: Vec<f64> = (0..100).map(|_| rng1.gen_f64()).collect();
66 let seq2: Vec<f64> = (0..100).map(|_| rng2.gen_f64()).collect();
67
68 assert_eq!(seq1, seq2, "Same seed must produce identical sequences");
69 }
70
71 #[test]
72 fn test_simrng_different_seeds() {
73 let mut rng1 = SimRng::new(42);
75 let mut rng2 = SimRng::new(43);
76
77 let seq1: Vec<f64> = (0..100).map(|_| rng1.gen_f64()).collect();
78 let seq2: Vec<f64> = (0..100).map(|_| rng2.gen_f64()).collect();
79
80 assert_ne!(seq1, seq2, "Different seeds must produce different sequences");
81 }
82
83 #[test]
84 fn test_simrng_partitioning() {
85 let mut rng = SimRng::new(42);
87 let partitions = rng.partition(4);
88
89 assert_eq!(partitions.len(), 4);
90
91 let mut seqs: Vec<Vec<f64>> = Vec::new();
93 for mut p in partitions {
94 seqs.push((0..10).map(|_| p.gen_f64()).collect());
95 }
96
97 for i in 0..seqs.len() {
98 for j in (i + 1)..seqs.len() {
99 assert_ne!(seqs[i], seqs[j], "Partitions must be independent");
100 }
101 }
102 }
103
104 #[test]
105 fn test_simrng_gen_f32_for_trueno() {
106 let mut rng = SimRng::new(42);
108
109 let test_data: Vec<f32> = (0..1000).map(|_| rng.gen_f64() as f32).collect();
110
111 for v in &test_data {
113 assert!(v.is_finite(), "Generated value should be finite");
114 assert!(*v >= 0.0 && *v < 1.0, "Value should be in [0, 1)");
115 }
116 }
117
118 #[test]
119 fn test_full_simulation_workflow() {
120 let config = SimTestConfig::builder()
122 .seed(42)
123 .tolerance(BackendTolerance::default())
124 .backends(vec![Backend::Scalar, Backend::AVX2])
125 .input_sizes(vec![100])
126 .cycles(2)
127 .build();
128
129 let mut scheduler = config.create_scheduler();
131
132 let nan_guard = JidokaGuard::nan_guard("simulation_test");
134
135 let mut test_count = 0;
137 while let Some(test) = scheduler.next_test() {
138 let mut rng = SimRng::new(test.seed);
140 let data: Vec<f32> = (0..test.input_size).map(|_| rng.gen_f64() as f32).collect();
141
142 let result = nan_guard.check_output(&data);
144 assert!(result.is_ok(), "Generated data should not contain NaN");
145
146 test_count += 1;
147 }
148
149 assert_eq!(test_count, 4);
151 }
152}
153
154#[cfg(test)]
155mod proptests {
156 use super::*;
157 use proptest::prelude::*;
158
159 proptest! {
160 #[test]
162 fn prop_nan_detection_complete(values in prop::collection::vec(-1000.0f32..1000.0, 0..100)) {
163 let guard = JidokaGuard::nan_guard("test");
164
165 let result = guard.check_output(&values);
167 prop_assert!(result.is_ok());
168 }
169 }
170}