Skip to main content

jugar_probar/pixel_coverage/
mod.rs

1//! Pixel-Level GUI Coverage Visualization (Advanced Feature A)
2//!
3//! Grid-based coverage tracking that renders heatmaps showing which
4//! screen regions have been exercised by tests. Identifies untested
5//! visual regions between UI elements.
6
7mod config;
8mod falsification;
9mod heatmap;
10mod metrics;
11mod parallel;
12mod terminal;
13mod tracker;
14mod wasm_demo;
15
16pub use config::{
17    ConfigValidationError, OutputConfig, PerformanceConfig, PixelCoverageConfig, ThresholdConfig,
18    VerificationConfig,
19};
20pub use falsification::{
21    ComparisonOperator, FalsifiabilityGate, FalsifiableHypothesis, FalsifiableHypothesisBuilder,
22    FalsificationCondition, FalsificationLayer, GateResult,
23};
24pub use heatmap::{
25    BitmapFont, ColorPalette, HeatmapRenderer, PngHeatmap, Rgb, StatsPanel, TerminalHeatmap,
26};
27pub use metrics::{
28    CieDe2000Metric, DeltaEClassification, DeltaEResult, Lab, PerceptualHash, PhashAlgorithm,
29    PixelVerificationResult, PixelVerificationSuite, PsnrMetric, PsnrQuality, PsnrResult,
30    SsimMetric, SsimResult,
31};
32pub use parallel::{
33    BatchProcessor, DeltaEBatchResult, Downscaler, HashCache, ParallelContext, SsimBatchResult,
34};
35pub use terminal::{
36    ansi, ConfidenceInterval, CoverageHypothesis, GapRegion, OutputMode, RichTerminalHeatmap,
37    ScoreBar,
38};
39pub use tracker::{
40    CombinedCoverageReport, CoverageCell, GridConfig, LineCoverageReport, PixelCoverageReport,
41    PixelCoverageTracker, Point as PixelPoint, Region as PixelRegion,
42};
43pub use wasm_demo::{
44    wilson_confidence_interval, ConfigError as WasmDemoConfigError,
45    CoverageStats as WasmCoverageStats, DemoGapRegion, DemoPalette, GapSeverity as DemoGapSeverity,
46    GpuPixelBuffer, PcgRng, WasmDemoConfig, WasmPixelDemo,
47};
48
49/// Coverage threshold presets
50pub mod thresholds {
51    /// Minimum acceptable coverage (60%)
52    pub const MINIMUM: f32 = 0.60;
53    /// Standard coverage target (80%)
54    pub const STANDARD: f32 = 0.80;
55    /// High coverage target (90%)
56    pub const HIGH: f32 = 0.90;
57    /// Complete coverage (100%)
58    pub const COMPLETE: f32 = 1.0;
59}
60
61#[cfg(test)]
62#[allow(
63    clippy::unwrap_used,
64    clippy::expect_used,
65    clippy::float_cmp,
66    clippy::assertions_on_constants
67)]
68mod tests {
69    use super::tracker::{Point, Region};
70    use super::*;
71
72    // =========================================================================
73    // H₀-PIX-01: PixelCoverageTracker creation
74    // =========================================================================
75
76    #[test]
77    fn h0_pix_01_tracker_creation() {
78        let tracker = PixelCoverageTracker::new(1920, 1080, 64, 36);
79        assert_eq!(tracker.resolution(), (1920, 1080));
80        assert_eq!(tracker.grid_size(), (64, 36));
81    }
82
83    #[test]
84    fn h0_pix_02_tracker_builder() {
85        let tracker = PixelCoverageTracker::builder()
86            .resolution(1280, 720)
87            .grid_size(32, 18)
88            .threshold(0.75)
89            .build();
90
91        assert_eq!(tracker.resolution(), (1280, 720));
92        assert_eq!(tracker.grid_size(), (32, 18));
93        assert!((tracker.threshold() - 0.75).abs() < f32::EPSILON);
94    }
95
96    // =========================================================================
97    // H₀-PIX-03: Coverage recording
98    // =========================================================================
99
100    #[test]
101    fn h0_pix_03_record_interaction() {
102        let mut tracker = PixelCoverageTracker::new(100, 100, 10, 10);
103
104        // Record interaction at (50, 50) - should affect cell (5, 5)
105        tracker.record_interaction(Point::new(50, 50));
106
107        let report = tracker.generate_report();
108        assert!(report.overall_coverage > 0.0);
109    }
110
111    #[test]
112    fn h0_pix_04_record_region() {
113        let mut tracker = PixelCoverageTracker::new(100, 100, 10, 10);
114
115        // Record a region covering multiple cells
116        tracker.record_region(Region::new(0, 0, 50, 50));
117
118        let report = tracker.generate_report();
119        // Should cover 25% of the grid (5x5 cells out of 10x10)
120        assert!(report.overall_coverage >= 0.20);
121    }
122
123    // =========================================================================
124    // H₀-PIX-05: Coverage report
125    // =========================================================================
126
127    #[test]
128    fn h0_pix_05_report_empty_tracker() {
129        let tracker = PixelCoverageTracker::new(100, 100, 10, 10);
130        let report = tracker.generate_report();
131
132        assert_eq!(report.overall_coverage, 0.0);
133        assert!(!report.meets_threshold);
134    }
135
136    #[test]
137    fn h0_pix_06_report_full_coverage() {
138        let mut tracker = PixelCoverageTracker::new(100, 100, 10, 10);
139
140        // Cover entire screen
141        tracker.record_region(Region::new(0, 0, 100, 100));
142
143        let report = tracker.generate_report();
144        assert_eq!(report.overall_coverage, 1.0);
145        assert!(report.meets_threshold);
146    }
147
148    // =========================================================================
149    // H₀-PIX-07: Uncovered regions
150    // =========================================================================
151
152    #[test]
153    fn h0_pix_07_find_uncovered_regions() {
154        let mut tracker = PixelCoverageTracker::new(100, 100, 10, 10);
155
156        // Cover only top-left quadrant
157        tracker.record_region(Region::new(0, 0, 50, 50));
158
159        let uncovered = tracker.uncovered_regions();
160        assert!(!uncovered.is_empty());
161    }
162
163    // =========================================================================
164    // H₀-PIX-08: Heatmap rendering
165    // =========================================================================
166
167    #[test]
168    fn h0_pix_08_terminal_heatmap() {
169        let mut tracker = PixelCoverageTracker::new(100, 100, 10, 10);
170        tracker.record_region(Region::new(0, 0, 50, 50));
171
172        let heatmap = tracker.terminal_heatmap();
173        let rendered = heatmap.render();
174
175        assert!(!rendered.is_empty());
176        assert!(rendered.contains('█') || rendered.contains('░'));
177    }
178
179    // =========================================================================
180    // H₀-PIX-09: Point and Region types
181    // =========================================================================
182
183    #[test]
184    fn h0_pix_09_point_creation() {
185        let point = Point::new(100, 200);
186        assert_eq!(point.x, 100);
187        assert_eq!(point.y, 200);
188    }
189
190    #[test]
191    fn h0_pix_10_region_creation() {
192        let region = Region::new(10, 20, 30, 40);
193        assert_eq!(region.x, 10);
194        assert_eq!(region.y, 20);
195        assert_eq!(region.width, 30);
196        assert_eq!(region.height, 40);
197    }
198
199    #[test]
200    fn h0_pix_11_region_contains() {
201        let region = Region::new(10, 10, 50, 50);
202
203        assert!(region.contains(Point::new(20, 20)));
204        assert!(region.contains(Point::new(10, 10))); // Edge case
205        assert!(!region.contains(Point::new(5, 5))); // Outside
206        assert!(!region.contains(Point::new(70, 70))); // Outside
207    }
208
209    // =========================================================================
210    // H₀-PIX-12: Grid configuration
211    // =========================================================================
212
213    #[test]
214    fn h0_pix_12_grid_cell_size() {
215        let tracker = PixelCoverageTracker::new(640, 480, 64, 48);
216        let config = tracker.grid_config();
217
218        assert_eq!(config.cell_width(), 10); // 640 / 64
219        assert_eq!(config.cell_height(), 10); // 480 / 48
220    }
221
222    // =========================================================================
223    // H₀-PIX-13: Threshold presets
224    // =========================================================================
225
226    #[test]
227    fn h0_pix_13_threshold_presets() {
228        assert!(thresholds::MINIMUM < thresholds::STANDARD);
229        assert!(thresholds::STANDARD < thresholds::HIGH);
230        assert!(thresholds::HIGH < thresholds::COMPLETE);
231    }
232}