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

nordisk/facett

xtask/tests/wasm_bundle_has_gpu_features.rs

//! **Row 3 — the wasm bundle must ship the GPU lanes it is built to run on.**
//!
//! LAW 4 says WebGPU everywhere, and `facet-wrapped` already runs eframe on the wgpu
//! backend with `Backends::BROWSER_WEBGPU`. But until this row, the bundle pulled
//! `facett-map3d`, `facett-graph3d` and `facett-graphview` on their DEFAULT features —
//! which means their GPU lanes were compiled OUT. The result was CPU painters
//! projecting, culling and lighting on the main thread, *behind* a WebGPU surface: the
//! page reported `webgpu` truthfully and drew every triangle in software.
//!
//! That is the exact shape of a green nobody has seen red. `facet_backend()` returns
//! the constant `"webgpu"`, and `facet_canvas_api()` asks the canvas which context it
//! holds — both were and are `webgpu`, and neither can tell you whether the 3D map's
//! GPU renderer exists in the binary at all.
//!
//! So this test asserts the DEPENDENCY EDGES, read out of cargo's own manifest parse
//! rather than by grepping the TOML text. A comment claiming the feature is on is not
//! the feature being on.

use std::process::Command;

/// The three edges this row turns on, and the feature each one needs.
///
/// `facett-graphview` spells it `gpu` and the other two spell it `wgpu` — the names
/// differ in the crates themselves, so they are listed here rather than derived.
const REQUIRED: [(&str, &str); 7] = [
    ("facett-flow", "wgpu"),
    ("facett-pipeline", "wgpu"),
    ("facett-plandag", "wgpu"),
    ("facett-brokkrview", "wgpu"),
    ("facett-map3d", "wgpu"),
    ("facett-graph3d", "wgpu"),
    ("facett-graphview", "gpu"),
];

/// `cargo metadata --no-deps`, so this reads the DECLARED edges of the workspace's own
/// packages and never resolves the whole graph (fast, and unaffected by feature
/// unification across other members).
fn metadata() -> serde_json::Value {
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
    let out = Command::new("cargo")
        .current_dir(root)
        .args(["metadata", "--no-deps", "--format-version", "1"])
        .output()
        .expect("run cargo metadata");
    assert!(out.status.success(), "cargo metadata failed: {}", String::from_utf8_lossy(&out.stderr));
    serde_json::from_slice(&out.stdout).expect("cargo metadata emits JSON")
}

fn facet_wrapped_deps(md: &serde_json::Value) -> Vec<serde_json::Value> {
    md["packages"]
        .as_array()
        .expect("packages")
        .iter()
        .find(|p| p["name"] == "facet-wrapped")
        .expect("facet-wrapped is a workspace member")["dependencies"]
        .as_array()
        .expect("dependencies")
        .clone()
}

#[test]
fn the_bundle_enables_every_gpu_lane_it_wraps() {
    let md = metadata();
    let deps = facet_wrapped_deps(&md);

    let mut missing = Vec::new();
    for (crate_name, feature) in REQUIRED {
        let dep = deps
            .iter()
            .find(|d| d["name"] == crate_name)
            .unwrap_or_else(|| panic!("facet-wrapped does not depend on {crate_name} at all"));
        let feats: Vec<&str> =
            dep["features"].as_array().map(|a| a.iter().filter_map(|f| f.as_str()).collect())
                .unwrap_or_default();
        if !feats.contains(&feature) {
            missing.push(format!("{crate_name}/{feature} (declared features: {feats:?})"));
        }
    }
    assert!(
        missing.is_empty(),
        "the wasm bundle wraps these components with their GPU lane compiled OUT: {missing:?}\n\
         The page would report `webgpu` for the canvas and still draw every triangle on \
         the CPU — a truthful backend string in front of a software renderer."
    );
}

#[test]
fn the_gpu_lanes_are_not_turned_on_by_accident_of_default_features() {
    // `default-features` must stay ON for these three: their defaults carry the CPU
    // painter, which is the reference path and the snapshot golden path. This row adds
    // the GPU lane, it does not replace the CPU one — a bundle with only the GPU lane
    // has nothing to fall back to when `requestAdapter()` returns null.
    let md = metadata();
    for (crate_name, _) in REQUIRED {
        let deps = facet_wrapped_deps(&md);
        let dep = deps.iter().find(|d| d["name"] == crate_name).unwrap();
        assert_ne!(
            dep["uses_default_features"], serde_json::Value::Bool(false),
            "{crate_name} has default-features off — the CPU painter is the reference \
             path and the fallback, and this row must not remove it"
        );
    }
}

#[test]
fn the_comment_beside_the_edge_no_longer_claims_the_lane_is_off() {
    // The manifest carried a comment stating, in so many words, that map3d "rides its
    // DEFAULT features on purpose: the `wgpu` feature is off". Leaving that in place
    // while turning the feature on would leave the next reader with a file that
    // contradicts itself — and comments are what people read before code.
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
    let toml = std::fs::read_to_string(root.join("facet-wrapped/Cargo.toml")).unwrap();
    // NORMALISED first, and that is not a nicety: this test was GREEN on the
    // unchanged manifest because the phrase is line-wrapped across a `#` comment
    // continuation —
    //
    //     # `facett-map3d` rides its DEFAULT features on purpose: the `wgpu`
    //     # feature is off, so the 3D map projects + back-face-culls …
    //
    // so the substring "the `wgpu` feature is off" does not occur anywhere in the
    // bytes. A guard that passes because of where a line happens to wrap is a guard
    // that would have passed on the very file it exists to catch.
    let flat = toml
        .lines()
        .map(|l| l.trim_start().trim_start_matches('#').trim())
        .collect::<Vec<_>>()
        .join(" ");
    assert!(
        !flat.contains("the `wgpu` feature is off"),
        "facet-wrapped/Cargo.toml still says the wgpu feature is off while enabling it"
    );
}