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

nordisk/facett

facett-app/tests/devid_release_profile.rs

//! **The DEV-ID chip, proven on a RELEASE build.** The one hole no `#[test]` could cover.
//!
//! # The hole
//!
//! `debug_assertions` is decided at compile time and `cargo test` always compiles with it
//! **on**. Every other devid test — the unit table in `facett-core/src/devid.rs`, the
//! AccessKit assertions in `devid_scene_slots.rs`, facett-demo's deck tests — runs in that
//! one profile. So when the badge was gated on `#[cfg(debug_assertions)]` alone and
//! `badge` compiled to an *empty function body* in every installed `--release` binary,
//! five green tests said nothing was wrong. The tests were not weak; they were structurally
//! incapable of seeing it.
//!
//! The only way out is to leave the profile: build a real release binary and inspect it.
//!
//! # What this asserts
//!
//! `cargo build --release --bin devid_release_probe` (see
//! `facett-app/src/bin/devid_release_probe.rs`), then runs it twice:
//!
//! | run | `enabled()` | dev-id atoms in the AccessKit tree |
//! |---|---|---|
//! | `FACETT_DEVID` unset | `false` | 0 |
//! | `FACETT_DEVID=1` | `true` | ≥ 1, reading `korp/map_canvas` |
//!
//! The probe renders through [`facett_app::scene::render`] — the container korp actually
//! calls — so the assertion is on **applied output**: an atom really in the tree, in a
//! process where `debug_assertions` is really off. Every run also checks the probe's own
//! `debug_assertions` field is `false`; without that, a mis-wired build would inspect a
//! debug binary and pass for the wrong reason.
//!
//! # Cost
//!
//! One release compile of `facett-core` + `facett-app` + `egui` the first time (~1 min on
//! oden, seconds when warm). It reuses the caller's `--target-dir`, so `cargo test
//! -p facett-app --target-dir X` puts the release artifacts in `X/release`. That is the
//! price of the only check in the suite that can see this class of bug, and it is not
//! `#[ignore]`d on purpose — an ignored guard is a green that was never run.
//!
//! ```bash
//! cargo test -p facett-app --test devid_release_profile
//! ```

use std::path::{Path, PathBuf};
use std::process::Command;

/// The target dir the *caller* is using — derived from this test binary's own path
/// (`<target>/debug/deps/devid_release_profile-<hash>`), so `--target-dir` is honoured
/// automatically and the release build shares the caller's dependency artifacts instead
/// of starting a second tree from scratch.
fn target_dir() -> PathBuf {
    let exe = std::env::current_exe().expect("test binary has a path");
    // deps/ -> debug/ -> <target>
    exe.ancestors().nth(3).expect("<target>/debug/deps/<exe>").to_path_buf()
}

fn cargo() -> String {
    std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
}

fn manifest() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml")
}

/// Build the probe with `--release` and hand back the path to the binary.
fn build_release_probe(target: &Path) -> PathBuf {
    let out = Command::new(cargo())
        .args([
            "build",
            "--release",
            "--bin",
            "devid_release_probe",
            "--features",
            "devid-probe",
            "--manifest-path",
        ])
        .arg(manifest())
        .arg("--target-dir")
        .arg(target)
        // The probe must decide its gate from the env WE give it, never from whatever
        // the developer's shell (or a running korp-ui) happens to export.
        .env_remove("FACETT_DEVID")
        .output()
        .expect("cargo is runnable");
    assert!(
        out.status.success(),
        "release build of devid_release_probe FAILED:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );
    let bin = target.join("release").join("devid_release_probe");
    assert!(bin.is_file(), "cargo reported success but {} does not exist", bin.display());
    bin
}

/// Run the probe and parse its one JSON line.
fn run_probe(bin: &Path, devid: Option<&str>) -> serde_json::Value {
    let mut cmd = Command::new(bin);
    cmd.env_remove("FACETT_DEVID");
    if let Some(v) = devid {
        cmd.env("FACETT_DEVID", v);
    }
    let out = cmd.output().expect("the probe runs");
    assert!(
        out.status.success(),
        "probe exited {:?}:\n{}",
        out.status.code(),
        String::from_utf8_lossy(&out.stderr)
    );
    let stdout = String::from_utf8_lossy(&out.stdout);
    let line = stdout.lines().last().unwrap_or_default();
    serde_json::from_str(line).unwrap_or_else(|e| panic!("probe printed no JSON ({e}): {stdout:?}"))
}

#[test]
fn a_release_build_paints_the_chip_when_facett_devid_is_set() {
    let target = target_dir();
    let bin = build_release_probe(&target);

    // ── The control: a release build with nothing set shows nothing. ─────────────
    let off = run_probe(&bin, None);
    assert_eq!(
        off["debug_assertions"], false,
        "the probe was NOT a release build — this whole test would then be asserting on \
         the same profile as every other test and could not see the bug it exists for. \
         Got {off}"
    );
    assert_eq!(off["enabled"], false, "release + FACETT_DEVID unset must be off: {off}");
    assert_eq!(
        off["devid_atoms"], 0,
        "a release build with the gate closed still painted a chip: {off}"
    );

    // ── The case that matters: the escape hatch must actually reach the pixels. ──
    let on = run_probe(&bin, Some("1"));
    assert_eq!(on["debug_assertions"], false, "not a release build: {on}");
    assert_eq!(
        on["enabled"], true,
        "FACETT_DEVID=1 did not switch the chip on in a RELEASE build — the aid is then \
         invisible in every binary that ships: {on}"
    );
    // The APPLIED output, not the gate: an atom really in the AccessKit tree of a real
    // `scene::render` pass. This is what `badge` compiling to an empty body would break,
    // and what no debug-profile test can observe.
    assert!(
        on["devid_atoms"].as_u64().unwrap_or(0) >= 1,
        "the gate said ON but NO dev-id atom reached the rendered tree — `badge` drew \
         nothing in the release build: {on}"
    );
    let labels = on["labels"].as_array().cloned().unwrap_or_default();
    assert!(
        labels.iter().any(|l| l.as_str().unwrap_or_default().contains("korp/map_canvas")),
        "the chip is there but does not name the pane (expected `korp/map_canvas`): {on}"
    );
    // A chip identical on every pane is no better than no chip; the registered form is
    // `component/slot`, and that is what the release build must produce.
    assert_eq!(on["badge_text"], "korp/map_canvas", "wrong chip text in release: {on}");
}