open-standards/wai/wai-rs/tests/support/require_vectors.rs
Transaction Science f218af5ac0
Some checks failed
wai-ops-gpu integer operators on GPUs / does this change touch it (push) Successful in 25s
WAI operation catalogue / does this change touch it (push) Successful in 34s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 40s
wai-det-jpegai WAISM float-level module of record / does this change touch it (push) Successful in 1m7s
wai-app and Create / wai-app · does this change touch it (push) Successful in 46s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 1m23s
wai-ops integer operators / does this change touch it (push) Successful in 35s
wai-quantum for JavaScript / does this change touch it (push) Successful in 31s
wai-sink-android native sink library / does this change touch it (push) Successful in 18s
wai-pack / wai-pack · does this change touch it (push) Successful in 33s
wai-ledger service / does this change touch it (push) Successful in 1m37s
joule-moq-http live HTTP egress / joule-moq-http · does this change touch it (push) Successful in 1m48s
wai-audit collector and player integration / does this change touch it (push) Successful in 1m44s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 1m47s
wai-key-proxy / does this change touch it (push) Successful in 1m38s
wai-fleet service / does this change touch it (push) Successful in 1m38s
WAI Studio / does this change touch it (push) Successful in 22s
wai-sink-swift native sink package / does this change touch it (push) Successful in 51s
wai-quantum studio and desktop / does this change touch it (push) Successful in 1m9s
wai-ops-gpu integer operators on GPUs / Vulkan GPUs (framework-128gb) (push) Successful in 9m59s
wai-ops-gpu integer operators on GPUs / Vulkan GPUs (jetson-hub) (push) Successful in 11m56s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-pack / wai-pack · linux (push) Has been cancelled
wai-pack / wai-pack · linux-arm64 (push) Has been cancelled
wai-pack / wai-pack · macos (push) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (push) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (push) Has been cancelled
wai-pack / wai-pack · dependency licences (push) Has been cancelled
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (push) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
wai-key-proxy / proxy · linux (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
WAI examples / WAI examples · does this change touch them (push) Has been cancelled
WAI examples / WAI examples · portable · linux (push) Has been cancelled
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Has been cancelled
WAI examples / WAI examples · android-app · linux-arm64 (push) Has been cancelled
wai-det-jpegai WAISM float-level module of record / linux · both checkers, F1, F2 on wasmtime (Cranelift, Winch, Pulley) and Wasmi, the planted faults (push) Has been cancelled
wai-det-jpegai WAISM float-level module of record / linux-arm64 · both checkers, F1, F2 on wasmtime (Cranelift, Winch, Pulley) and Wasmi, the planted faults (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-moq-http live HTTP egress / joule-moq-http, every target (push) Has been cancelled
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 37s
wai-ops integer operators / linux-aarch64 (push) Successful in 4m58s
wai-ops integer operators / wasm32-wasip2 (wasmtime) (push) Successful in 1m1s
wai-ops integer operators / independent checker (Python) (push) Successful in 26s
wai-ops integer operators / wasm32 under Node (push) Successful in 50s
wai-ops integer operators / linux-x86_64 (push) Successful in 3m6s
wai-ffmpeg / wai-ffmpeg · VLC 3.0.24 on the patched FFmpeg, playing the corpora (push) Failing after 4m37s
wai-gstreamer / wai-gstreamer · the plugin on a distribution's GStreamer (push) Failing after 5m59s
wai-ffmpeg / wai-ffmpeg · the series on FFmpeg 9.0.2, checked and fuzzed (push) Successful in 17m38s
wai-ops integer operators / macos-arm64 (push) Successful in 1m55s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 9m49s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 15m49s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 20m14s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 14m1s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 12m3s
wai.image.jpegai: the exact decoder decodes the configurations of its bundle's model its vectors hold; two the reference leaves to the machine are refused configuration_undetermined
Completeness row C8. The exact decoder (jpegai_decode) decoded one configuration, the
reference codestream's; every other trapped. With the jpegai/v1 bundle (tools_0, the
high-operation-point synthesis, one eICCI pair) it now decodes:
- pictures whose sides are 128, 192 or 256, any display cropping (the post-filters run on the
  displayed picture; eICCI pads a side to a multiple of 4 by repetition);
- beta_displacement_log from -1069 to 702, the chroma one independent or not;
- LSBS off per plane; residual variance scaling and GRFS both off, or GRFS alone (one band);
- each post-filter off; eICCI on any plane subset with the bundle's pair; no colour transform.
Held by 29 configuration vectors (wai-rs/tests/vectors/jpegai/configs/: synthetic pictures the
reference software's encoder coded at each configuration, its rate search off, and header
edits of them), each pinned to its RGB8 (the same digests on Linux x86_64 and macOS arm64),
decoded the direct way to the same bits at every stage, and max|d| = 1 from the reference
decoder's picture on 2.5% to 6.6% of samples (tests/jpegai_configs.rs; wai-jpegai-e2e.yml).

Refused with a new code, configuration_undetermined (D74): residual variance scaling without
GRFS (the reference dequantises it by an integer shift or a float division as its tables sit,
and raises on a CPU) and a signalled colour matrix (a library inverse, applied by its first
row alone; the reference raises before either). Everything else outside scope is refused as
before, each with a vector or a header test showing where.

Readings fixed (D74): the scale's incomplete 8x8 blocks pad with 1411; LEF samples its scale
surface at the binary32 quotient's floor; no colour transform gives the decoded components.
The float ONNX path keeps the reference codestream's configuration alone
(check_reference_configuration). The scale path's text (integer-operators 13.6.1) gains the
picture's sizes, beta and the RVS modes; 13.6.2 states the graphs' declared sizes as maxima.

Second reader: tools/wai_jpegai_header.py (standard library) reads and writes the headers and
gives each codestream's scope; tools/wai_jpegai_configs.py writes the manifest, checks that
reader against it and holds decodes to the reference pictures; tools/wai_jpegai_make_vectors.py
makes the vectors (the edits regenerate byte for byte).

Planted faults (cfg(wai_planted), never in an ordinary build), each caught by the vectors:
likely-pad-index, beta-after-likely, grfs-only-floor, rvs-mode-swap, lef-nearest-round,
icci-pad-index, crop-index, icci-subset-swap, lsbs-off-ignored, ct0-order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-12 04:54:58 -05:00

205 lines
9.9 KiB
Rust

//! What a test does when the out-of-tree inputs behind a headline decode claim are
//! absent.
//!
//! Some decode claims are tested against inputs too large, or not redistributable,
//! to commit: the `int_hyper` vectors (the pretrained mbt2018_mean, exported to
//! integers) and the JPEG-AI whole-decoder bundle. A test that `return`s when its
//! inputs are missing is reported as *passed*, so a run with nothing to check looks
//! exactly like a run that checked everything. These helpers remove that ambiguity:
//!
//! - `WAI_REQUIRE_SOTA_VECTORS` unset (or `0`/`false`/`no`): the test still skips,
//! but writes a banner to the process's real stderr — past libtest's output
//! capture, so it shows in an ordinary `cargo test` run, not only under
//! `--nocapture`.
//! - `WAI_REQUIRE_SOTA_VECTORS=1`: a missing input is a test failure naming the
//! tool that produces it. A run that claims to verify these decodes sets it.
//!
//! Either way, with or without the switch, a variable that names a test's inputs
//! makes them required: pointing a test at inputs and having it pass because they
//! are not there is the failure this module exists to prevent. So the test fails
//! when a variable read through [`env_paths`] names a path that does not exist,
//! and when a directory variable read through [`env_dir`] (`WAI_SOTA_INT_DIR`,
//! `WAI_JPEGAI_ASSETS`) names something that is not a directory, or a directory
//! missing a file the test reads ([`InputDir::absent`]). The JPEG-AI tests that
//! take a directory through [`env_paths`] fail the same way on a missing file,
//! because their loaders do not skip. So without the switch a test skips only when
//! no variable names what is missing: when a variable read through [`env_paths`]
//! is unset (the ones that are set must still name existing paths), or when the
//! variable of [`env_dir`] is unset or empty and the default location it falls
//! back to is absent or incomplete. [`env_paths`] has no default, so an empty value
//! there names no existing path and fails. `tests/require_vectors_rule.rs` checks
//! this rule by running these helpers in child processes, with the switch unset and
//! set.
//!
//! Where each covered input comes from, and who verifies it:
//!
//! | test | inputs | produced by | verified in CI |
//! |---|---|---|---|
//! | `int_hyper_synth` (pretrained-model cases) | `WAI_SOTA_INT_DIR` | `tools/wai_sota_vectors.sh` | yes — `wai-sota-vectors.yml` regenerates and requires them |
//! | `int_video_sota_keyframe` | `WAI_SOTA_INT_DIR` | `tools/wai_sota_vectors.sh` | yes — same job |
//! | `jpegai_e2e_full` | `WAI_JPEGAI_{CODESTREAM,WEIGHTS_DIR,NORM}` | `tools/wai_jpegai_fetch_e2e_inputs.sh` (the models root's `jpegai/v1/`, pinned by `tools/jpegai-e2e-v1.sha256`) | yes — `wai-jpegai-e2e.yml` fetches, checks and requires them |
//! | `jpegai_reconstruction_binding` | same, plus optional `WAI_JPEGAI_EXPECT_SHA256` | same | yes — same job, with the decoded-pixel hash pinned |
//! | `jpegai_configs` (the decodes) | `WAI_JPEGAI_{WEIGHTS_DIR,NORM}` | same | yes — same job, each vector's decode pinned by its manifest |
//! | `jpegai_live` | `WAI_JPEGAI_ASSETS` (default `../jpegai-vm-assets`) | the local JPEG-AI asset directory (not in the repository) | no — local only; also needs the `neural` + `codecs` features |
//!
//! Tests that still skip silently on absent inputs and are NOT covered by this
//! switch — an open gap, listed here so it stays visible:
//! `jpegai_*_calibration.rs`, `jpegai_e2e.rs`, `jpegai_e2e_{chain,chroma,dequant,
//! frontend,hyperlatent,synthesis}.rs`, `jpegai_mans_golden.rs`,
//! `jpegai_postfilter_chain.rs` (per-stage and staged JPEG-AI fixtures traced from
//! the reference software), `neural_native_decode.rs` (float ONNX decoders, models
//! and `/tmp` fixtures built per machine), and the `WAI_JPEGAI_*` unit tests in
//! `src/jpegai_synthesis.rs` and `src/neural/mod.rs`.
use std::io::Write;
use std::path::{Path, PathBuf};
pub const SWITCH: &str = "WAI_REQUIRE_SOTA_VECTORS";
/// Whether this run has declared that the out-of-tree inputs must be present.
pub fn required() -> bool {
match std::env::var(SWITCH) {
Ok(v) => !matches!(v.trim().to_ascii_lowercase().as_str(), "" | "0" | "false" | "no"),
Err(_) => false,
}
}
/// Call at the point a test would otherwise return early for lack of inputs.
/// Panics when [`required`]; otherwise announces the skip where it cannot be
/// missed. The caller returns (or skips the sub-check) after this.
pub fn absent(test: &str, missing: &str, produce: &str) {
let msg = format!("{test}: NOT VERIFIED — {missing}\n produce with: {produce}");
if required() {
panic!("{msg}\n {SWITCH} is set, so a missing input is a failure, not a skip");
}
// Deliberately not eprintln!: libtest captures the print macros of a passing
// test, which is how skips became invisible in the first place.
// wai-sota-vectors.yml and wai-jpegai-e2e.yml fail on "SKIPPED" or
// "NOT VERIFIED" in their test log, so keep both words in this banner.
let _ = writeln!(
std::io::stderr(),
"\n!!! SKIPPED — {msg}\n set {SWITCH}=1 to make a missing input fail\n"
);
}
/// Read a JSON input. `None` only when the file does not exist; a file that exists
/// but cannot be read or parsed is a broken input and fails the test either way.
#[allow(dead_code)]
pub fn load_json(p: &Path) -> Option<serde_json::Value> {
if !p.exists() {
return None;
}
let bytes = std::fs::read(p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()));
Some(serde_json::from_slice(&bytes).unwrap_or_else(|e| panic!("parse {}: {e}", p.display())))
}
/// Every named JSON file from `dir`, or `None` after [`InputDir::absent`] names
/// the ones missing (which fails instead when a variable named `dir`).
#[allow(dead_code)]
pub fn load_all<const N: usize>(
test: &str,
dir: &InputDir,
names: [&str; N],
produce: &str,
) -> Option<[serde_json::Value; N]> {
let loaded = names.map(|n| load_json(&dir.join(n)));
let missing: Vec<&str> =
names.iter().zip(&loaded).filter(|(_, v)| v.is_none()).map(|(n, _)| *n).collect();
if !missing.is_empty() {
dir.absent(test, &format!("{} not in {}", missing.join(", "), dir.display()), produce);
return None;
}
Some(loaded.map(Option::unwrap))
}
/// The environment variables a test reads its input paths from. `None` (after
/// [`absent`]) when any is unset. A variable that IS set but names a path that does
/// not exist is a misconfigured run — pointing a test at a file and having it pass
/// because the file is missing is the failure this module exists to prevent — so
/// that fails regardless of the switch, and whether or not the other variables are
/// set.
#[allow(dead_code)]
pub fn env_paths<const N: usize>(test: &str, vars: [&str; N], produce: &str) -> Option<[String; N]> {
let got = vars.map(|v| std::env::var(v).ok());
// The set ones first: a variable naming a missing path fails even when another
// variable the test reads is unset, which would otherwise end the test as a skip.
for (v, p) in vars.iter().zip(&got) {
if let Some(p) = p {
assert!(Path::new(p).exists(), "{test}: {v}={p} does not exist (produce with: {produce})");
}
}
let unset: Vec<&str> =
vars.iter().zip(&got).filter(|(_, g)| g.is_none()).map(|(v, _)| *v).collect();
if !unset.is_empty() {
absent(test, &format!("{} not set", unset.join(" + ")), produce);
return None;
}
Some(got.map(Option::unwrap))
}
/// The directory a test reads its inputs from, and whether a variable named it.
/// Made by [`env_dir`].
#[allow(dead_code)]
pub struct InputDir {
path: PathBuf,
/// The variable that named `path`; `None` when `path` is the default.
named_by: Option<String>,
}
#[allow(dead_code)]
impl InputDir {
pub fn path(&self) -> &Path {
&self.path
}
pub fn join(&self, name: impl AsRef<Path>) -> PathBuf {
self.path.join(name)
}
pub fn display(&self) -> std::path::Display<'_> {
self.path.display()
}
/// [`absent`] for an input that belongs in this directory. When a variable
/// named the directory, the run pointed the test at its inputs, so a missing
/// one fails whether or not the switch is set. Only the default directory may
/// be absent or incomplete and skip.
pub fn absent(&self, test: &str, missing: &str, produce: &str) {
if let Some(var) = &self.named_by {
panic!(
"{test}: NOT VERIFIED — {missing}\n produce with: {produce}\n \
{var} names this directory, so a missing input is a failure with or \
without {SWITCH}"
);
}
absent(test, missing, produce);
}
}
impl AsRef<Path> for InputDir {
fn as_ref(&self) -> &Path {
&self.path
}
}
/// The directory a test reads its inputs from: the variable `var` when it is set
/// and non-empty, else `default`. Empty counts as unset, as it does for
/// `tools/wai_sota_vectors.sh`'s own `${1:-/tmp/sota_int}`, so the script and the
/// tests fall back to the same place. A variable that IS set names the test's
/// inputs: it fails the test, regardless of the switch, when it does not name a
/// directory (as in [`env_paths`]), and the returned [`InputDir`] fails it when a
/// file is missing from that directory. The default may be absent or incomplete:
/// the test then goes through [`absent`].
#[allow(dead_code)]
pub fn env_dir(test: &str, var: &str, default: &str, produce: &str) -> InputDir {
match std::env::var(var) {
Ok(p) if !p.is_empty() => {
assert!(
Path::new(&p).is_dir(),
"{test}: {var}={p} is not a directory (produce with: {produce})"
);
InputDir { path: PathBuf::from(p), named_by: Some(var.to_owned()) }
}
_ => InputDir { path: PathBuf::from(default), named_by: None },
}
}