The tests behind WAI's whole-decode claims read inputs from outside the repository and returned early — reporting a pass — when those inputs were absent. No workflow supplied them, so the claims were not checked continuously. - WAI_REQUIRE_SOTA_VECTORS=1 turns a missing input into a failure. A path variable naming a missing or incomplete input fails with or without the switch. Local runs without either still skip, and every skip prints a banner saying the claim was not verified. - wai-sota-vectors.yml regenerates the int_hyper vectors from the pretrained mbt2018_mean checkpoint (pinned by SHA-256, with a pinned Python stack) and runs int_hyper_synth and int_video_sota_keyframe with the switch set. The regenerated vectors identify one run, not one machine: the encoder side is float. - wai-jpegai-e2e.yml fetches the pinned JPEG AI inputs from the public models root, checks each against a SHA-256 manifest, and runs the whole-decode and reconstruction-binding tests with the switch set, requiring each test by name and the pinned pixel hash. - Both jobs fail on any skip banner, and trigger on the kernels and float helpers the decode imports. - The int_hyper claims are stated as the checks CI runs, with its quality measured on the pinned image; JPEG AI cross-machine identity is stated as a measurement, not a registry-class claim. determinism-tiers §5–§6 record the jobs. Neither workflow has yet run through the forge. Co-Authored-By: Claude Opus 5.5 <[email protected]>
300 lines
10 KiB
Rust
300 lines
10 KiB
Rust
//! The missing-input rule of `support/require_vectors.rs`, checked in the
|
|
//! environment it is about.
|
|
//!
|
|
//! The vector tests' CI jobs always set `WAI_REQUIRE_SOTA_VECTORS=1`, so those runs
|
|
//! never exercise what the helpers do WITHOUT the switch: that a variable naming a
|
|
//! test's inputs still makes them required, and that a test skips only when no
|
|
//! variable names what is missing (an unset variable, or an absent or incomplete
|
|
//! default location). These tests check that rule directly. Each case starts this
|
|
//! test binary again as a child process running the ignored `probe` test, with the
|
|
//! switch and the input variables set exactly as the case needs (and every other
|
|
//! one removed, so the parent's environment cannot leak in), then checks whether
|
|
//! the child passed or failed and what it printed.
|
|
//!
|
|
//! Their inputs are scratch directories and small files that each case creates
|
|
//! under the system temporary directory, so these tests never skip.
|
|
|
|
#[path = "support/require_vectors.rs"]
|
|
mod require_vectors;
|
|
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
/// Which helper the probe runs: `dir` (`env_dir` + `load_all`, as the int_hyper
|
|
/// tests use them) or `paths` (`env_paths`, as the JPEG-AI tests use it).
|
|
const MODE: &str = "WAI_RV_PROBE";
|
|
/// The default directory the probe passes to `env_dir`.
|
|
const DEFAULT: &str = "WAI_RV_PROBE_DEFAULT";
|
|
/// The directory variable the probe reads, as `int_hyper_synth` does.
|
|
const DIR_VAR: &str = "WAI_SOTA_INT_DIR";
|
|
/// The two path variables the probe reads, as the JPEG-AI tests read three.
|
|
const PATH_VAR: &str = "WAI_JPEGAI_CODESTREAM";
|
|
const PATH_VAR2: &str = "WAI_JPEGAI_NORM";
|
|
/// The one input file the `dir` probe needs.
|
|
const INPUT: &str = "cond.json";
|
|
|
|
/// Runs one helper the way a vector test does and prints what it returned. It is
|
|
/// only meaningful in a child started by [`run`], with the environment [`run`]
|
|
/// gives it; run any other way, it returns without doing anything.
|
|
#[test]
|
|
#[ignore = "a probe that the other tests in this file run in a child process"]
|
|
fn probe() {
|
|
let Ok(mode) = std::env::var(MODE) else {
|
|
return;
|
|
};
|
|
let loaded = match mode.as_str() {
|
|
"dir" => {
|
|
let default = std::env::var(DEFAULT).expect("the default directory");
|
|
let d = require_vectors::env_dir("probe", DIR_VAR, &default, "the probe");
|
|
require_vectors::load_all("probe", &d, [INPUT], "the probe").is_some()
|
|
}
|
|
"paths" => require_vectors::env_paths("probe", [PATH_VAR, PATH_VAR2], "the probe").is_some(),
|
|
other => panic!("unknown probe mode {other}"),
|
|
};
|
|
println!(
|
|
"probe: {}",
|
|
if loaded {
|
|
"inputs loaded"
|
|
} else {
|
|
"returned without inputs"
|
|
}
|
|
);
|
|
}
|
|
|
|
/// What a probe run did.
|
|
struct Outcome {
|
|
passed: bool,
|
|
/// The child's stdout followed by its stderr.
|
|
out: String,
|
|
}
|
|
|
|
impl Outcome {
|
|
/// The probe passed because it found nothing to check: the loud skip.
|
|
fn assert_skipped_loudly(&self) {
|
|
assert!(self.passed, "expected a skip, got a failure:\n{}", self.out);
|
|
assert!(
|
|
self.out.contains("probe: returned without inputs"),
|
|
"{}",
|
|
self.out
|
|
);
|
|
// The banner CI's backstop looks for, so a skip is never read as a pass.
|
|
assert!(
|
|
self.out.contains("SKIPPED") && self.out.contains("NOT VERIFIED"),
|
|
"{}",
|
|
self.out
|
|
);
|
|
}
|
|
|
|
fn assert_failed_with(&self, why: &str) {
|
|
assert!(
|
|
!self.passed,
|
|
"expected a failure naming {why:?}, got a pass:\n{}",
|
|
self.out
|
|
);
|
|
assert!(
|
|
self.out.contains(why),
|
|
"the failure does not say {why:?}:\n{}",
|
|
self.out
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Runs the probe in a child process in `mode`, with `env` set and the switch and
|
|
/// every input variable otherwise removed.
|
|
fn run(mode: &str, env: &[(&str, &str)]) -> Outcome {
|
|
let exe = std::env::current_exe().expect("this test binary");
|
|
let mut cmd = Command::new(exe);
|
|
cmd.args([
|
|
"probe",
|
|
"--exact",
|
|
"--ignored",
|
|
"--nocapture",
|
|
"--test-threads=1",
|
|
]);
|
|
for v in [require_vectors::SWITCH, MODE, DEFAULT, DIR_VAR, PATH_VAR, PATH_VAR2] {
|
|
cmd.env_remove(v);
|
|
}
|
|
cmd.env(MODE, mode);
|
|
for (k, v) in env {
|
|
cmd.env(k, v);
|
|
}
|
|
let o = cmd.output().expect("start the probe");
|
|
let out = format!(
|
|
"{}{}",
|
|
String::from_utf8_lossy(&o.stdout),
|
|
String::from_utf8_lossy(&o.stderr)
|
|
);
|
|
// A filter that matched nothing would also exit 0.
|
|
assert!(
|
|
out.contains("running 1 test"),
|
|
"the probe did not run:\n{out}"
|
|
);
|
|
Outcome {
|
|
passed: o.status.success(),
|
|
out,
|
|
}
|
|
}
|
|
|
|
/// A scratch directory unique to one test case, removed when dropped.
|
|
struct Scratch(PathBuf);
|
|
|
|
impl Scratch {
|
|
fn new(case: &str) -> Scratch {
|
|
let p = std::env::temp_dir().join(format!(
|
|
"wai-require-vectors-rule-{}-{case}",
|
|
std::process::id()
|
|
));
|
|
let _ = std::fs::remove_dir_all(&p);
|
|
std::fs::create_dir_all(&p).expect("create the scratch directory");
|
|
Scratch(p)
|
|
}
|
|
|
|
/// A path inside the scratch directory, not created.
|
|
fn at(&self, name: &str) -> String {
|
|
self.0
|
|
.join(name)
|
|
.to_str()
|
|
.expect("a UTF-8 temp path")
|
|
.to_owned()
|
|
}
|
|
}
|
|
|
|
impl Drop for Scratch {
|
|
fn drop(&mut self) {
|
|
let _ = std::fs::remove_dir_all(&self.0);
|
|
}
|
|
}
|
|
|
|
// ---- without the switch ----
|
|
|
|
#[test]
|
|
fn without_the_switch_a_named_directory_that_does_not_exist_fails() {
|
|
let s = Scratch::new("dir-missing");
|
|
let (named, default) = (s.at("not-there"), s.at("default"));
|
|
run("dir", &[(DIR_VAR, &named), (DEFAULT, &default)]).assert_failed_with("is not a directory");
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_a_named_path_that_is_a_file_fails_as_a_directory() {
|
|
let s = Scratch::new("dir-is-file");
|
|
let (named, default) = (s.at("a-file"), s.at("default"));
|
|
std::fs::write(&named, b"{}").unwrap();
|
|
run("dir", &[(DIR_VAR, &named), (DEFAULT, &default)]).assert_failed_with("is not a directory");
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_a_named_empty_directory_fails() {
|
|
// The case this file was written for: the directory exists, so only the
|
|
// missing file can fail the run.
|
|
let s = Scratch::new("dir-empty");
|
|
let (named, default) = (s.at("vectors"), s.at("default"));
|
|
std::fs::create_dir(&named).unwrap();
|
|
run("dir", &[(DIR_VAR, &named), (DEFAULT, &default)])
|
|
.assert_failed_with(&format!("{DIR_VAR} names this directory"));
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_an_unset_or_empty_directory_variable_skips_on_an_absent_default() {
|
|
let s = Scratch::new("dir-default");
|
|
let default = s.at("default");
|
|
run("dir", &[(DEFAULT, &default)]).assert_skipped_loudly();
|
|
run("dir", &[(DIR_VAR, ""), (DEFAULT, &default)]).assert_skipped_loudly();
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_an_incomplete_default_directory_skips() {
|
|
let s = Scratch::new("dir-default-empty");
|
|
let default = s.at("default");
|
|
std::fs::create_dir(&default).unwrap();
|
|
run("dir", &[(DEFAULT, &default)]).assert_skipped_loudly();
|
|
}
|
|
|
|
#[test]
|
|
fn a_named_directory_holding_the_input_loads_it() {
|
|
// The positive control: without it, a probe that always failed would make the
|
|
// failure cases above pass for the wrong reason.
|
|
let s = Scratch::new("dir-full");
|
|
let (named, default) = (s.at("vectors"), s.at("default"));
|
|
std::fs::create_dir(&named).unwrap();
|
|
std::fs::write(PathBuf::from(&named).join(INPUT), b"{}").unwrap();
|
|
let o = run("dir", &[(DIR_VAR, &named), (DEFAULT, &default)]);
|
|
assert!(
|
|
o.passed && o.out.contains("probe: inputs loaded"),
|
|
"{}",
|
|
o.out
|
|
);
|
|
assert!(!o.out.contains("NOT VERIFIED"), "{}", o.out);
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_a_path_variable_naming_nothing_fails() {
|
|
let s = Scratch::new("path-missing");
|
|
let other = s.at("norm.json");
|
|
std::fs::write(&other, b"{}").unwrap();
|
|
run("paths", &[(PATH_VAR, &s.at("not-there")), (PATH_VAR2, &other)])
|
|
.assert_failed_with("does not exist");
|
|
// env_paths has no default: an empty value names no existing path.
|
|
run("paths", &[(PATH_VAR, ""), (PATH_VAR2, &other)]).assert_failed_with("does not exist");
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_a_path_variable_naming_nothing_fails_when_another_is_unset() {
|
|
// The other variable being unset would end the test as a skip; the one that
|
|
// names a missing path must fail it first.
|
|
let s = Scratch::new("path-missing-partial");
|
|
run("paths", &[(PATH_VAR, &s.at("not-there"))]).assert_failed_with("does not exist");
|
|
run("paths", &[(PATH_VAR2, &s.at("not-there"))]).assert_failed_with("does not exist");
|
|
}
|
|
|
|
#[test]
|
|
fn without_the_switch_an_unset_path_variable_skips() {
|
|
run("paths", &[]).assert_skipped_loudly();
|
|
// One set to a file that exists, the other unset: nothing named is missing.
|
|
let s = Scratch::new("path-partial");
|
|
let file = s.at("codestream.bin");
|
|
std::fs::write(&file, b"x").unwrap();
|
|
run("paths", &[(PATH_VAR, &file)]).assert_skipped_loudly();
|
|
}
|
|
|
|
#[test]
|
|
fn path_variables_naming_files_that_exist_load_them() {
|
|
// The positive control for `env_paths`, as the directory one is for `env_dir`.
|
|
let s = Scratch::new("path-full");
|
|
let (a, b) = (s.at("codestream.bin"), s.at("norm.json"));
|
|
std::fs::write(&a, b"x").unwrap();
|
|
std::fs::write(&b, b"{}").unwrap();
|
|
let o = run("paths", &[(PATH_VAR, &a), (PATH_VAR2, &b)]);
|
|
assert!(
|
|
o.passed && o.out.contains("probe: inputs loaded"),
|
|
"{}",
|
|
o.out
|
|
);
|
|
assert!(!o.out.contains("NOT VERIFIED"), "{}", o.out);
|
|
}
|
|
|
|
#[test]
|
|
fn switch_values_that_leave_it_off_behave_as_unset() {
|
|
let s = Scratch::new("switch-off");
|
|
let default = s.at("default");
|
|
for off in ["", "0", "false", "no", "FALSE", " no "] {
|
|
run(
|
|
"dir",
|
|
&[(require_vectors::SWITCH, off), (DEFAULT, &default)],
|
|
)
|
|
.assert_skipped_loudly();
|
|
}
|
|
}
|
|
|
|
// ---- with the switch ----
|
|
|
|
#[test]
|
|
fn with_the_switch_an_absent_default_fails() {
|
|
let s = Scratch::new("switch-on");
|
|
let default = s.at("default");
|
|
for on in ["1", "true", "yes"] {
|
|
run("dir", &[(require_vectors::SWITCH, on), (DEFAULT, &default)])
|
|
.assert_failed_with("a missing input is a failure, not a skip");
|
|
run("paths", &[(require_vectors::SWITCH, on)])
|
|
.assert_failed_with("a missing input is a failure, not a skip");
|
|
}
|
|
}
|