facett-app/tests/devid_scene_slots.rs
//! **Every scene-hosted pane paints its DEV-ID** — proven off the live AccessKit tree.
//!
//! `facett-core`'s `devid` unit tests prove the text is right, and facett-demo's prove
//! the `FacetDeck` path paints. Neither covers `facett_app::scene`, which is the
//! container korp and the other scene-based apps actually use — a badge wired only into
//! the deck would show nothing there while every existing test stayed green. That is
//! exactly how the first version of this feature shipped (wired into `Deck::pane_ui`,
//! a path facett-demo never calls).
//!
//! ```bash
//! cargo test -p facett-app --test devid_scene_slots
//! cargo test --release -p facett-app --test devid_scene_slots # ← must be green too
//! ```
//!
//! # Why every test here opens the gate itself
//!
//! The chip is off by default in a release build (see [`facett_core::devid`]: the aid
//! ships behind `FACETT_DEVID=1`). This file is about the CONTAINER — does
//! `scene::render` paint a chip per pane, does it name the right pane, does it mark the
//! focused one — and that question has the same answer in both profiles. But the
//! assertions could only observe it while the gate happened to be open, so a plain
//! `cargo test --release --workspace` turned **4 of these 5 tests red** (measured
//! 2026-08-31) for a reason that had nothing to do with the scene container. A red
//! nobody can act on is a red nobody reads.
//!
//! So each test calls [`force_devid_on`] first. The alternatives were both worse: a
//! crate-level `#![cfg(debug_assertions)]` makes this file compile to zero tests in
//! exactly the profile that ships, and making each assertion conditional on
//! `devid::enabled()` replaces four positive proofs with "assert nothing when the gate
//! is closed" — a green produced by looking at nothing.
//!
//! Forcing does **not** hide the profile default, which is the bug that shipped once
//! already. That rule keeps its own two guards, neither of which this file touches:
//! `facett_core::devid`'s `resolve_enabled` table, and `tests/devid_release_profile.rs`,
//! which builds a real `--release` binary and proves the chip stays off until the env
//! hatch opens it.
use egui_kittest::kittest::{By, Queryable};
use egui_kittest::Harness;
use facett_app::scene::{self, Axis, NavState, Pane, Scene, SceneHost};
use facett_core::devid::{DEVID_ATOM, UNREGISTERED};
use serde_json::Value;
/// Open the dev-id gate for this test binary, whatever profile it was compiled in.
///
/// `devid::force_gate` panics if the gate was already decided as something else, so this
/// cannot quietly leave a test asserting against a closed gate — and every caller in this
/// file asks for the same value, so the order libtest runs them in does not matter.
fn force_devid_on() {
facett_core::devid::force_gate(true);
}
/// A host that declares a REGISTERED component (`korp` is in the errcode registry).
struct Korpish;
impl SceneHost for Korpish {
fn slot(&mut self, id: &str, ui: &mut egui::Ui) -> Value {
ui.label(format!("content of {id}"));
Value::Null
}
fn component(&self) -> &'static str {
"korp"
}
}
/// A host that declares NOTHING — must render the honest marker, never a plausible id.
struct Undeclared;
impl SceneHost for Undeclared {
fn slot(&mut self, id: &str, ui: &mut egui::Ui) -> Value {
ui.label(format!("content of {id}"));
Value::Null
}
}
fn scene_with_two_slots() -> Scene {
Scene::new(
"devid-test",
Pane::split(
"root",
Axis::Horizontal,
0.5,
vec![
Pane::slot("ingest", "ingest_load").title("Ingest"),
Pane::slot("map", "map").title("Map"),
],
),
)
}
#[test]
fn a_declared_host_paints_component_slash_slot_on_every_pane() {
force_devid_on();
let scene = scene_with_two_slots();
let mut nav = NavState::new(&scene);
let mut host = Korpish;
let mut harness = Harness::new_ui(move |ui| {
scene::render(&scene, &mut nav, ui, &mut host);
});
harness.run();
let any = harness.query_all(By::new().label_contains(DEVID_ATOM)).count();
assert!(any > 0, "no dev-id atom — the scene slot path does not paint the badge");
// Each pane must be tellable apart. A component-only chip would be identical on
// both, which is no more use than no chip at all.
for slot in ["ingest_load", "map"] {
let n = harness.query_all(By::new().label_contains(&format!("korp/{slot}"))).count();
assert!(n > 0, "no chip reading korp/{slot}");
}
assert_eq!(
harness.query_all(By::new().label_contains(UNREGISTERED)).count(),
0,
"a REGISTERED host rendered the unregistered marker"
);
}
#[test]
fn an_undeclared_host_is_marked_not_given_a_plausible_id() {
force_devid_on();
let scene = scene_with_two_slots();
let mut nav = NavState::new(&scene);
let mut host = Undeclared;
let mut harness = Harness::new_ui(move |ui| {
scene::render(&scene, &mut nav, ui, &mut host);
});
harness.run();
assert!(
harness.query_all(By::new().label_contains(UNREGISTERED)).count() > 0,
"a host with no component must say so, not invent an id"
);
}
/// **A RELEASE build must be able to show the chips.**
///
/// The badge was gated on `debug_assertions` alone, so the installed release binaries —
/// the ones actually run on this fleet — painted nothing while every test above stayed
/// green. The tests all ran in debug. A debugging aid that only exists in builds nobody
/// runs is not a debugging aid, and no test could see the gap because the tests are the
/// one place `debug_assertions` is always true.
///
/// This asserts the ESCAPE HATCH exists and that the gate honours it in both
/// directions, which is the part a debug-only test can still prove.
///
/// **It used to prove nothing.** The earlier body read
/// `let parsed = matches!(v, "1" | "true" | "on" | "yes"); assert_eq!(parsed, want)` —
/// it re-implemented the rule inside the test and then compared its own copy against the
/// expectation, never touching `devid`. Deleting the entire gate from `devid.rs` would
/// have left it green. It now calls the SHIPPED decision,
/// [`facett_core::devid::resolve_enabled`], with `debug_assertions = false` — the branch
/// `cargo test` can otherwise never reach.
#[test]
fn the_chip_can_be_forced_on_or_off_regardless_of_build_profile() {
use facett_core::devid::resolve_enabled;
// The PROFILE DEFAULT — debug shows the chip without being asked. It is asserted on
// `resolve_enabled`, not on `enabled()`, for two reasons: `debug_assertions` is a
// compile-time fact so `enabled()` can only ever report the profile this binary was
// built in, and this binary forces the gate open (module doc) so `enabled()` would be
// true here by construction — a line that cannot fail. `enabled()` is a one-liner
// over this function, so there is still exactly one copy of the rule.
assert!(resolve_enabled(None, true), "debug + unset: the chip is on by default");
// RELEASE profile — the build that ships. Off unless asked, on when asked.
assert!(!resolve_enabled(None, false), "release + unset: the chip stays off");
for v in ["1", "true", "on", "yes"] {
assert!(resolve_enabled(Some(v), false), "FACETT_DEVID={v} must open the release hatch");
}
// DEBUG profile — on by default, and a developer can still silence it.
for v in ["0", "false", "off", "no"] {
assert!(!resolve_enabled(Some(v), true), "FACETT_DEVID={v} must silence it in debug");
}
}
/// **The gate must be readable WITHOUT a screenshot.**
///
/// When the chips did not appear, nobody could tell from outside the process whether the
/// gate was even open — so the diagnosis ran on inference for three rounds. `render`'s
/// `state_json` now carries `devid`, which is what korp's `$KORP_STATE` dump exposes.
///
/// This is a narrowing tool, not a verdict: `enabled: true` says the switch is on, and
/// both earlier misses (badge wired into a path nobody calls; badge absent from this very
/// container) would ALSO have reported `enabled: true`. Only looking at the window closes
/// that gap — see `tests/devid_release_profile.rs` for the assertion on applied output.
#[test]
fn the_scene_state_reports_the_devid_gate() {
force_devid_on();
let scene = scene_with_two_slots();
let mut nav = NavState::new(&scene);
let mut host = Korpish;
let mut harness = Harness::new_ui(move |ui| {
let state = scene::render(&scene, &mut nav, ui, &mut host);
ui.data_mut(|d| d.insert_temp(egui::Id::new("devid-state"), state.to_string()));
});
harness.run();
let dumped: String =
harness.ctx.data_mut(|d| d.get_temp(egui::Id::new("devid-state")).unwrap_or_default());
let state: Value = serde_json::from_str(&dumped).expect("render returned JSON");
let gate = &state["devid"];
assert!(!gate.is_null(), "scene state_json carries no `devid` gate: {state:#}");
assert_eq!(
gate["enabled"],
Value::Bool(facett_core::devid::enabled()),
"the scene reports a gate that disagrees with the one badge() obeys: {gate}"
);
assert_eq!(
gate["debug_assertions"],
Value::Bool(cfg!(debug_assertions)),
"the reported build profile is not this binary's: {gate}"
);
assert!(gate.get("env").is_some(), "`env` must be present (null when unset): {gate}");
// `forced` is the third legal value and the one this binary produces (see the module
// doc). It has to be its own answer: a forced gate reporting `profile` would be a
// dump asserting this build's profile said something it did not.
assert_eq!(
gate["source"], "forced",
"this binary forces the gate, so the dump must SAY forced — not launder it as \
the profile's own answer: {gate}"
);
assert!(
matches!(gate["source"].as_str(), Some("env" | "profile" | "forced")),
"`source` must name who decided the gate: {gate}"
);
}
/// **Which pane is the robot in?** — the focus mark, asserted off the tree.
///
/// The owner's ask was to make the chip flash when its pane has focus, "nice for robot
/// tests". A visible highlight helps a human watching a drive, but the useful half is that
/// a driver can ASK — `label_contains("[focused]")` answers "which pane am I in" instead of
/// inferring it from whatever it clicked last.
///
/// Deliberately pointer-containment, not an animation: the render oracle compares snapshot
/// images, so anything time-based would make every snapshot differ from the last and the
/// oracle would be worthless. A scripted pointer is deterministic.
#[test]
fn the_focused_pane_says_so_in_the_tree_and_the_others_do_not() {
use facett_core::devid::FOCUSED_MARK;
force_devid_on();
let scene = scene_with_two_slots();
let mut nav = NavState::new(&scene);
let mut host = Korpish;
let mut harness = Harness::builder()
.with_size(egui::vec2(800.0, 600.0))
.build_ui(move |ui| {
scene::render(&scene, &mut nav, ui, &mut host);
});
harness.run();
// No pointer anywhere ⇒ nothing claims focus. A chip that were always focused would
// pass the positive half below while telling a driver nothing.
harness.remove_cursor();
harness.run();
assert_eq!(
harness.query_all(By::new().label_contains(FOCUSED_MARK)).count(),
0,
"with no pointer on screen, no pane may claim focus"
);
// Pointer into the LEFT pane (the scene is a 0.5 horizontal split, so x=100 is left).
harness.hover_at(egui::pos2(100.0, 300.0));
harness.run();
let focused: usize = harness.query_all(By::new().label_contains(FOCUSED_MARK)).count();
assert!(focused > 0, "the pane under the pointer must carry {FOCUSED_MARK}");
// And it must be the INGEST one, not the map one — a mark on every chip is no signal.
let ingest_focused = harness
.query_all(By::new().label_contains(&format!("korp/ingest_load {FOCUSED_MARK}")))
.count();
assert!(ingest_focused > 0, "the LEFT pane (ingest_load) is the one under the pointer");
let map_focused =
harness.query_all(By::new().label_contains(&format!("korp/map {FOCUSED_MARK}"))).count();
assert_eq!(map_focused, 0, "the pane the pointer is NOT in must not claim focus");
}