Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-demo/tests/snapshot.rs

//! **Screenshot PROOF** (LAW #4) — render the populated `facett-demo` to a real
//! PNG with egui_kittest's wgpu backend (RTX 4090 / lavapipe on this box) and
//! **compare it, pixel for pixel, against the committed golden**: the whole
//! showcase, themed with a striking palette, drawn from the one fake dataset.
//!
//! Until 2026-07-23 this test *overwrote* the golden and then asserted only
//! "≥ 1200×640 and > 5 KB" — a write-only image that could not fail, and did
//! silently absorb a label rename. The guard is now
//! [`facett_core::golden::assert_golden_within`]: any pixel moved by more than 2 channel
//! levels, a size change, or a **missing** golden fails, with `*.actual.png` /
//! `*.diff.png` left under `target/snapshot-failures/`. The renders are deterministic
//! per host; the 2-level bound absorbs driver AA rounding between hosts (measured at
//! the call site).
//!
//! Run just this: `cargo test -p facett-demo --test snapshot`
//! Bless an intended visual change: `UPDATE_SNAPSHOTS=1 cargo test -p facett-demo --test snapshot`
//! The golden lives at `facett-demo/tests/snapshots/facett_demo.png` (git-lfs).

use egui_kittest::Harness;

use facett::Theme;
use facett_demo::DemoState;

/// **The golden was blessed with the DEV-ID chip painted, so this run must paint it too.**
///
/// The chip is on by default in a debug build and off in a release one (it ships behind
/// `FACETT_DEVID=1`), so this golden — recorded in debug — could only ever match in debug.
/// MEASURED 2026-08-31: `cargo test --release --workspace` failed here with **2 044 of
/// 921 600 px (0.22 %), max channel delta 138** — one missing chip, and a red that says
/// nothing about the demo. Two goldens carried it, and two more facett-demo tests failed
/// the same way, so this is the file-by-file half of one profile bug.
///
/// Pinning the gate is what makes the golden mean the same thing in both profiles. The
/// alternative was re-blessing both PNGs without the chip, which costs the same and proves
/// less: the chip would then be in no golden at all. It does NOT weaken the profile rule —
/// `facett_core::devid::resolve_enabled`'s table and
/// `facett-app/tests/devid_release_profile.rs` (a real `--release` binary) still hold it,
/// and `gate_json` reports `source: "forced"` so a dump can never pass this off as the
/// profile's own answer.
fn force_devid_on() {
    facett_core::devid::force_gate(true);
}

#[test]
fn snapshot_populated_demo_to_png() {
    force_devid_on();
    // Build the whole headless demo (every Facet component, from the fake fleet)
    // and pin a striking palette + glow so the golden reads as "the showcase".
    let mut demo = DemoState::new();
    demo.deck.set_palette(2); // nordic-aurora
    demo.deck.fx_mut().glow = true;
    // Sanity: the demo really is populated before we render it.
    assert_eq!(demo.fleet.len(), facett_demo::data::FLEET);

    let mut harness = Harness::builder().with_size(egui::vec2(1280.0, 720.0)).build_ui(move |ui| {
        // Apply the same striking palette to the standard widgets too.
        facett::set_theme(ui.ctx(), Theme::nordic_aurora());
        ui.heading("facett-demo — every component, every palette");
        demo.deck.ui(ui);
    });

    // A few fixed frames so the virtualized grid/table, the 3D spin and the glow
    // settle. `run_steps` (not `run`) because the glow/3D request a repaint every
    // frame — they never reach a steady state, by design.
    harness.run_steps(4);

    let image = harness.render().expect("wgpu render produced an image");
    assert!(image.width() >= 1200 && image.height() >= 640, "rendered at the requested size");

    // ── THE GUARD ────────────────────────────────────────────────────────────
    // Compare, don't overwrite. A difference, a size change or a missing golden
    // all panic here with the evidence written under `target/snapshot-failures/`.
    let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots/facett_demo.png");
    // Within 2 channel levels, per the golden module's rule for a host that jitters (the
    // same bound and reasoning as facett-grid's snapshot.rs, 67a3dfb/8d13ba7).
    // MEASURED 2026-09-29 on oden's NVIDIA RTX 4090 (Vulkan, driver 595.91.07): 18 090 of
    // 921 600 pixels (1.96 %) differ from the committed golden with a max channel delta of
    // **2**, region x 2..1277 y 11..717; the render is byte-identical on main 537ea35,
    // 12264fd and this branch (actual PNG sha256 3d77f9aa8dee…). The diff image is magenta
    // only on glyph edges, graph edge strokes, node rings and panel outlines — driver
    // anti-aliasing rounding, no fill, layout or text change. The golden stays the blessed
    // one; a structural regression moves channels by tens to hundreds, far past 2.
    // RE-BLESSED 2026-10-02 (lane F) on loki's RTX 5080 (NVIDIA, Vulkan): lanes D/E changed the
    // deck (the two-level group tab bar, the graph tab's size + generator row) and no GPU ran
    // this test after them — 97 337 px (10.56 %), max delta 255, y 32..671 on the t14s, the
    // SAME render on main 61e619f (actual PNG sha256 b3e2036e…, byte-identical). The new
    // golden on the t14s Radeon 890M: 12 056 px (1.31 %) within 2.
    // RE-BLESSED 2026-10-04 on loki's RTX 5080 (NVIDIA 595.91.07, Vulkan), main 6a9bc66:
    // a05499e (lane G, 19 minutes after the lane-F bless) lifted the graph tab's generator
    // ceiling 1k -> 100k, so its row reads `100 1k [10k] 100k` (GenControl opens at
    // min(10 000, max)) where the golden had `100 [1k]`. 3 225 px (0.35 %), max delta 142,
    // ALL of it in x 217..532 y 104..121 — that row and nothing else; the 7 palette
    // goldens in mega_matrix moved by the same strip (3 222–3 226 px each).
    // RE-BLESSED 2026-10-04 on loki's RTX 5080 (NVIDIA 595.91.07, Vulkan) for the Falsett
    // sub-tab (facett-flow, PR #1): the "Grafer" group's tab strip gained `Falsett` after
    // `pipeline`. 5 698 px (0.62 %), max delta 222, ALL of it in x 12..1243 y 57..89 — the
    // tab strip and nothing else; the 7 palette goldens moved by the same strip (5 696–5 701 px).
    facett_core::golden::assert_golden_within(&image, &path, "facett_demo", 2);
}