crates/nornir-robotui/tests/watch_wgpu_is_an_alias.rs
//! **The `watch-wgpu` feature is an ALIAS, and this is the guard that keeps the
//! manifest and the prose from disagreeing about it again.**
//!
//! History: the `eframe` dependency line used to read `glow`, so `watch-wgpu` really
//! did pick a different renderer. LAW #4 moved the fleet to WebGPU and put `wgpu` on
//! that line UNCONDITIONALLY — which made `watch-wgpu` a feature that cannot change a
//! single byte of the build, while its own comment still described it as the
//! difference between a WebGPU window and an OpenGL one. Two writers of one fact: the
//! manifest was fixed and the sentences beside it were not.
//!
//! `watch-wgpu` is deliberately KEPT rather than deleted — korp
//! (`Cargo.toml`: `"nornir-robotui/watch-wgpu"`) and facett-demo
//! (`features = ["watch-wgpu"]`) both name it, and korp has a test that asserts this
//! manifest still declares it. Deleting it is a cross-repo change, not a leaf one. So
//! it stays, documented as the no-op alias it is, and these assertions make the alias
//! claim MECHANICAL: the moment the algebra stops matching the sentence, this refuses
//! by name instead of leaving a knife on the receipt that was never used.
//!
//! The whole oracle is the crate's own manifest, read at test time — not a rendered
//! doc string, so nothing that merely *echoes* the words can satisfy it.
use std::path::PathBuf;
fn manifest() -> toml::Value {
let p = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
let text = std::fs::read_to_string(&p).expect("read nornir-robotui/Cargo.toml");
toml::from_str(&text).expect("nornir-robotui/Cargo.toml parses as TOML")
}
fn feature(m: &toml::Value, name: &str) -> Vec<String> {
m.get("features")
.and_then(|f| f.get(name))
.unwrap_or_else(|| panic!("feature `{name}` is declared"))
.as_array()
.unwrap_or_else(|| panic!("feature `{name}` is a list"))
.iter()
.map(|v| v.as_str().expect("feature entry is a string").to_string())
.collect()
}
/// `dependencies.eframe.features` — the list that decides which renderer eframe can
/// even build. This is the OTHER writer of the renderer fact.
fn eframe_features(m: &toml::Value) -> Vec<String> {
m.get("dependencies")
.and_then(|d| d.get("eframe"))
.expect("the eframe dependency is declared")
.get("features")
.expect("the eframe dependency pins an explicit feature list")
.as_array()
.expect("eframe features is a list")
.iter()
.map(|v| v.as_str().expect("feature entry is a string").to_string())
.collect()
}
/// The load-bearing one. `watch-wgpu`'s entire effect over `watch` is `eframe/wgpu`,
/// and `wgpu` is already on the `eframe` line unconditionally — therefore
/// `watch-wgpu` resolves to exactly the same dependency graph as `watch`, and every
/// sentence that calls it a renderer CHOICE is false.
///
/// If `wgpu` ever leaves the `eframe` line, this reds: the alias claim would have
/// become false again and the feature's documentation has to change WITH the
/// manifest, in the same commit, not eight days later.
#[test]
fn watch_wgpu_adds_nothing_that_watch_does_not_already_carry() {
let m = manifest();
let watch_wgpu = feature(&m, "watch-wgpu");
let ef = eframe_features(&m);
assert!(
watch_wgpu.iter().any(|f| f == "watch"),
"`watch-wgpu` must imply `watch` (it is an alias of it), got {watch_wgpu:?}"
);
// Everything it adds beyond `watch` itself.
let extra: Vec<&String> = watch_wgpu.iter().filter(|f| *f != "watch").collect();
assert_eq!(
extra,
vec!["eframe/wgpu"],
"`watch-wgpu` is documented as a no-op alias of `watch`; its only extra entry \
may be `eframe/wgpu`, which the eframe line already carries. It now adds \
{extra:?} — either that entry is a REAL effect, in which case the feature is \
no longer an alias and its Cargo.toml comment plus mode.rs/watch.rs docs must \
say what it does, or it is dead weight."
);
assert!(
ef.iter().any(|f| f == "wgpu"),
"the `eframe` dependency must carry `wgpu` UNCONDITIONALLY (LAW #4: WebGPU \
everywhere) — that is what makes `watch-wgpu` an alias. eframe features are \
{ef:?}. If wgpu moved behind the feature, `watch-wgpu` is a real switch again \
and every doc calling it a no-op alias is now wrong."
);
}
/// LAW #4, made mechanical rather than left to prose: `glow` is not a feature of this
/// crate and not a feature this crate asks eframe for. `resolve_renderer` returns
/// `eframe::Renderer::Wgpu` unconditionally and `WatchRenderer` has one variant, so a
/// `glow` entry reappearing anywhere in the manifest would re-arm the exact defect
/// (`Renderer::default()` resolves to `Glow` whenever eframe's `glow` feature is on).
#[test]
fn no_glow_anywhere_in_this_crates_manifest() {
let m = manifest();
if let Some(features) = m.get("features").and_then(|f| f.as_table()) {
for (name, entries) in features {
assert!(
!name.contains("glow"),
"feature `{name}` names glow; LAW #4 — this fleet is WebGPU only"
);
let list = entries.as_array().cloned().unwrap_or_default();
for e in list {
let s = e.as_str().unwrap_or_default();
assert!(
!s.contains("glow"),
"feature `{name}` enables `{s}`; LAW #4 — no glow/WebGL"
);
}
}
}
assert!(
!eframe_features(&m).iter().any(|f| f.contains("glow")),
"the eframe dependency must never ask for `glow` — with it on, eframe's \
`Renderer::default()` is `Glow`, so the live watch window paints on OpenGL \
decided by one word in a manifest with nothing in the source saying so."
);
// `x11` is deliberately absent too (2026-08-07, Wayland-only) — same shape of
// by-construction refusal, worth pinning here rather than trusting the comment.
assert!(
!eframe_features(&m).iter().any(|f| f == "x11"),
"`x11` must stay off the eframe line: robot UI is Wayland-only, and NOT \
compiling the X11 backend is the fix by construction, not a runtime check."
);
}
/// The second way the stale alias bites: anything *gated on* `cfg(feature =
/// "watch-wgpu")` is gated on a condition indistinguishable from `watch`, so a
/// WebGPU-only path can be compiled out while the wgpu backend is nonetheless active
/// — with no compile error either way. While the feature is an alias, no such gate
/// may exist.
#[test]
fn nothing_is_cfg_gated_on_the_alias() {
let src = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src");
let mut offenders = Vec::new();
walk(&src, &mut |path, text| {
for (i, line) in text.lines().enumerate() {
// Only real cfg gates — a doc comment mentioning the name is prose.
let code = line.split("//").next().unwrap_or("");
if code.contains("feature = \"watch-wgpu\"") {
offenders.push(format!("{}:{}", path.display(), i + 1));
}
}
});
assert!(
offenders.is_empty(),
"`watch-wgpu` is an alias of `watch`, so a cfg gate on it compiles exactly \
when `watch` does — the gate reads as a choice and is not one. Found: {offenders:?}"
);
}
fn walk(dir: &std::path::Path, f: &mut impl FnMut(&std::path::Path, &str)) {
let Ok(rd) = std::fs::read_dir(dir) else { return };
for e in rd.flatten() {
let p = e.path();
if p.is_dir() {
walk(&p, f);
} else if p.extension().is_some_and(|x| x == "rs") {
if let Ok(t) = std::fs::read_to_string(&p) {
f(&p, &t);
}
}
}
}