use std::cmp::Ordering;
use std::path::PathBuf;
const UNGUARDED_BUDGET: usize = 0;
const ACQUIRES_A_DEVICE: &[&str] = &["Instance::new("];
const DELIBERATE_MARKER: &str = "GPU-LOCK-DIRECT:";
const GUARDED_HELPERS: &[&str] = &[
"instance_and_devices(",
"compute_device(",
"first_compute(",
"create_device_on(",
];
fn tests_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests")
}
fn rust_test_files() -> Vec<PathBuf> {
let mut out = Vec::new();
let dir = tests_dir();
let entries =
std::fs::read_dir(&dir).unwrap_or_else(|e| panic!("cannot read {}: {e}", dir.display()));
for entry in entries.flatten() {
let p = entry.path();
if p.extension().is_some_and(|e| e == "rs") {
out.push(p);
}
}
out.sort();
out
}
const MARKER_WINDOW_LINES: usize = 8;
fn marked_above(src: &str, at: usize) -> bool {
window_above(src, at).any(|l| l.contains(DELIBERATE_MARKER))
}
fn guarded_above(src: &str, at: usize) -> bool {
window_above(src, at).any(|l| l.contains("require_serialization_lock()"))
}
fn window_above(src: &str, at: usize) -> impl Iterator<Item = &str> {
let start = src[..at].rfind('\n').unwrap_or(0);
src[..start].rsplit('\n').take(MARKER_WINDOW_LINES)
}
fn unguarded_sites() -> (usize, Vec<(String, usize)>) {
let mut total = 0;
let mut per_file = Vec::new();
for path in rust_test_files() {
if path
.file_name()
.is_some_and(|n| n == "gpu_lock_coverage.rs")
{
continue;
}
let src = std::fs::read_to_string(&path).unwrap_or_else(|e| {
panic!(
concat!(
"cannot read {} — the scan cannot see this file, and an ",
"unreadable file is indistinguishable from a clean one: {}"
),
path.display(),
e
)
});
let n = ACQUIRES_A_DEVICE
.iter()
.flat_map(|needle| src.match_indices(needle))
.filter(|(at, _)| !marked_above(&src, *at))
.count();
for (at, _) in ACQUIRES_A_DEVICE
.iter()
.flat_map(|needle| src.match_indices(needle))
{
assert!(
!marked_above(&src, at) || guarded_above(&src, at),
"{}:{} carries {} but does not call \
`require_serialization_lock()` within {} lines.\n\n\
The marker exempts this site from the scan. Without the guard \
call it exempts it from the RUNTIME check too — while \
recording a reason, which reads as considered. An exemption \
must say why AND still assert the property.",
path.display(),
src[..at].matches('\n').count() + 1,
DELIBERATE_MARKER,
MARKER_WINDOW_LINES
);
}
if n > 0 {
total += n;
per_file.push((
path.file_name()
.and_then(|s| s.to_str())
.unwrap_or("?")
.to_string(),
n,
));
}
}
(total, per_file)
}
#[test]
fn unguarded_device_acquisitions_do_not_increase() {
let (total, per_file) = unguarded_sites();
let detail = per_file
.iter()
.map(|(f, n)| format!(" {n:>3} {f}"))
.collect::<Vec<_>>()
.join("\n");
match total.cmp(&UNGUARDED_BUDGET) {
Ordering::Equal => {}
Ordering::Greater => panic!(
concat!(
"unguarded device acquisitions rose to {} (budget {}).
{}
",
"A live test acquiring a device outside a guarded helper can touch ",
"the GPU without the machine-wide `Global\\gpu-run` mutex, and ",
"nothing at runtime will say so -- the run completes, the tests ",
"pass, and the only difference is a mutex nobody observes.
",
"Route it through `common::instance_and_devices` (or another helper ",
"that calls `require_serialization_lock`) rather than raising this ",
"number. The budget is DEBT, not an allowlist: it may only go down."
),
total, UNGUARDED_BUDGET, detail
),
Ordering::Less => panic!(
concat!(
"unguarded device acquisitions fell to {} -- below the recorded ",
"budget of {}. That is good; lower UNGUARDED_BUDGET to {} in the ",
"same change so the ratchet keeps holding at the new level.
{}
",
"Left as a failure rather than a pass on purpose: a budget that ",
"silently tolerates being beaten stops being a ratchet and becomes ",
"a ceiling nobody lowers."
),
total, UNGUARDED_BUDGET, total, detail
),
}
}
#[test]
fn the_guarded_helpers_still_call_the_guard() {
let common = tests_dir().join("common").join("mod.rs");
let src = std::fs::read_to_string(&common)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", common.display()));
assert!(
src.contains("fn require_serialization_lock"),
"common/mod.rs no longer defines `require_serialization_lock`; every \
file this scan counts as guarded would silently become unguarded, and \
the site count would not move."
);
assert!(
src.contains("require_serialization_lock();"),
"common/mod.rs defines `require_serialization_lock` but never calls it. \
The helpers this scan treats as guarded would be guarded in name only \
— and the budget above would keep reporting the same number while \
meaning something entirely different."
);
}
#[test]
fn every_common_helper_that_acquires_a_device_is_listed() {
let common = tests_dir().join("common").join("mod.rs");
let src = std::fs::read_to_string(&common)
.unwrap_or_else(|e| panic!("cannot read {}: {e}", common.display()));
let mut examined = 0usize;
for (name, signature) in public_signatures(&src) {
let returns_device = signature.contains("PhysicalDevice")
|| signature.contains("Instance")
|| signature.contains("Device");
if !returns_device {
continue;
}
examined += 1;
let hint = format!("{name}(");
assert!(
GUARDED_HELPERS.iter().any(|h| *h == hint),
"common/mod.rs exposes `{name}`, whose signature mentions a device \
or instance, but GUARDED_HELPERS does not list it.\n\n\
A caller routing through it would be counted as UNGUARDED by this \
scan (a false alarm), or — if it does not call \
`require_serialization_lock` — would be genuinely unguarded while \
looking fine. Add it to GUARDED_HELPERS and make sure it calls the \
guard.\n\n signature: {signature}"
);
}
assert!(
examined >= GUARDED_HELPERS.len(),
"the signature scan examined only {examined} device-returning helpers, \
but GUARDED_HELPERS names {}. It is not seeing the file — a scan that \
matches nothing passes, which is how the single-line version of this \
check reported ok while examining zero helpers.",
GUARDED_HELPERS.len()
);
}
fn public_signatures(src: &str) -> Vec<(String, String)> {
let mut out = Vec::new();
let mut rest = src;
while let Some(i) = rest.find("pub fn ") {
let after = &rest[i + "pub fn ".len()..];
let Some(name_end) = after.find(['(', '<', ' ']) else {
break;
};
let name = after[..name_end].trim().to_string();
let sig_end = after.find(" {").unwrap_or(after.len().min(400));
out.push((name, after[..sig_end].replace('\n', " ")));
rest = after;
}
out
}
#[test]
fn the_scan_actually_reads_files() {
let files = rust_test_files();
assert!(
files.len() > 5,
"the scan found only {} test files under {} — it is looking in the \
wrong place, and a scan that reads nothing reports zero unguarded \
sites and passes.",
files.len(),
tests_dir().display()
);
assert!(
files.iter().any(|p| p.ends_with("safe_wrapper_test.rs")),
"the scan did not find safe_wrapper_test.rs, which is the largest \
population of direct acquisition sites in the suite"
);
}
#[cfg(test)]
mod marker_window_tests {
use super::{DELIBERATE_MARKER, MARKER_WINDOW_LINES, guarded_above, marked_above};
fn src_with_gap(gap: usize) -> (String, usize) {
let mut s = format!("// {DELIBERATE_MARKER} reason\n");
for i in 0..gap {
s.push_str(&format!("// filler {i}\n"));
}
s.push_str(" Instance::new(x);\n");
let at = s.find("Instance::new(").expect("target present");
(s, at)
}
#[test]
fn a_marker_directly_above_is_honoured() {
let (s, at) = src_with_gap(0);
assert!(marked_above(&s, at));
}
#[test]
fn the_window_ends_where_it_says_it_does() {
let (s, at) = src_with_gap(MARKER_WINDOW_LINES - 1);
assert!(
marked_above(&s, at),
"a marker exactly at the window edge must still count"
);
let (s, at) = src_with_gap(MARKER_WINDOW_LINES);
assert!(
!marked_above(&s, at),
"a marker one line past the window must not count — otherwise the \
window is wider than it claims and a distant comment can exempt a \
site it was never written for"
);
}
#[test]
fn a_marker_at_or_below_the_site_does_not_count() {
let same = format!(" Instance::new(x); // {DELIBERATE_MARKER} nope\n");
let at = same.find("Instance::new(").unwrap();
assert!(!marked_above(&same, at));
let below = format!(" Instance::new(x);\n// {DELIBERATE_MARKER} nope\n");
let at = below.find("Instance::new(").unwrap();
assert!(!marked_above(&below, at));
}
#[test]
fn the_start_of_a_file_is_handled() {
let s = "Instance::new(x);\n";
assert!(!marked_above(s, 0));
let s = format!("// {DELIBERATE_MARKER} r\nInstance::new(x);\n");
let at = s.find("Instance::new(").unwrap();
assert!(marked_above(&s, at));
}
#[test]
fn the_guard_call_uses_the_same_window() {
let s = " require_serialization_lock();\n Instance::new(x);\n";
let at = s.find("Instance::new(").unwrap();
assert!(guarded_above(s, at));
let mut far = String::from(" require_serialization_lock();\n");
for i in 0..MARKER_WINDOW_LINES {
far.push_str(&format!("// filler {i}\n"));
}
far.push_str(" Instance::new(x);\n");
let at = far.find("Instance::new(").unwrap();
assert!(
!guarded_above(&far, at),
"a guard call beyond the window must not vouch for this site — the \
two checks have to agree about what 'nearby' means, or a marker \
could be honoured while the call it depends on is not found"
);
}
}