facett-map/tests/common/mod.rs
//! **The GPU gate — ONE writer, and a skip that cannot be read as a pass.**
//!
//! Eleven call sites across five test files opened a headless wgpu device with five
//! byte-identical copies of the same function and then did this:
//!
//! ```ignore
//! let Some((device, queue)) = headless_device() else {
//! eprintln!("no wgpu adapter on this host — skipping (NOT a pass)");
//! return;
//! };
//! ```
//!
//! `eprintln!` is a message to a human; libtest records `ok`. On a CPU-only host every
//! one of those tests reported **PASS** with zero device involvement — the same dark
//! green as the crash they were written for (the z15 `line_cull_bg` panic), and the
//! parenthesis "(NOT a pass)" is exactly the kind of verdict a heading can carry and
//! code cannot.
//!
//! This module is the one writer for the device probe (L5) and makes the skip loud in
//! three ways that a green run cannot swallow:
//!
//! 1. the matrix gets its own row — `functional_status(.., ok = false, "SKIPPED..")`,
//! so a skipped cell is visibly not a passing cell;
//! 2. under `CI=1` (or `FACETT_REQUIRE_GPU=1`) the skip is a **panic**: a host that is
//! supposed to have an adapter and does not is a broken host, not a quiet pass;
//! 3. `FACETT_FORCE_NO_GPU=1` makes a GPU host take the no-adapter path on purpose, so
//! the skip path itself can be exercised and proven RED instead of being assumed.
#![allow(dead_code)]
/// **The one-device-at-a-time turn, shared with the `egui_kittest` files.**
///
/// MEASURED on oden 2026-08-27, default runner, 8 runs per binary: `line_gpu` — seven
/// `#[test]`s, each opening its own headless device through this function — died 1/8 with
/// `signal: 11, SIGSEGV`. The same hazard the kittest files record, reached by the other
/// door. Serialising the BRING-UP here is one writer for all thirteen files that say
/// `mod common;`, and it costs no call-site discipline at all: the turn is taken and
/// released inside this function, so nothing a caller does can forget it.
///
/// The five sites that used to wrap `headless_device(..)` in their own `hold()` had to
/// lose it in the same commit — `std::sync::Mutex` is not re-entrant, and a caller
/// holding the turn across this call would deadlock instead of crashing.
#[path = "one_gpu.rs"]
pub mod one_gpu;
/// The component name every row in this crate is filed under.
const COMPONENT: &str = "facett-map";
/// A headless wgpu device, or `None` when this host has no adapter. The ONE copy —
/// `downlevel_defaults` limits and no optional features, so what it hands back is the
/// weakest device any of these tests may assume.
pub fn headless_device(label: &str) -> Option<(wgpu::Device, wgpu::Queue)> {
// Held for the bring-up only: instance → adapter → `request_device`. That is the
// window the SIGSEGV lands in, and holding past the return would need the guard to
// travel with the device through thirteen call sites.
let _turn = one_gpu::hold();
if std::env::var("FACETT_FORCE_NO_GPU").is_ok() {
// The test hook that lets a GPU host walk the no-adapter path (see the module
// doc): without it, the skip branch is unreachable on the only machines where
// anyone would notice it is wrong.
return None;
}
let instance = wgpu::Instance::default();
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
compatible_surface: None,
force_fallback_adapter: false,
apply_limit_buckets: false,
}))
.ok()?;
pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some(label),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_defaults(),
..Default::default()
}))
.ok()
}
/// **The device, or a skip that announces itself.** `None` means the caller should
/// `return` — but by then the matrix already carries a non-passing row for `check`, the
/// human already has a line on stderr, and a host that claims to have a GPU has already
/// failed.
///
/// `detail` says what the skipped arm would have proven, so the matrix row is readable
/// without opening the test.
pub fn device_or_skip(check: &str, detail: &str) -> Option<(wgpu::Device, wgpu::Queue)> {
if let Some(d) = headless_device(check) {
return Some(d);
}
// Deliberately NOT "this host has no adapter" — `gpu_skip` asks the loader and says
// which of the two it actually is. This site only knows that the DEVICE request came
// back empty.
skip(check, "no wgpu device could be obtained", detail);
None
}
/// **The ONE way an arm of this crate's suite may decline to run.** Any test that
/// returns early because the host cannot carry it calls this first, so the decision
/// leaves three marks a green run cannot swallow: a line on stderr, a non-passing row
/// in the matrix, and a hard failure on a host that declared it has the hardware.
///
/// Split out of [`device_or_skip`] because the adapter is not the only thing an arm can
/// lack. `gpu_clock.rs`'s real-timestamp arm needs an adapter *with*
/// `PASS_CLOCK_FEATURES`, and it used to bail with a bare `println!` + `return` — the
/// exact `eprintln!`-then-`ok` shape this module was written to delete, one capability
/// to the right of the one it deleted. `why` names what was missing; `detail` names what
/// the arm would have proven had it run.
///
/// **One writer, and it is not this file (LAW 5).** The body used to be a twelve-line
/// copy of `facett_core::testmatrix::gpu_skip` — the stderr line, the non-passing row and
/// the `CI` / `FACETT_REQUIRE_GPU` assert, all restated. Six other facett crates carried
/// the *un*fixed shape (`eprintln!` + `return`), so the fix had to move up into
/// `facett-core` anyway; keeping a second copy here would have meant the constellation's
/// skip policy lived in two places and could drift in one. This function survives only to
/// bind `COMPONENT`, so a call site inside this crate does not have to name it.
///
/// The matrix row now travels through `facett-core`'s `testmatrix` feature (this crate's
/// own `testmatrix = ["facett-core/testmatrix"]` passthrough). That is NOT a weakening:
/// this crate's `nornir-testmatrix` dev-dependency is `default-features = false`, so
/// `functional_status` was already a no-op unless something turned the feature on. The
/// teeth — the assert — never depended on a feature and still does not.
pub fn skip(check: &str, why: &str, detail: &str) {
facett_core::testmatrix::gpu_skip(COMPONENT, check, why, detail);
}