1use crate::Backend;
7
8#[derive(Debug, Clone)]
10pub struct StressTestConfig {
11 pub cycles_per_backend: u32,
13 pub input_sizes: Vec<usize>,
15 pub backends: Vec<Backend>,
17 pub thresholds: StressThresholds,
19 pub master_seed: u64,
21}
22
23impl Default for StressTestConfig {
24 fn default() -> Self {
25 Self {
26 cycles_per_backend: 100,
27 input_sizes: vec![100, 1_000, 10_000, 100_000, 1_000_000],
28 backends: vec![Backend::Scalar, Backend::AVX2],
29 thresholds: StressThresholds::default(),
30 master_seed: 42,
31 }
32 }
33}
34
35impl StressTestConfig {
36 #[must_use]
38 pub fn new(master_seed: u64) -> Self {
39 Self { master_seed, ..Default::default() }
40 }
41
42 #[must_use]
44 pub const fn with_cycles(mut self, cycles: u32) -> Self {
45 self.cycles_per_backend = cycles;
46 self
47 }
48
49 #[must_use]
51 pub fn with_input_sizes(mut self, sizes: Vec<usize>) -> Self {
52 self.input_sizes = sizes;
53 self
54 }
55
56 #[must_use]
58 pub fn with_backends(mut self, backends: Vec<Backend>) -> Self {
59 self.backends = backends;
60 self
61 }
62
63 #[must_use]
65 pub fn with_thresholds(mut self, thresholds: StressThresholds) -> Self {
66 self.thresholds = thresholds;
67 self
68 }
69
70 #[must_use]
72 pub fn total_tests(&self) -> usize {
73 self.backends.len() * self.input_sizes.len() * self.cycles_per_backend as usize
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct StressThresholds {
80 pub max_op_time_ms: u64,
82 pub max_memory_bytes: usize,
84 pub max_timing_variance: f64,
86 pub max_failure_rate: f64,
88}
89
90impl Default for StressThresholds {
91 fn default() -> Self {
92 Self {
93 max_op_time_ms: 1000, max_memory_bytes: 256 * 1024 * 1024, max_timing_variance: 0.5, max_failure_rate: 0.0, }
98 }
99}
100
101impl StressThresholds {
102 #[must_use]
104 pub const fn strict() -> Self {
105 Self {
106 max_op_time_ms: 100,
107 max_memory_bytes: 64 * 1024 * 1024,
108 max_timing_variance: 0.2,
109 max_failure_rate: 0.0,
110 }
111 }
112
113 const RELAXED_MAX_OP_TIME_MS: u64 = 5000;
115
116 #[must_use]
118 pub const fn relaxed() -> Self {
119 Self {
120 max_op_time_ms: Self::RELAXED_MAX_OP_TIME_MS,
121 max_memory_bytes: 512 * 1024 * 1024,
122 max_timing_variance: 1.0,
123 max_failure_rate: 0.01,
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
130pub struct StressResult {
131 pub backend: Backend,
133 pub input_size: usize,
135 pub cycles_completed: u32,
137 pub tests_passed: u32,
139 pub tests_failed: u32,
141 pub mean_op_time_ms: f64,
143 pub max_op_time_ms: u64,
145 pub timing_variance: f64,
147 pub anomalies: Vec<StressAnomaly>,
149}
150
151impl StressResult {
152 #[must_use]
154 pub fn passed(&self) -> bool {
155 self.tests_failed == 0 && self.anomalies.is_empty()
156 }
157
158 #[must_use]
160 pub fn pass_rate(&self) -> f64 {
161 let total = self.tests_passed + self.tests_failed;
162 if total == 0 {
163 1.0
164 } else {
165 self.tests_passed as f64 / total as f64
166 }
167 }
168}
169
170#[derive(Debug, Clone)]
172pub struct StressAnomaly {
173 pub cycle: u32,
175 pub kind: StressAnomalyKind,
177 pub description: String,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum StressAnomalyKind {
184 SlowOperation,
186 HighMemory,
188 TestFailure,
190 TimingSpike,
192 NonDeterministic,
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_stress_test_config_default() {
202 let config = StressTestConfig::default();
203
204 assert_eq!(config.cycles_per_backend, 100);
205 assert_eq!(config.input_sizes.len(), 5);
206 assert_eq!(config.backends.len(), 2);
207 assert_eq!(config.master_seed, 42);
208 }
209
210 #[test]
211 fn test_stress_test_config_builder() {
212 let config = StressTestConfig::new(123)
213 .with_cycles(50)
214 .with_input_sizes(vec![100, 1000])
215 .with_backends(vec![Backend::Scalar])
216 .with_thresholds(StressThresholds::strict());
217
218 assert_eq!(config.master_seed, 123);
219 assert_eq!(config.cycles_per_backend, 50);
220 assert_eq!(config.input_sizes.len(), 2);
221 assert_eq!(config.backends.len(), 1);
222 }
223
224 #[test]
225 fn test_stress_test_config_total_tests() {
226 let config = StressTestConfig::default()
227 .with_cycles(10)
228 .with_input_sizes(vec![100, 1000, 10000])
229 .with_backends(vec![Backend::Scalar, Backend::AVX2]);
230
231 assert_eq!(config.total_tests(), 60);
233 }
234
235 #[test]
236 fn test_stress_thresholds_default() {
237 let thresholds = StressThresholds::default();
238
239 assert_eq!(thresholds.max_op_time_ms, 1000);
240 assert_eq!(thresholds.max_memory_bytes, 256 * 1024 * 1024);
241 assert!((thresholds.max_timing_variance - 0.5).abs() < 0.001);
242 assert_eq!(thresholds.max_failure_rate, 0.0);
243 }
244
245 #[test]
246 fn test_stress_thresholds_strict() {
247 let thresholds = StressThresholds::strict();
248
249 assert_eq!(thresholds.max_op_time_ms, 100);
250 assert_eq!(thresholds.max_memory_bytes, 64 * 1024 * 1024);
251 assert!((thresholds.max_timing_variance - 0.2).abs() < 0.001);
252 }
253
254 #[test]
255 fn test_stress_thresholds_relaxed() {
256 let thresholds = StressThresholds::relaxed();
257
258 assert_eq!(thresholds.max_op_time_ms, 5000);
259 assert_eq!(thresholds.max_memory_bytes, 512 * 1024 * 1024);
260 assert!((thresholds.max_timing_variance - 1.0).abs() < 0.001);
261 }
262
263 #[test]
264 fn test_stress_result_passed() {
265 let result = StressResult {
266 backend: Backend::Scalar,
267 input_size: 1000,
268 cycles_completed: 10,
269 tests_passed: 100,
270 tests_failed: 0,
271 mean_op_time_ms: 50.0,
272 max_op_time_ms: 100,
273 timing_variance: 0.1,
274 anomalies: vec![],
275 };
276
277 assert!(result.passed());
278 assert_eq!(result.pass_rate(), 1.0);
279 }
280
281 #[test]
282 fn test_stress_result_failed() {
283 let result = StressResult {
284 backend: Backend::AVX2,
285 input_size: 10000,
286 cycles_completed: 10,
287 tests_passed: 95,
288 tests_failed: 5,
289 mean_op_time_ms: 100.0,
290 max_op_time_ms: 500,
291 timing_variance: 0.3,
292 anomalies: vec![],
293 };
294
295 assert!(!result.passed()); assert!((result.pass_rate() - 0.95).abs() < 0.001);
297 }
298
299 #[test]
300 fn test_stress_result_with_anomaly() {
301 let result = StressResult {
302 backend: Backend::Scalar,
303 input_size: 1000,
304 cycles_completed: 10,
305 tests_passed: 100,
306 tests_failed: 0,
307 mean_op_time_ms: 50.0,
308 max_op_time_ms: 100,
309 timing_variance: 0.1,
310 anomalies: vec![StressAnomaly {
311 cycle: 5,
312 kind: StressAnomalyKind::SlowOperation,
313 description: "Operation took 200ms".to_string(),
314 }],
315 };
316
317 assert!(!result.passed()); }
319
320 #[test]
321 fn test_stress_anomaly_kinds() {
322 assert_eq!(StressAnomalyKind::SlowOperation, StressAnomalyKind::SlowOperation);
323 assert_ne!(StressAnomalyKind::SlowOperation, StressAnomalyKind::TestFailure);
324
325 let _ = StressAnomalyKind::SlowOperation;
327 let _ = StressAnomalyKind::HighMemory;
328 let _ = StressAnomalyKind::TestFailure;
329 let _ = StressAnomalyKind::TimingSpike;
330 let _ = StressAnomalyKind::NonDeterministic;
331 }
332
333 #[test]
334 fn test_stress_result_zero_tests() {
335 let result = StressResult {
336 backend: Backend::Scalar,
337 input_size: 0,
338 cycles_completed: 0,
339 tests_passed: 0,
340 tests_failed: 0,
341 mean_op_time_ms: 0.0,
342 max_op_time_ms: 0,
343 timing_variance: 0.0,
344 anomalies: vec![],
345 };
346
347 assert!(result.passed());
349 assert_eq!(result.pass_rate(), 1.0);
350 }
351}