golden.rs raw

//! Golden-image test harness, shared by every crate with rendering tests.
//!
//! `check` compares a rendered pixmap against `<dir>/<name>.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"
        )
    });

    // `<crate>/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(),
            );
        }
    }
}