//! Golden-image test harness, shared by every crate with rendering tests. //! //! `check` compares a rendered pixmap against `/.png` exactly. //! With `GUIDUCK_BLESS` set, it (re)writes the golden instead. On mismatch //! the actual render lands in `target/golden-failures/` and the check //! panics with the diff statistics. use std::path::Path; use crate::diff::{DiffTolerance, compare}; /// Goldens come from the same deterministic renderer, so they must /// reproduce exactly. const TOLERANCE: DiffTolerance = DiffTolerance::EXACT; pub fn check(name: &str, actual: &tiny_skia::Pixmap, golden_dir: &Path) { let golden_path = golden_dir.join(format!("{name}.png")); if std::env::var_os("GUIDUCK_BLESS").is_some() { std::fs::create_dir_all(golden_dir).expect("create goldens dir"); actual.save_png(&golden_path).expect("write golden"); return; } let golden = tiny_skia::Pixmap::load_png(&golden_path).unwrap_or_else(|e| { panic!( "missing or unreadable golden {golden_path:?} ({e}); \ run with GUIDUCK_BLESS=1 to create it" ) }); // `/tests/goldens` → workspace root → `target/golden-failures`. let failure_dir = golden_dir .ancestors() .nth(4) .unwrap_or_else(|| Path::new(".")) .join("target/golden-failures"); match compare(&golden, actual, TOLERANCE) { Some((_, true)) => {} Some((stats, false)) => { std::fs::create_dir_all(&failure_dir).ok(); let actual_path = failure_dir.join(format!("{name}-actual.png")); actual.save_png(&actual_path).ok(); panic!( "{name} differs from golden: {:.4}% of pixels (max channel diff {}); \ actual written to {actual_path:?}", stats.differing_fraction * 100.0, stats.max_channel_diff, ); } None => { std::fs::create_dir_all(&failure_dir).ok(); let actual_path = failure_dir.join(format!("{name}-actual.png")); actual.save_png(&actual_path).ok(); panic!( "{name} has wrong dimensions: golden {}x{}, actual {}x{}; \ actual written to {actual_path:?}", golden.width(), golden.height(), actual.width(), actual.height(), ); } } }