open-standards/wai/wai-rs/tests/audio_renditions.rs
Transaction Science ffa2d01c4f wai-pack writes marks: a policy option, off by default, with the document named in the listing and the receipt
packaging §8.12: a package whose policy has a `marks` table holds its lead's marks (§8.11). The
table declares the chapters (`marks.lang`, each `marks.chapter`'s `title` and `from_ms`) and
`marks.program_date_time_ms`. An absent table is no member of the effective policy, so every
policy digest, receipt and golden written before is unchanged, byte for byte.

With the table, wai-pack writes `marks-<lead>.json` (the document) and `marks-<lead>.vtt` (its
chapters), both lines of LISTING; the marks' EXT-X-PROGRAM-DATE-TIME and EXT-X-DATERANGE lines in
every media playlist of the lead, after its header tags and before its first segment; and their
EventStreams in every MPD, before the Period's AdaptationSet. The version-2 package receipt gains
one optional member, `marks`, the document's digest, and its signing payload ends with
`u8 1 ‖ marks[32]` when it has one; a receipt without it has the payload, signature and hash it
had. The packager refuses a policy whose marks do not derive (marks-chapter, marks-index,
marks-hls), and the live packer refuses the table. Mode F and the protected floor are as they were.

A package's verifier (§11.3) derives the marks again from the lead's stage index, the effective
policy and the policy's chapters and compares byte for byte: nothing of marks without the table
(marks-unexpected); with it, the files and the receipt's member (marks-missing), the document read
(marks-malformed) and the lead's (marks-package), the derivation and the document (marks-mismatch),
the receipt's digest (marks-receipt), the chapters file (marks-vtt), each playlist's date ranges
(marks-hls) and each MPD's event streams (marks-dash); where the lines sit is the catalogs'
comparison. §3.4 gains item 14 (policy-marks).

The golden is pack-conformance/packs/video-marks-01 (the staged golden's fixture and ladder with
three chapters) and its planted faults pack-conformance/marks-mutations, twenty cases: the marks of
another package, date ranges and an event stream that disagree with the document, a chapter moved
off its piece start, a shot left out, marks present with the option off in the policy digest, and
the rest, each refused with its reason by wai-pack and by pack_verify.py, which reads the table,
the receipt's member and payload, derives the marks with marks_verify.py, places them in the
catalogs it renders and leaves each of its own checks out in turn. policy_diff.py draws the table
in its random policies.

Also: tests/audio_renditions.rs and tests/pack_marks.rs read their reasons tables up to the next
item of §8, as the Python verifiers do; the former had been reading §8.11's tables since marks
were added.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
2026-11-11 07:25:00 -05:00

53 lines
2.5 KiB
Rust

//! Audio renditions (extensions/packaging.md §8.10): the committed corpus
//! `pack-conformance/renditions/` regenerates byte for byte, holds no other file, every case in it
//! gets the outcome it states, and §8.10.6 lists exactly the reasons the reference reports.
#![cfg(feature = "audio_renditions")]
use std::collections::BTreeMap;
use std::path::PathBuf;
use wai::audio_renditions::{corpus, CODES};
fn wai_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}
fn committed() -> BTreeMap<String, String> {
let dir = wai_dir().join("pack-conformance/renditions");
std::fs::read_dir(&dir)
.expect("the committed corpus")
.flatten()
.map(|e| (e.file_name().to_string_lossy().into_owned(), e.path()))
.filter(|(n, _)| n != "README.md")
.map(|(n, p)| (n, std::fs::read_to_string(p).expect("a text file")))
.collect()
}
#[test]
fn the_committed_corpus_regenerates_from_its_seeds() {
let files = committed();
let generated: BTreeMap<String, String> = corpus::generate().into_iter().collect();
let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect();
let missing: Vec<&String> = generated.keys().filter(|k| !files.contains_key(*k)).collect();
let differ: Vec<&String> = generated.iter().filter(|(k, v)| files.get(*k).is_some_and(|f| f != *v)).map(|(k, _)| k).collect();
assert!(extra.is_empty() && missing.is_empty() && differ.is_empty(), "extra {extra:?}, missing {missing:?}, differ {differ:?}: run `wai_audio_renditions_vectors gen`");
}
#[test]
fn every_case_gets_its_stated_outcome() {
let fails = corpus::verify(&committed());
assert!(fails.is_empty(), "{}", fails.join("\n"));
}
/// §8.10.6 lists exactly the codes `CODES` has, each once, in order.
#[test]
fn the_extension_lists_the_reasons() {
let text = std::fs::read_to_string(wai_dir().join("extensions/packaging.md")).expect("the extension");
let at = text.find("\n**8.10.6**").expect("§8.10.6");
// The table ends where the next item of §8 begins (§8.11, marks), as renditions_verify.py
// reads it.
let end = text[at + 1..].find("\n**8.11**").map_or(text.len(), |e| at + 1 + e);
let listed: Vec<String> =
text[at..end].lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect();
let want: Vec<String> = CODES.iter().map(|c| c.to_string()).collect();
assert_eq!(listed, want, "§8.10.6's table and CODES differ");
}