use crate::device_runtime::GpuRuntime;
use crate::{GpuPolicy, global_policy};
use std::sync::atomic::{AtomicU64, Ordering};
static SKIPPED_FOR_ABSENT_DEVICE: AtomicU64 = AtomicU64::new(0);
pub const SKIPPED_MARKER: &str = "SKIPPED(no-cuda):";
pub fn skipped_for_absent_device() -> u64 {
SKIPPED_FOR_ABSENT_DEVICE.load(Ordering::Relaxed)
}
pub fn assert_absent_device_was_counted(floor: u64) -> u64 {
let observed = skipped_for_absent_device();
assert!(
observed >= floor + 1,
"an absent device must be COUNTED, not silently skipped: the skip counter \
read {floor} before the gate and {observed} after, so this test's skip left \
no trace and `ok` would again mean nothing (#2422)"
);
observed
}
#[derive(Debug)]
pub enum GpuTestGate {
Ready(&'static GpuRuntime),
AbsentDevice,
}
impl GpuTestGate {
pub fn runtime(&self) -> Option<&'static GpuRuntime> {
match self {
Self::Ready(runtime) => Some(runtime),
Self::AbsentDevice => None,
}
}
}
pub fn gpu_for_test(label: &str) -> GpuTestGate {
let policy = global_policy();
match GpuRuntime::resolve(GpuPolicy::Auto) {
Ok(Some(runtime)) => GpuTestGate::Ready(runtime),
Ok(None) => {
if matches!(policy, GpuPolicy::Required) {
panic!(
"[gpu-test] {label} REQUIRES a device: the process policy is \
GpuPolicy::Required and no CUDA runtime resolved. Skipping here \
would report a pass for a test that verified nothing (#2422)."
);
}
SKIPPED_FOR_ABSENT_DEVICE.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{SKIPPED_MARKER} {label} (policy={policy:?}) \
-- this test asserted NOTHING about a device; libtest still prints `ok`"
);
GpuTestGate::AbsentDevice
}
Err(error) => panic!(
"[gpu-test] {label}: CUDA resolution FAULTED: {error}. A faulted device is \
not an absent one, and must never be skipped (#2422) -- twenty-one sites \
used `let Some(..) = resolve(..) else {{ return }}`, whose else-arm \
swallowed exactly this."
),
}
}
#[cfg(test)]
mod tests_gpu_test_gate_2422 {
use super::*;
#[test]
fn an_absent_device_is_counted_not_silent_2422() {
let before = skipped_for_absent_device();
match gpu_for_test("gate self-test") {
GpuTestGate::Ready(runtime) => {
assert_eq!(
skipped_for_absent_device(),
before,
"a resolved runtime must not count as a skip"
);
assert!(
!runtime.selected_device().name.is_empty(),
"a Ready gate must carry a runtime with a selected device"
);
}
GpuTestGate::AbsentDevice => {
assert_absent_device_was_counted(before);
}
}
}
#[test]
fn the_skip_marker_is_one_greppable_string_2422() {
assert_eq!(
SKIPPED_MARKER, "SKIPPED(no-cuda):",
"the marker a CI ledger greps for must not drift; update the ledger step \
in the same commit if this ever changes"
);
assert!(
!SKIPPED_MARKER.contains('{'),
"the marker must be a literal, not a format template, or a grep cannot match it"
);
}
#[test]
fn the_gate_has_no_third_silent_outcome_2422() {
let before = skipped_for_absent_device();
match gpu_for_test("gate exhaustiveness self-test") {
GpuTestGate::Ready(runtime) => {
assert!(
runtime.selected_device().total_mem_bytes > 0,
"a Ready gate must carry a usable device, not a placeholder"
);
}
GpuTestGate::AbsentDevice => {
assert_absent_device_was_counted(before);
}
}
}
#[test]
fn the_gate_projects_to_an_option_only_at_the_call_site_2422() {
let gate = gpu_for_test("gate projection self-test");
assert_eq!(
gate.runtime().is_some(),
matches!(gate, GpuTestGate::Ready(_)),
"runtime() must agree with the variant"
);
}
}