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
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]>
205 lines
9.9 KiB
Rust
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 },
|
|
}
|
|
}
|