## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/trueno/src/simulation/stress.rs

    Line
    Count
    Source
    1
    //! Stress Testing (Heijunka: Leveled Workload Testing)
    2
    //!
    3
    //! Provides configurable stress testing infrastructure for validating
    4
    //! compute operations under various load conditions.
    5
    6
    use crate::Backend;
    7
    8
    /// Stress test configuration for trueno operations
    9
    #[derive(Debug, Clone)]
    10
    pub struct StressTestConfig {
    11
        /// Number of cycles per backend
    12
        pub cycles_per_backend: u32,
    13
        /// Input sizes to test (leveled)
    14
        pub input_sizes: Vec<usize>,
    15
        /// Backends to stress test
    16
        pub backends: Vec<Backend>,
    17
        /// Performance thresholds
    18
        pub thresholds: StressThresholds,
    19
        /// Master seed for RNG
    20
        pub master_seed: u64,
    21
    }
    22
    23
    impl Default for StressTestConfig {
    24
    0
        fn default() -> Self {
    25
    0
            Self {
    26
    0
                cycles_per_backend: 100,
    27
    0
                input_sizes: vec![100, 1_000, 10_000, 100_000, 1_000_000],
    28
    0
                backends: vec![Backend::Scalar, Backend::AVX2],
    29
    0
                thresholds: StressThresholds::default(),
    30
    0
                master_seed: 42,
    31
    0
            }
    32
    0
        }
    33
    }
    34
    35
    impl StressTestConfig {
    36
        /// Create new stress test config
    37
        #[must_use]
    38
    0
        pub fn new(master_seed: u64) -> Self {
    39
    0
            Self {
    40
    0
                master_seed,
    41
    0
                ..Default::default()
    42
    0
            }
    43
    0
        }
    44
    45
        /// Set cycles per backend
    46
        #[must_use]
    47
    0
        pub const fn with_cycles(mut self, cycles: u32) -> Self {
    48
    0
            self.cycles_per_backend = cycles;
    49
    0
            self
    50
    0
        }
    51
    52
        /// Set input sizes
    53
        #[must_use]
    54
    0
        pub fn with_input_sizes(mut self, sizes: Vec<usize>) -> Self {
    55
    0
            self.input_sizes = sizes;
    56
    0
            self
    57
    0
        }
    58
    59
        /// Set backends to test
    60
        #[must_use]
    61
    0
        pub fn with_backends(mut self, backends: Vec<Backend>) -> Self {
    62
    0
            self.backends = backends;
    63
    0
            self
    64
    0
        }
    65
    66
        /// Set performance thresholds
    67
        #[must_use]
    68
    0
        pub fn with_thresholds(mut self, thresholds: StressThresholds) -> Self {
    69
    0
            self.thresholds = thresholds;
    70
    0
            self
    71
    0
        }
    72
    73
        /// Calculate total test count
    74
        #[must_use]
    75
    0
        pub fn total_tests(&self) -> usize {
    76
    0
            self.backends.len() * self.input_sizes.len() * self.cycles_per_backend as usize
    77
    0
        }
    78
    }
    79
    80
    /// Performance thresholds for stress testing
    81
    #[derive(Debug, Clone)]
    82
    pub struct StressThresholds {
    83
        /// Max time per operation (ms)
    84
        pub max_op_time_ms: u64,
    85
        /// Max memory per operation (bytes)
    86
        pub max_memory_bytes: usize,
    87
        /// Max variance in operation times (coefficient of variation)
    88
        pub max_timing_variance: f64,
    89
        /// Max allowed failure rate (0.0 to 1.0)
    90
        pub max_failure_rate: f64,
    91
    }
    92
    93
    impl Default for StressThresholds {
    94
    0
        fn default() -> Self {
    95
    0
            Self {
    96
    0
                max_op_time_ms: 1000,                // 1s max per op
    97
    0
                max_memory_bytes: 256 * 1024 * 1024, // 256MB max
    98
    0
                max_timing_variance: 0.5,            // 50% max variance
    99
    0
                max_failure_rate: 0.0,               // Zero failures allowed
    100
    0
            }
    101
    0
        }
    102
    }
    103
    104
    impl StressThresholds {
    105
        /// Strict thresholds for CI
    106
        #[must_use]
    107
    0
        pub const fn strict() -> Self {
    108
    0
            Self {
    109
    0
                max_op_time_ms: 100,
    110
    0
                max_memory_bytes: 64 * 1024 * 1024,
    111
    0
                max_timing_variance: 0.2,
    112
    0
                max_failure_rate: 0.0,
    113
    0
            }
    114
    0
        }
    115
    116
        /// Relaxed thresholds for development
    117
        #[must_use]
    118
    0
        pub const fn relaxed() -> Self {
    119
    0
            Self {
    120
    0
                max_op_time_ms: 5000,
    121
    0
                max_memory_bytes: 512 * 1024 * 1024,
    122
    0
                max_timing_variance: 1.0,
    123
    0
                max_failure_rate: 0.01,
    124
    0
            }
    125
    0
        }
    126
    }
    127
    128
    /// Stress test result for a single operation
    129
    #[derive(Debug, Clone)]
    130
    pub struct StressResult {
    131
        /// Backend used
    132
        pub backend: Backend,
    133
        /// Input size
    134
        pub input_size: usize,
    135
        /// Cycles completed
    136
        pub cycles_completed: u32,
    137
        /// Total tests passed
    138
        pub tests_passed: u32,
    139
        /// Total tests failed
    140
        pub tests_failed: u32,
    141
        /// Mean operation time (ms)
    142
        pub mean_op_time_ms: f64,
    143
        /// Max operation time (ms)
    144
        pub max_op_time_ms: u64,
    145
        /// Timing variance (coefficient of variation)
    146
        pub timing_variance: f64,
    147
        /// Detected anomalies
    148
        pub anomalies: Vec<StressAnomaly>,
    149
    }
    150
    151
    impl StressResult {
    152
        /// Check if all tests passed
    153
        #[must_use]
    154
    0
        pub fn passed(&self) -> bool {
    155
    0
            self.tests_failed == 0 && self.anomalies.is_empty()
    156
    0
        }
    157
    158
        /// Calculate pass rate
    159
        #[must_use]
    160
    0
        pub fn pass_rate(&self) -> f64 {
    161
    0
            let total = self.tests_passed + self.tests_failed;
    162
    0
            if total == 0 {
    163
    0
                1.0
    164
            } else {
    165
    0
                self.tests_passed as f64 / total as f64
    166
            }
    167
    0
        }
    168
    }
    169
    170
    /// Anomaly detected during stress testing
    171
    #[derive(Debug, Clone)]
    172
    pub struct StressAnomaly {
    173
        /// Cycle where anomaly was detected
    174
        pub cycle: u32,
    175
        /// Type of anomaly
    176
        pub kind: StressAnomalyKind,
    177
        /// Description
    178
        pub description: String,
    179
    }
    180
    181
    /// Types of stress test anomalies
    182
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    183
    pub enum StressAnomalyKind {
    184
        /// Operation too slow
    185
        SlowOperation,
    186
        /// High memory usage
    187
        HighMemory,
    188
        /// Test failure
    189
        TestFailure,
    190
        /// Timing spike
    191
        TimingSpike,
    192
        /// Non-deterministic output
    193
        NonDeterministic,
    194
    }
    195
    196
    #[cfg(test)]
    197
    mod 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
            // 2 backends * 3 sizes * 10 cycles = 60 tests
    232
            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()); // Failed because tests_failed > 0
    296
            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()); // Failed because anomalies not empty
    318
        }
    319
    320
        #[test]
    321
        fn test_stress_anomaly_kinds() {
    322
            assert_eq!(
    323
                StressAnomalyKind::SlowOperation,
    324
                StressAnomalyKind::SlowOperation
    325
            );
    326
            assert_ne!(
    327
                StressAnomalyKind::SlowOperation,
    328
                StressAnomalyKind::TestFailure
    329
            );
    330
    331
            // Test all variants exist
    332
            let _slow = StressAnomalyKind::SlowOperation;
    333
            let _mem = StressAnomalyKind::HighMemory;
    334
            let _fail = StressAnomalyKind::TestFailure;
    335
            let _spike = StressAnomalyKind::TimingSpike;
    336
            let _ndet = StressAnomalyKind::NonDeterministic;
    337
        }
    338
    339
        #[test]
    340
        fn test_stress_result_zero_tests() {
    341
            let result = StressResult {
    342
                backend: Backend::Scalar,
    343
                input_size: 0,
    344
                cycles_completed: 0,
    345
                tests_passed: 0,
    346
                tests_failed: 0,
    347
                mean_op_time_ms: 0.0,
    348
                max_op_time_ms: 0,
    349
                timing_variance: 0.0,
    350
                anomalies: vec![],
    351
            };
    352
    353
            // Zero tests should still pass with pass_rate of 1.0
    354
            assert!(result.passed());
    355
            assert_eq!(result.pass_rate(), 1.0);
    356
        }
    357
    }