open-standards/wai/wai-rs/tests/audio_mark.rs
Transaction Science 63e856b226
Some checks failed
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 8m12s
WAI operation catalogue / does this change touch it (push) Successful in 8m3s
wai-app and Create / wai-app · does this change touch it (push) Successful in 8m7s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 8m21s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 8m24s
wai-audit collector and player integration / does this change touch it (push) Successful in 8m6s
wai-key-proxy / does this change touch it (push) Successful in 7m59s
wai-fleet service / does this change touch it (push) Successful in 8m2s
wai-quantum studio and desktop / does this change touch it (push) Successful in 1m40s
wai-ledger service / does this change touch it (push) Successful in 1m42s
wai-quantum for JavaScript / does this change touch it (push) Successful in 1m43s
wai-sink-android native sink library / does this change touch it (push) Successful in 1m41s
WAI Studio / does this change touch it (push) Successful in 1m40s
wai-sink-swift native sink package / does this change touch it (push) Successful in 1m41s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 46s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 1m40s
wai-fleet service / service · macos (push) Successful in 47s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 5m44s
wai-ledger service / service · macos (push) Successful in 50s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 4m37s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 7m9s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 10m50s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 10m5s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 8s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 8s
wai-audit collector and player integration / collector · linux (push) Successful in 1m50s
wai-key-proxy / proxy · linux (push) Successful in 1m37s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 3m28s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 9m6s
wai-quantum studio and desktop / studio · browser (push) Successful in 7m10s
wai-quantum for JavaScript / javascript · linux (push) Successful in 6m42s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 9m10s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m38s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 2m38s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 5m0s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m33s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 17m45s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 1m42s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 4m49s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 4m25s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 7m38s
wai: register wai-mark:band64, a watermark for sound, peer of tent64
synthesis §7.10 registers the second marking layer for speech and other audio: the same
8-byte payload and BCH code as tent64, the same marking and detection records and failure
codes (audio states rate, channels and length), embedded by a pinned integer derivation in
the band energies of the mono mix and read key-free after trims, rate and level changes and
the codecs §7.10.7 measures. robustness-evidence registers audio corpora, ten pinned
transforms of sound and the lossy codecs as measured transforms, whose records are stated.

Reference in wai-rs (synthesis::mark::audio, audio_mark, robustness::audio,
wai_audio_mark_vectors), second implementation in _wai_audio_mark.py with
audio_mark_verify.py, corpus in live-conformance/audio-mark/ with records measured on
generated voices and music and on recorded speech named by digest.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-08 16:20:24 -05:00

152 lines
6.5 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The audio scheme of the second marking layer, `wai-mark:band64` (extensions/synthesis.md
//! §7.10), and its corpus `live-conformance/audio-mark/`: every generated file regenerates byte
//! for byte and no other file but the measured records is there; every embedding, detection,
//! case and record gets what it states; §7.10.1's definition, §7.10.3's shuffle and §7.10.6's
//! bound are the reference's; and §7.10.7's tables are the measurements' and its claims attacks
//! every measurement shows survived.
use serde_json::Value;
use std::collections::BTreeMap;
use std::path::PathBuf;
use wai::audio_mark::corpus;
use wai::synthesis::mark::audio as band;
fn wai_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}
fn tree(root: &PathBuf) -> BTreeMap<String, Vec<u8>> {
let mut out = BTreeMap::new();
let mut stack = vec![root.clone()];
while let Some(d) = stack.pop() {
for e in std::fs::read_dir(&d).expect("the corpus directory") {
let p = e.expect("an entry").path();
if p.is_dir() {
stack.push(p);
continue;
}
let rel = p.strip_prefix(root).expect("under the root").to_string_lossy().replace('\\', "/");
if rel != "README.md" {
out.insert(rel, std::fs::read(&p).expect("a file"));
}
}
}
out
}
fn committed() -> BTreeMap<String, Vec<u8>> {
tree(&wai_dir().join("live-conformance/audio-mark"))
}
fn section(text: &str, head: &str) -> String {
let at = text.find(&format!("\n{head}")).unwrap_or_else(|| panic!("a section {head}"));
let level = head.chars().take_while(|c| *c == '#').count();
let end = text[at + 1..]
.match_indices("\n#")
.find(|(i, _)| text[at + 1 + i + 1..].chars().take_while(|c| *c == '#').count() <= level)
.map_or(text.len(), |(i, _)| at + 1 + i);
text[at..end].to_string()
}
fn fold(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
fn synthesis() -> String {
std::fs::read_to_string(wai_dir().join("extensions/synthesis.md")).expect("synthesis.md")
}
#[test]
fn the_committed_corpus_regenerates_from_its_seeds() {
let generated: BTreeMap<String, Vec<u8>> = corpus::generate().into_iter().collect();
let files = committed();
for k in generated.keys() {
assert!(files.get(k) == generated.get(k), "{k} differs from its regeneration, or is missing");
}
for k in files.keys() {
assert!(generated.contains_key(k) || k.starts_with("robustness/measured/"), "{k} is not generated");
}
}
#[test]
fn every_vector_case_and_record_gets_what_it_states() {
let fails = corpus::verify(&committed());
assert!(fails.is_empty(), "{fails:#?}");
}
#[test]
fn the_extension_states_the_references_definition_shuffle_and_bound() {
let s = synthesis();
let def = section(&s, "#### 7.10.1 ");
let block = def.split("```").nth(1).expect("the definition's block");
let joined: String = block.lines().map(str::trim).collect();
assert_eq!(joined, band::definition_text(), "§7.10.1's definition is the reference's");
let digest: String = band::digest().iter().map(|b| format!("{b:02x}")).collect();
assert!(fold(&def).contains(&format!("its digest is `{digest}`")), "§7.10.1 states the digest");
let lay = band::layout();
let head = format!("π begins {}, {}, {}, {}", lay.perm[0], lay.perm[1], lay.perm[2], lay.perm[3]);
assert!(fold(&section(&s, "#### 7.10.3 ")).contains(&head), "§7.10.3 states the shuffle's first entries");
let bound = corpus::false_positive_log2(corpus::ATTEMPTS);
assert!(fold(&section(&s, "#### 7.10.6 ")).contains(&format!("2^−{}", bound.trim_start_matches('-'))), "§7.10.6 states {bound}");
}
/// §7.10.7's tables, by their first data column's header: attack → column → cell.
fn tables(s: &str) -> BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>> {
let mut out: BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>> = BTreeMap::new();
let mut head: Option<Vec<String>> = None;
for line in s.lines() {
let cells: Vec<String> = line.trim().trim_matches('|').split('|').map(|c| c.trim().trim_matches('`').to_string()).collect();
if line.starts_with("| attack") {
out.entry(cells[1].clone()).or_default();
head = Some(cells);
} else if let (Some(h), true) = (&head, line.starts_with("| `")) {
let row: BTreeMap<String, String> = h[1..].iter().cloned().zip(cells[1..].iter().cloned()).collect();
out.get_mut(&h[1]).expect("a table").insert(cells[0].clone(), row);
} else if !line.starts_with('|') {
head = None;
}
}
out
}
#[test]
fn the_tables_are_the_measurements_and_the_claims_were_survived() {
let files = committed();
let mut rows: BTreeMap<(String, String), Value> = BTreeMap::new();
let mut add = |b: &[u8]| {
let v: Value = serde_json::from_slice(b).expect("a measurement");
for r in v["table"].as_array().expect("a table") {
rows.insert((r["attack"].as_str().unwrap_or("").into(), r["set"].as_str().unwrap_or("").into()), r.clone());
}
};
add(&files["battery-band64.json"]);
for (k, b) in &files {
if k.starts_with("robustness/measured/measurement-") {
add(b);
}
}
let text = section(&synthesis(), "#### 7.10.7 ");
let t = tables(&text);
assert!(t.contains_key("voice"), "§7.10.7 has the pinned attacks' table");
for table in t.values() {
for (attack, cells) in table {
for (col, cell) in cells {
let r = rows.get(&(attack.clone(), col.clone())).unwrap_or_else(|| panic!("no measurement of {attack} on {col}"));
let mut want = format!("{} / {}", r["survived"], r["items"]);
if r["survived"].as_u64() != Some(0) {
want += &format!(" ({})", r["errors_max"]);
}
assert_eq!(cell, &want, "§7.10.7 {attack}, {col}");
}
}
}
let f = fold(&text);
let claims = f.split("**claims to survive** ").nth(1).and_then(|x| x.split('.').next()).expect("§7.10.7's claims");
for a in claims.split('`').skip(1).step_by(2) {
let sets: Vec<&Value> = rows.iter().filter(|((at, _), _)| at == a).map(|(_, r)| r).collect();
assert!(!sets.is_empty(), "{a} is measured");
for r in sets {
assert!(r["survived"] == r["items"] && r["false_positives"] == 0, "§7.10.7 claims {a}, which {} did not survive", r["set"]);
}
}
}