facett-core/tests/webgl_free_law.rs
//! **LAW 4 for the facett workspace: modern backends only — no GL, anywhere.**
//!
//! Decision 0 (`1fca337`) dropped `Backends::GL` from the runtime mask and
//! Decision 0b (`39dcddc`) purged the `glow` dev-deps, but until 2026-08-22 every
//! SHIPPED binary still *compiled* wgpu-hal's GLES backend, `glow` (54k LOC,
//! 6.9 MB rlib) and `khronos-egl` — because the direct `wgpu` deps carried
//! wgpu's default features (which include `gles`) and every eframe host's `wgpu`
//! feature expanded to `egui-wgpu/default` → `wgpu/default` + `wgpu/webgl`.
//! GFX_V2 V2.8 measured it; the owner decided: flip it. The workspace now has ONE
//! `wgpu` feature list (`[workspace.dependencies]` in the root manifest —
//! default minus `gles`) and this file is the gate that keeps it flipped.
//!
//! | check | what it fails on |
//! |---|---|
//! | [`manifests_never_reopen_the_gl_door`] | a member manifest bypassing the one workspace `wgpu` list, or an `egui-wgpu` dep with default features |
//! | [`no_gl_reaches_a_shipped_binary`] | a **normal** dep edge to `glow` or `khronos-egl` |
//!
//! The DEV graph is out of scope on purpose, and that is a fact, not an
//! oversight: `egui_kittest`'s `wgpu` feature pins `gles` in its own dependency
//! table (a registry crate — unreachable from our manifests short of a fork), so
//! test builds still compile `glow`. Nothing we ship links it; the same
//! ships-only line the rayon law draws for the `dify` screenshot harness.
//!
//! Run: `cargo test -p facett-core --test webgl_free_law`
use facett_core::law;
use std::path::{Path, PathBuf};
fn root() -> PathBuf {
law::workspace_root(Path::new(env!("CARGO_MANIFEST_DIR")))
}
/// The two crates that exist ONLY under the GL backend. `wgpu-hal`'s `gles`
/// module has no crate boundary of its own; these are its imports, and either
/// re-appearing on a normal edge means `gles` (or `webgl`) got re-enabled.
const GL_CRATES: [&str; 2] = ["glow", "khronos-egl"];
#[test]
fn manifests_never_reopen_the_gl_door() {
let root = root();
// (1) The one writer exists and is shaped right: the root manifest's
// `[workspace.dependencies]` wgpu entry, default-features off, no GL feature.
let ws = std::fs::read_to_string(root.join("Cargo.toml")).expect("workspace Cargo.toml");
let wgpu_entry = ws
.split("\nwgpu = {")
.nth(1)
.map(|rest| &rest[..rest.find('}').unwrap_or(rest.len())])
.expect("the workspace manifest declares THE `wgpu` entry under [workspace.dependencies]");
assert!(
wgpu_entry.contains("default-features = false"),
"the workspace `wgpu` entry lost `default-features = false` — wgpu's default \
list includes `gles`, which is the GL backend LAW 4 forbids"
);
for banned in ["\"gles\"", "\"webgl\"", "\"angle\""] {
assert!(
!wgpu_entry.contains(banned),
"the workspace `wgpu` feature list re-admits {banned} — LAW 4: no GL, anywhere"
);
}
// (2) No member manifest bypasses the one writer with its own versioned
// `wgpu` dep (that is how the default `gles` came back last time), and no
// `egui-wgpu` dep runs with default features (its default list ends in
// `wgpu/default` + `wgpu/webgl`).
let manifests = law::workspace_manifests(&root);
assert!(
manifests.len() > 50,
"the manifest scan only found {} Cargo.toml files under {} — it is not looking \
at the workspace",
manifests.len(),
root.display(),
);
let mut hits: Vec<String> = Vec::new();
for m in &manifests {
if *m == root.join("Cargo.toml") {
continue; // the one writer itself
}
let Ok(text) = std::fs::read_to_string(m) else { continue };
for (i, line) in text.lines().enumerate() {
let line = line.trim();
if line.starts_with("wgpu =") && line.contains("version") {
hits.push(format!(
"{}:{} declares its own versioned `wgpu` (use `workspace = true`): {line}",
m.display(),
i + 1
));
}
if line.starts_with("egui-wgpu =") && !line.contains("default-features = false") {
hits.push(format!(
"{}:{} `egui-wgpu` with default features (re-enables `wgpu/default` + \
`wgpu/webgl`): {line}",
m.display(),
i + 1
));
}
}
}
assert!(
hits.is_empty(),
"LAW 4 violated by OUR OWN MANIFESTS ({} scanned):\n{}",
manifests.len(),
hits.join("\n"),
);
}
#[test]
fn no_gl_reaches_a_shipped_binary() {
let root = root();
for krate in GL_CRATES {
// LIVENESS FIRST — same command, same parser, one flag wider. glow IS in
// this workspace's dev graph (egui_kittest's `wgpu` feature pins `gles`),
// so a `None` here means cargo, the command or the parser stopped working
// and the clean verdict below would be worth nothing.
let live = law::dep_tree(&root, krate, "normal,dev,build", &[])
.expect("cargo tree -e normal,dev,build");
assert!(
!law::tree_found_nothing(&live),
"`cargo tree -e normal,dev,build -i {krate}` no longer reaches this workspace \
at all. Either egui_kittest stopped pinning `gles` (check, then update this \
guard's liveness arm) or the tree walk stopped reading cargo's output — until \
that is resolved, the clean answer below is not evidence of anything.",
);
// THE VERDICT: no normal edge. This is the exact command the flip was
// proven with, and the red proof is re-adding wgpu's default features to
// the workspace entry (captured in GFX_V2 V2.8).
let shipped = law::dep_tree(&root, krate, "normal", &[]).expect("cargo tree -e normal");
assert!(
law::tree_found_nothing(&shipped),
"LAW 4 violated on a SHIPPED edge: `{krate}` is reachable through NORMAL \
dependencies, so the GL backend links into what we ship. Chain:\n{}\n\
Reproduce with `cargo tree -e normal -i {krate} --target all`.",
law::tree_chain_to_first_party(&shipped, &root)
.map(|c| c.join(" -> "))
.unwrap_or_else(|| shipped.lines().take(12).collect::<Vec<_>>().join("\n")),
);
}
}
/// **The SOURCE may not branch on the GL backend either** — the third leg of LAW 4,
/// added 2026-08-24.
///
/// The two checks above hold the *dependency* line: no `glow`, no `khronos-egl`, no
/// `wgpu/gles` on a shipped edge. They cannot see the other half of the residue, which is
/// what GFX_V2 §V2.9b actually found:
///
/// > `facett-core/src/render/gpu/adapter_wgpu.rs:163-168` holds an **unreachable**
/// > WebGL2-limits branch (`Backends::GL` never appears in `preferred_backends()`).
///
/// That branch compiled, cost nothing at runtime, and could never fire — and it was still
/// worth removing, because dead code describing a lane the product does not have is a
/// documentation defect with a compiler behind it. A reader finds live-looking WebGL2
/// handling in the adapter policy and concludes there is a GL path to maintain; the next
/// one "fixes" it, and now there is.
///
/// So the shape this fails on is a source line that TESTS FOR or ASKS FOR the GL backend:
/// `wgpu::Backend::Gl`, `Backends::GL`, `downlevel_webgl2_defaults`. Comments are stripped
/// first, deliberately — every one of these names appears in prose across the workspace
/// explaining why GL is gone, and a law that could not be explained in a comment would be
/// obeyed by deleting the explanation.
#[test]
fn no_source_branches_on_the_gl_backend() {
// The tokens that mean "this code expects a GL adapter to be possible" — SPLIT so
// they never appear whole in this file. The alternative was to exempt this file from
// its own scan, and an exempted file is exactly the hole the next real violation hides
// in: `webgl_free_law.rs` is a plausible place for somebody to park a GL helper.
// Assembled at runtime, the guard scans itself like every other file and still passes.
let gl_code_tokens: [String; 4] = [
format!("Backend{}Gl", "::"),
format!("Backends{}GL", "::"),
format!("Backends{}GL_", "::"),
format!("downlevel{}webgl2", "_"),
];
fn rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else { return };
for e in entries.flatten() {
let p = e.path();
if p.is_dir() {
if p.file_name().is_some_and(|n| n == "target" || n == ".git") {
continue;
}
rs_files(&p, out);
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
let root = root();
let mut files = Vec::new();
for dir in facett_core::law::source_roots(&root) {
rs_files(&dir, &mut files);
}
// LIVENESS FIRST, and not as decoration: this file's own prose below contains every
// token in the list, so a scan that read nothing would report a clean workspace, and a
// scan whose comment-stripping broke would report this file. Both are checked.
assert!(
files.len() > 100,
"the source scan read only {} .rs files under {} — it is not looking at the \
workspace, so its clean verdict is worth nothing",
files.len(),
root.display()
);
let mut hits: Vec<String> = Vec::new();
for file in &files {
let Ok(text) = std::fs::read_to_string(file) else { continue };
for (i, raw) in text.lines().enumerate() {
let code = raw.split("//").next().unwrap_or("");
for tok in &gl_code_tokens {
if code.contains(tok.as_str()) {
hits.push(format!(
"{}:{}: {}",
file.strip_prefix(&root).unwrap_or(file).display(),
i + 1,
raw.trim()
));
}
}
}
}
assert!(
hits.is_empty(),
"LAW 4: {} source line(s) branch on or request the GL backend, which \
`preferred_backends()` can never produce (PRIMARY on native, BROWSER_WEBGPU on \
wasm) and which this workspace no longer compiles at all. Delete the branch — an \
unreachable GL path reads as a lane somebody has to maintain:\n {}",
hits.len(),
hits.join("\n ")
);
}