//! Header probes (extensions/format-probes.md): the committed corpus `format-probe-conformance/` //! regenerates byte for byte from its external vectors and holds no other generated file, //! every vector, binding and fact gets its stated outcome, the extension's §2.4 and §12 tables //! are the reference's probes and codes, and projection.md §4 lists only codes of §12. #![cfg(feature = "format_probe")] use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use wai::format_probe::{corpus, Refusal, PROBES}; fn wai_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") } fn walk(root: &Path, dir: &Path, out: &mut BTreeMap>) { for e in std::fs::read_dir(dir).expect("a corpus directory").flatten() { let p = e.path(); if p.is_dir() { walk(root, &p, out); } else { let rel = p.strip_prefix(root).unwrap().to_string_lossy().replace('\\', "/"); out.insert(rel, std::fs::read(&p).unwrap()); } } } fn committed() -> BTreeMap> { let root = wai_dir().join("format-probe-conformance"); let mut out = BTreeMap::new(); for sub in ["ext", "gen"] { walk(&root, &root.join(sub), &mut out); } out.insert("vectors.json".into(), std::fs::read(root.join("vectors.json")).expect("vectors.json")); out } #[test] fn the_corpus_regenerates_and_every_outcome_holds() { let files = committed(); assert!(files.keys().filter(|k| k.starts_with("ext/")).count() >= 9, "the external vectors"); let fails = corpus::verify(&files); assert!(fails.is_empty(), "{}: run `wai_format_probe gen`", fails.join("\n")); } fn section(text: &str, heading: &str) -> String { let at = text.find(&format!("\n{heading}")).unwrap_or_else(|| panic!("{heading}")); let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e); text[at..end].to_string() } /// §2.4 lists exactly the probes and bases of `PROBES`, in order; §12 exactly the codes of /// `Refusal::ALL`, in order. #[test] fn the_extension_lists_the_probes_and_the_codes() { let text = std::fs::read_to_string(wai_dir().join("extensions/format-probes.md")).expect("the extension"); let rows: Vec<(String, String)> = section(&text, "## 2. Common rules") .lines() .filter(|l| l.starts_with("| `wai.")) .map(|l| { let cells: Vec<&str> = l.split('|').map(str::trim).collect(); (cells[1].trim_matches('`').to_string(), cells[2].to_string()) }) .collect(); let want: Vec<(String, String)> = PROBES.iter().map(|(c, b)| (c.to_string(), b.label().to_string())).collect(); assert_eq!(rows, want, "§2.4's table and PROBES differ"); let codes: Vec = section(&text, "## 12. Refusals") .lines() .filter(|l| l.starts_with("| `")) .filter_map(|l| l.split('`').nth(1).map(String::from)) .collect(); let want: Vec = Refusal::ALL.iter().map(|r| r.code().to_string()).collect(); assert_eq!(codes, want, "§12's table and Refusal::ALL differ"); // projection.md states the codes its reader and its binding check give. let proj = std::fs::read_to_string(wai_dir().join("extensions/projection.md")).expect("projection.md"); let checks = section(&proj, "## 4. Checks"); for r in [Refusal::NoSignature, Refusal::LengthMismatch, Refusal::ReservedValue, Refusal::DescriptorInvalid] { assert!(checks.contains(&format!("`{}`", r.code())), "projection.md §4 does not state {}", r.code()); } assert!(section(&proj, "## 5. Binding to the rendition").contains("`layer_absent`")); } fn sha256_hex(b: &[u8]) -> String { use sha2::{Digest, Sha256}; Sha256::digest(b).iter().map(|x| format!("{x:02x}")).collect() } /// Planted faults in the AV2, AC-4, IAMF and IVAS vectors (format-probes §14 to §17): a /// stated profile, level, version, bit rate or codec string changed, or a header byte changed /// with the file's digest restated so that only the outcome can catch it. `corpus::verify` /// must report each. #[test] fn planted_faults_in_the_av2_ac4_iamf_and_ivas_vectors_are_caught() { use serde_json::{json, Value}; let files = committed(); let doc: Value = serde_json::from_slice(&files["vectors.json"]).unwrap(); let find = |doc: &Value, name: &str| doc["vectors"].as_array().unwrap().iter().position(|v| v["name"] == name).unwrap_or_else(|| panic!("{name}")); let stated = [ ("main420_10_1080p", "/outcome/accept/profile", json!(1)), ("main420_10_1080p", "/outcome/accept/level", json!("4.1")), ("main420_10_1080p", "/outcome/accept/codec", json!("av02.0.08M.10")), ("v2_crc_24fps", "/outcome/accept/codec", json!("ac-4.02.01.04")), ("v2_crc_24fps", "/outcome/accept/presentation/version", json!(2)), ("opus_simple", "/outcome/accept/codec", json!("iamf.000.000.opus")), ("opus_simple", "/outcome/accept/primary_profile", json!(1)), ("stereo_32k_two_frames_cmr", "/outcome/accept/codec", json!("sevs")), ("stereo_32k_two_frames_cmr", "/outcome/accept/frames/0/bitrate", json!(24400)), ]; let mut caught = 0; for (name, ptr, value) in stated { let mut d = doc.clone(); let i = find(&d, name); *d["vectors"][i].pointer_mut(ptr).unwrap_or_else(|| panic!("{name}{ptr}")) = value; let mut f = files.clone(); f.insert("vectors.json".into(), serde_json::to_vec_pretty(&d).unwrap()); assert!(!corpus::verify(&f).is_empty(), "{name}{ptr} restated, and nothing fails"); caught += 1; } // Header bytes: the AV2 profile's last bit, the AC-4 frame rate index, the IAMF primary // profile, the IVAS ToC byte's bit rate. let bytes = [("main420_10_1080p", 4, 0x04u8, false), ("v0_single_44k", 5, 0x01, false), ("opus_simple", 6, 0x01, true), ("stereo_32k_two_frames_cmr", 1, 0x07, false)]; for (name, at, x, set) in bytes { let mut d = doc.clone(); let i = find(&d, name); let file = d["vectors"][i]["file"].as_str().unwrap().to_string(); let mut f = files.clone(); let mut b = f[&file].clone(); b[at] = if set { x } else { b[at] ^ x }; d["vectors"][i]["sha256"] = json!(sha256_hex(&b)); f.insert(file, b); f.insert("vectors.json".into(), serde_json::to_vec_pretty(&d).unwrap()); let fails = corpus::verify(&f); assert!(fails.iter().any(|m| m.starts_with(&format!("{name}: stated"))), "{name} byte {at}: {fails:?}"); caught += 1; } assert_eq!(caught, 13); }