Skip to main content

trueno_gpu/testing/
mod.rs

1//! E2E Visual Testing Framework for GPU Kernels
2//!
3//! This module provides pixel-level visual regression testing for GPU computations
4//! using the **sovereign stack only** - NO external crates.
5//!
6//! # Architecture (Sovereign Stack)
7//!
8//! ```text
9//! GPU Output → GpuPixelRenderer → trueno-viz → PNG → compare_png_bytes → Pass/Fail
10//!                                                           ↑
11//!                                                    Golden Baseline
12//! ```
13//!
14//! # Dependencies (Sovereign Stack)
15//!
16//! - `trueno-viz` v0.1.4: PNG encoding, Framebuffer (optional, feature = "viz")
17//! - `simular` v0.2.0: Deterministic RNG for reproducible tests
18//! - `renacer` v0.7.0: Profiling and anomaly detection (optional)
19//!
20//! # Features
21//!
22//! - `viz`: Enable GPU pixel renderer with trueno-viz
23//! - `stress-test`: Enable randomized frame-by-frame stress testing
24//! - `tui-monitor`: Enable TUI monitoring mode via presentar
25
26#[cfg(feature = "viz")]
27mod gpu_renderer;
28pub mod stress;
29pub mod tui;
30
31#[cfg(feature = "viz")]
32pub use gpu_renderer::{compare_png_bytes, ColorPalette, GpuPixelRenderer, PixelDiffResult, Rgb};
33
34pub use stress::{
35    verify_performance, Anomaly, AnomalyKind, FrameProfile, PerformanceResult,
36    PerformanceThresholds, StressConfig, StressReport, StressRng, StressTestRunner,
37};
38
39pub use tui::{progress_bar, render_to_string, TuiConfig, TuiState};
40
41/// GPU-specific bug classification based on diff patterns
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum BugClass {
44    /// Race condition: Non-deterministic output
45    RaceCondition,
46    /// Floating-point precision drift
47    FloatingPointDrift,
48    /// Accumulator not initialized to zero
49    AccumulatorInit,
50    /// Loop counter SSA bug
51    LoopCounter,
52    /// Memory addressing error
53    MemoryAddressing,
54    /// Thread synchronization issue
55    ThreadSync,
56    /// Unknown pattern
57    Unknown,
58}
59
60impl BugClass {
61    /// Description of the bug class
62    #[must_use]
63    pub const fn description(&self) -> &'static str {
64        match self {
65            Self::RaceCondition => "Race condition: non-deterministic output",
66            Self::FloatingPointDrift => "FP precision drift in accumulation",
67            Self::AccumulatorInit => "Accumulator not initialized to zero",
68            Self::LoopCounter => "Loop counter SSA bug (wrong iteration count)",
69            Self::MemoryAddressing => "Memory addressing error (offset/alignment)",
70            Self::ThreadSync => "Thread synchronization issue (barrier)",
71            Self::Unknown => "Unknown bug pattern",
72        }
73    }
74
75    /// Suggested fix for the bug class
76    #[must_use]
77    pub const fn suggested_fix(&self) -> &'static str {
78        match self {
79            Self::RaceCondition => "Add __syncthreads() / bar.sync; use atomics",
80            Self::FloatingPointDrift => "Use Kahan summation or pairwise reduction",
81            Self::AccumulatorInit => "Initialize accumulator to 0.0 before loop",
82            Self::LoopCounter => "Fix loop bound; use in-place += instead of reassignment",
83            Self::MemoryAddressing => "Check index calculations and stride",
84            Self::ThreadSync => "Add barrier synchronization at workgroup boundaries",
85            Self::Unknown => "Manual inspection required",
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_bug_class_descriptions() {
96        // Test all variants have non-empty descriptions
97        let variants = [
98            BugClass::RaceCondition,
99            BugClass::FloatingPointDrift,
100            BugClass::AccumulatorInit,
101            BugClass::LoopCounter,
102            BugClass::MemoryAddressing,
103            BugClass::ThreadSync,
104            BugClass::Unknown,
105        ];
106
107        for variant in variants {
108            assert!(
109                !variant.description().is_empty(),
110                "{variant:?} has empty description"
111            );
112            assert!(
113                !variant.suggested_fix().is_empty(),
114                "{variant:?} has empty fix"
115            );
116        }
117    }
118
119    #[test]
120    fn test_bug_class_equality() {
121        assert_eq!(BugClass::RaceCondition, BugClass::RaceCondition);
122        assert_ne!(BugClass::RaceCondition, BugClass::Unknown);
123    }
124
125    #[test]
126    fn test_bug_class_clone() {
127        let original = BugClass::FloatingPointDrift;
128        let cloned = original;
129        assert_eq!(original, cloned);
130    }
131}
132
133// Integration tests require viz feature for GpuPixelRenderer
134#[cfg(all(test, feature = "viz"))]
135mod integration_tests;