xtask/tests/silenced_tests_guard.rs
//! **This workspace must contain no silenced test file.**
//!
//! A `tests/*.rs` carrying a crate-level `#![cfg(feature = "…")]` (or a
//! `required-features` in the manifest) that the crate's `default` closure does
//! not satisfy compiles to an **empty test binary** — cargo prints
//! `running 0 tests … ok` for it, a green produced by looking at nothing. That is
//! LAW 2's exact failure mode and it is invisible in review: the file reads like
//! its running siblings. On a 71-crate GPU workspace that is a lot of surface to
//! hide a dead proof behind.
//!
//! `nornir_testmatrix::audit_repo` — the same serde-light engine draupnir, nornir,
//! knut, holger and modgunn already run — walks every `test` target of the
//! workspace via `cargo metadata`, resolves each crate's transitive `default`
//! feature closure, and returns one of four verdicts per gated file:
//! * **Default** — the gate is on by default, the file runs. Fine.
//! * **ViaEdge** — off in the crate's own `default`, but a sibling's dependency
//! edge turns it on, so `cargo test --workspace` DOES build it (e.g. the
//! `facett` umbrella's tests, green through facett-demo's map features).
//! Undeclared and one edge-edit from dark — surfaced, not failed.
//! * **Rescued** — off by default, but a declared arm in
//! `.nornir/testmatrix-arms.json` records that it was built and run. facett
//! already declares 55 such arms; this guard is what finally ENFORCES them —
//! until now the arms file was a note nothing read.
//! * **Silenced** — off by default, no edge turns it on, no arm rescues it: a
//! silent zero-tests file. This is the only verdict that fails the gate.
//!
//! Measured on `oden` 2026-08-27 at `origin/main`: **334 test targets scanned ·
//! 63 feature-gated · 55 rescued · 4 via-edge · 0 SILENCED — GREEN**. This file
//! keeps that zero at zero: a new gated `tests/*.rs` that is neither declared nor
//! reachable turns the whole `cargo test` RED, naming the file and its missing
//! feature. The count of silenced files can only ever fall.
//!
//! ## Why this file is not itself feature-gated
//!
//! A guard hidden behind the mechanism it polices would go dark exactly when a
//! feature is off — which is when it is needed. It carries **no** `#![cfg]`, and
//! its only extra dependency is the serde-only `nornir-testmatrix`
//! (a dev-dependency), so it runs on a plain `cargo test -p xtask` with the lean
//! default feature set and nothing else.
use std::path::{Path, PathBuf};
/// facett's workspace root. `xtask` is a member crate, so its manifest dir is
/// `<root>/xtask` — the root is one ancestor up. (Asserted below by the
/// non-vacuity check: a wrong root scans zero targets and fails loudly.)
fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(1)
.expect("xtask has a parent workspace directory")
.to_path_buf()
}
#[test]
fn this_workspace_has_no_silenced_test_files() {
let root = workspace_root();
let rep = nornir_testmatrix::audit_repo(&root, "self-audit").unwrap_or_else(|e| {
panic!(
"the silenced-test audit could not RUN at {} — that is a RED, not a skip: {e:#}",
root.display()
)
});
// Non-vacuity (LAW 2: check output, not exit code). A root that resolves but
// scans nothing reports "0 silenced" because it looked at nothing. facett has
// 300+ test targets; anything under a few dozen means the walk read the wrong
// tree (e.g. a member crate dir instead of the workspace root).
assert!(
rep.scanned > 50,
"the audit scanned only {} test targets under {} — facett has 300+, so the \
walk is reading the wrong tree and this guard proves nothing",
rep.scanned,
root.display()
);
assert!(
rep.is_green(),
"SILENCED TESTS — {}\n{}\nEach file above compiles to ZERO tests under a plain \
`cargo test` and no declared arm re-invokes it. Either wire it into a run \
(feature into `default`, or a sibling edge), or declare the arm you actually \
ran in .nornir/testmatrix-arms.json. Do NOT add an arm for a file you did not run.",
rep.summary(),
rep.silenced()
.iter()
.map(|f| format!(" · {}", f.message()))
.collect::<Vec<_>>()
.join("\n")
);
}