Skip to main content

trueno_gpu/testing/stress/
types.rs

1//! Core types for the stress testing framework.
2//!
3//! Contains frame profiles, reports, anomaly types, performance thresholds,
4//! and the `verify_performance` function.
5
6/// Frame profile data collected during stress testing
7#[derive(Debug, Clone, Default)]
8pub struct FrameProfile {
9    /// Cycle number
10    pub cycle: u32,
11    /// Duration in milliseconds
12    pub duration_ms: u64,
13    /// Memory usage estimate (bytes)
14    pub memory_bytes: usize,
15    /// Number of tests passed
16    pub tests_passed: u32,
17    /// Number of tests failed
18    pub tests_failed: u32,
19    /// Input seed used for this frame
20    pub input_seed: u64,
21    /// Input size used for this frame
22    pub input_size: usize,
23}
24
25/// Cumulative stress test report
26#[derive(Debug, Clone, Default)]
27pub struct StressReport {
28    /// All frame profiles
29    pub frames: Vec<FrameProfile>,
30    /// Total cycles completed
31    pub cycles_completed: u32,
32    /// Total tests passed across all cycles
33    pub total_passed: u32,
34    /// Total tests failed across all cycles
35    pub total_failed: u32,
36    /// Detected anomalies
37    pub anomalies: Vec<Anomaly>,
38}
39
40impl StressReport {
41    /// Calculate mean frame time in milliseconds
42    #[must_use]
43    pub fn mean_frame_time_ms(&self) -> f64 {
44        if self.frames.is_empty() {
45            return 0.0;
46        }
47        let sum: u64 = self.frames.iter().map(|f| f.duration_ms).sum();
48        sum as f64 / self.frames.len() as f64
49    }
50
51    /// Calculate timing variance (coefficient of variation)
52    #[must_use]
53    pub fn timing_variance(&self) -> f64 {
54        if self.frames.len() < 2 {
55            return 0.0;
56        }
57        let mean = self.mean_frame_time_ms();
58        if mean == 0.0 {
59            return 0.0;
60        }
61        let variance: f64 = self
62            .frames
63            .iter()
64            .map(|f| {
65                let diff = f.duration_ms as f64 - mean;
66                diff * diff
67            })
68            .sum::<f64>()
69            / self.frames.len() as f64;
70        variance.sqrt() / mean
71    }
72
73    /// Calculate max frame time
74    #[must_use]
75    pub fn max_frame_time_ms(&self) -> u64 {
76        self.frames.iter().map(|f| f.duration_ms).max().unwrap_or(0)
77    }
78
79    /// Calculate pass rate (0.0 to 1.0)
80    #[must_use]
81    pub fn pass_rate(&self) -> f64 {
82        let total = self.total_passed + self.total_failed;
83        if total == 0 {
84            return 1.0;
85        }
86        self.total_passed as f64 / total as f64
87    }
88
89    /// Add a frame to the report
90    pub fn add_frame(&mut self, profile: FrameProfile) {
91        self.total_passed += profile.tests_passed;
92        self.total_failed += profile.tests_failed;
93        self.cycles_completed += 1;
94        self.frames.push(profile);
95    }
96}
97
98/// Detected anomaly during stress testing
99#[derive(Debug, Clone)]
100pub struct Anomaly {
101    /// Cycle where anomaly was detected
102    pub cycle: u32,
103    /// Type of anomaly
104    pub kind: AnomalyKind,
105    /// Description
106    pub description: String,
107}
108
109/// Types of anomalies that can be detected
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum AnomalyKind {
112    /// Frame took longer than threshold
113    SlowFrame,
114    /// Memory usage exceeded threshold
115    HighMemory,
116    /// Test failure detected
117    TestFailure,
118    /// Timing variance spike
119    TimingSpike,
120    /// Non-deterministic behavior
121    NonDeterministic,
122}
123
124/// Performance thresholds for anomaly detection
125#[derive(Debug, Clone)]
126pub struct PerformanceThresholds {
127    /// Max time per frame (ms)
128    pub max_frame_time_ms: u64,
129    /// Max memory per frame (bytes)
130    pub max_memory_bytes: usize,
131    /// Max variance in frame times (coefficient of variation)
132    pub max_timing_variance: f64,
133    /// Max allowed failure rate (0.0 to 1.0)
134    pub max_failure_rate: f64,
135}
136
137impl Default for PerformanceThresholds {
138    fn default() -> Self {
139        Self {
140            max_frame_time_ms: 100,             // 10 FPS minimum
141            max_memory_bytes: 64 * 1024 * 1024, // 64MB max
142            max_timing_variance: 0.2,           // 20% max variance
143            max_failure_rate: 0.01,             // 1% max failures
144        }
145    }
146}
147
148/// Performance verification result
149#[derive(Debug, Clone)]
150pub struct PerformanceResult {
151    /// Whether all thresholds passed
152    pub passed: bool,
153    /// Max frame time observed
154    pub max_frame_ms: u64,
155    /// Mean frame time observed
156    pub mean_frame_ms: f64,
157    /// Timing variance observed
158    pub variance: f64,
159    /// Pass rate observed
160    pub pass_rate: f64,
161    /// List of threshold violations
162    pub violations: Vec<String>,
163}
164
165/// Verify performance against thresholds
166#[must_use]
167pub fn verify_performance(
168    report: &StressReport,
169    thresholds: &PerformanceThresholds,
170) -> PerformanceResult {
171    let max_frame = report.max_frame_time_ms();
172    let mean_frame = report.mean_frame_time_ms();
173    let variance = report.timing_variance();
174    let pass_rate = report.pass_rate();
175
176    let mut violations = Vec::new();
177
178    if max_frame > thresholds.max_frame_time_ms {
179        violations.push(format!(
180            "Max frame time {}ms exceeds threshold {}ms",
181            max_frame, thresholds.max_frame_time_ms
182        ));
183    }
184
185    if variance > thresholds.max_timing_variance {
186        violations.push(format!(
187            "Timing variance {:.3} exceeds threshold {:.3}",
188            variance, thresholds.max_timing_variance
189        ));
190    }
191
192    if pass_rate < (1.0 - thresholds.max_failure_rate) {
193        violations.push(format!(
194            "Pass rate {:.1}% below threshold {:.1}%",
195            pass_rate * 100.0,
196            (1.0 - thresholds.max_failure_rate) * 100.0
197        ));
198    }
199
200    PerformanceResult {
201        passed: violations.is_empty(),
202        max_frame_ms: max_frame,
203        mean_frame_ms: mean_frame,
204        variance,
205        pass_rate,
206        violations,
207    }
208}