open-standards/wai/wai-pack/tests/cmaf.rs
Transaction Science 5757156dd4 wai-pack tests/cmaf.rs: the width case edits the sample entry; the binding case reseals by rename
Two of the review-fix tests were wrong, not the checks:
- the "sample entry wider" case (#426 review S2) took the first `av01` in the init,
  which is ftyp's compatible brand, so it edited ftyp and the pack was accepted; it now
  finds the av01 entry after stsd (64x36 on video-cmaf-01's hi init);
- the_package_binds_the_supplied_bytes replaced a segment and resealed with the plain
  reseal, which now (#426 review S6) refuses a supplied floor that does not check; it
  reseals with golden::reseal_renamed, as the corpus case does, so the package's one
  fault is the binding, which the verifier still refuses (floor-cmaf-mismatch).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 04:48:09 -04:00

200 lines
11 KiB
Rust

//! A supplied floor in the CMAF form (packaging §7.7): the golden `video-cmaf-01` takes
//! `video-ladder-01`'s two AV1 streams as a media muxer wrote them into fMP4 segments, and
//! its floor's CMAF files are those segments, byte for byte, bound to the receipt by the
//! supplied floor's listing, which the package carries. Each fault of a supplied CMAF floor
//! is refused by name, and a package whose segments are not the supplied ones is refused.
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use wai::stage_index::{Role, StageIndex};
type Files = BTreeMap<String, Vec<u8>>;
fn corpus() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../pack-conformance")
}
fn held(p: &Path) -> Files {
wai::pin::read_dir_set(p).unwrap()
}
fn golden() -> Files {
held(&corpus().join("packs/video-cmaf-01/package"))
}
fn supplied() -> Files {
held(&corpus().join("packs/video-cmaf-01/floor"))
}
fn policy() -> String {
String::from_utf8(std::fs::read(corpus().join("packs/video-cmaf-01/policy.toml")).unwrap()).unwrap()
}
/// Pack the golden's fixture under `policy` over the supplied floor `floor` (its listing
/// pinned in the policy).
fn pack(policy: &str, floor: Files) -> Result<wai_pack::pack::PackOutput, wai_pack::pack::PackError> {
let listing = wai_pack::floor::listing_sha256(&floor).unwrap();
let at = policy.find("supplied_listing = \"").unwrap() + "supplied_listing = \"".len();
let policy = format!("{}{listing}{}", &policy[..at], &policy[at + 64..]);
let mut o = wai_pack::pack::PackOpts::new(ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]), "did:key:test", floor);
let mut src = BTreeMap::new();
src.insert("v".to_string(), wai_pack::fixture::generate("video-base-01").unwrap());
wai_pack::pack::pack(policy.as_bytes(), &src, &mut o)
}
fn code(r: Result<wai_pack::pack::PackOutput, wai_pack::pack::PackError>) -> String {
r.map(|_| "ok".to_string()).unwrap_or_else(|e| e.code)
}
/// Where box `kind` begins in `b` (its size field).
fn box_at(b: &[u8], kind: &[u8; 4]) -> usize {
b.windows(4).position(|w| w == kind).unwrap() - 4
}
/// The floor's CMAF files are the supplied ones, byte for byte, and `floor.listing` is the
/// supplied listing; each floor object holds its piece's samples as temporal units, which
/// are exactly the OBU form's units of the same streams (`video-ladder-01`'s).
#[test]
fn a_cmaf_floor_is_the_ladder_s_own_bytes() {
let pkg = golden();
wai_pack::verify::verify_files(&pkg).unwrap();
let sup = supplied();
let view: BTreeMap<String, &[u8]> = sup.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect();
assert_eq!(pkg["floor.listing"], wai::pin::listing_from(&view).unwrap());
let ix = StageIndex::parse(&pkg["index-v.wsix"]).unwrap();
let ladder = held(&corpus().join("packs/video-ladder-01/floor"));
for (k, name) in ["hi", "lo"].iter().enumerate() {
assert_eq!(pkg[&format!("floor/{name}/init.mp4")], sup[&format!("{name}/init.mp4")]);
let segs: Vec<&String> = sup.keys().filter(|p| p.starts_with(&format!("{name}/seg_"))).collect();
assert_eq!(segs.len(), ix.pieces.len());
let tus: Vec<Vec<u8>> = wai_pack::av1::temporal_units(&ladder[&format!("{name}.obu")]).unwrap().iter().map(|t| t.bytes()).collect();
for (p, seg) in segs.iter().enumerate() {
let b = &sup[*seg];
assert_eq!(&pkg[&format!("floor/{name}/{}.m4s", wai_pack::sha256_hex(b))], b, "{seg}, as supplied");
let fe = ix.piece_entries(p).iter().filter(|e| e.role == Role::Floor).nth(k).unwrap();
let w = wai::container::Wai::from_bytes(&pkg[&wai_pack::objects::object_path(&fe.content_hash)]).unwrap();
let (units, _) = wai_pack::floor::av1_units(&w.payload).unwrap();
let (s, n) = (ix.pieces[p].start as usize, ix.pieces[p].duration as usize);
assert_eq!(units, tus[s..s + n].to_vec(), "piece {p} of {name}: the stream's own units");
}
}
let master = String::from_utf8(pkg["hls/classic/master.m3u8"].clone()).unwrap();
assert!(master.contains("../classic/floor-hi.m3u8") && master.contains("../classic/floor-lo.m3u8"));
}
/// §7.7's refusals, each before anything is written.
#[test]
fn a_supplied_cmaf_floor_s_faults_are_refused() {
let p = policy();
let sup = supplied();
assert_eq!(code(pack(&p, sup.clone())), "ok");
let with = |edit: &dyn Fn(&mut Files)| {
let mut f = sup.clone();
edit(&mut f);
code(pack(&p, f))
};
assert_eq!(with(&|f| { f.remove("lo/init.mp4"); }), "floor-stream-missing");
assert_eq!(with(&|f| { f.insert("hi/index.m3u8".into(), b"#EXTM3U\n".to_vec()); }), "floor-cmaf-malformed");
assert_eq!(with(&|f| { f.insert("hi/x/seg.m4s".into(), Vec::new()); }), "floor-cmaf-malformed", "a segment in a directory below");
assert_eq!(with(&|f| {
let b = f.get_mut("hi/seg_0002.m4s").unwrap();
b[..4].copy_from_slice(&0u32.to_be_bytes());
}), "floor-cmaf-malformed", "a box of size 0");
assert_eq!(with(&|f| {
let b = f.get_mut("hi/seg_0002.m4s").unwrap();
let t = box_at(b, b"trun");
b[t + 24..t + 28].copy_from_slice(&0x0101_0000u32.to_be_bytes());
}), "floor-cmaf-malformed", "a segment that does not open on a sync sample");
assert_eq!(with(&|f| {
let b = f.get_mut("hi/seg_0002.m4s").unwrap();
let t = box_at(b, b"tfhd");
let d = u32::from_be_bytes(b[t + 16..t + 20].try_into().unwrap());
b[t + 16..t + 20].copy_from_slice(&(d + 1).to_be_bytes());
}), "floor-cmaf-timing", "a sample longer than one frame");
// Every sample a sync sample by tfhd's default flags: the non-key frames after each
// segment's first are flagged sync (#426 review S3).
assert_eq!(with(&|f| {
let b = f.get_mut("hi/seg_0002.m4s").unwrap();
let t = box_at(b, b"tfhd");
assert_eq!(u32::from_be_bytes(b[t + 8..t + 12].try_into().unwrap()) & 0xff_ffff, 0x02_0038, "default duration, size and flags");
b[t + 24..t + 28].copy_from_slice(&0x0200_0000u32.to_be_bytes());
}), "floor-cmaf-malformed", "a non-key frame flagged as a sync sample");
// The init's av1C and sample entry against the stream's sequence header (#426 review S2):
// its level one higher, and its width one wider.
assert_eq!(with(&|f| {
let b = f.get_mut("hi/init.mp4").unwrap();
let c = box_at(b, b"av1C");
b[c + 9] += 1;
}), "floor-cmaf-malformed", "an av1C whose level is not the sequence header's");
assert_eq!(with(&|f| {
let b = f.get_mut("hi/init.mp4").unwrap();
// The sample entry, after stsd: `ftyp` names `av01` among its brands first.
let st = box_at(b, b"stsd");
let e = st + b[st..].windows(4).position(|w| w == b"av01").unwrap() - 4;
let w = u16::from_be_bytes([b[e + 32], b[e + 33]]);
b[e + 32..e + 34].copy_from_slice(&(w + 1).to_be_bytes());
}), "floor-cmaf-malformed", "a sample entry wider than the sequence header's frames");
assert_eq!(with(&|f| { f.remove("lo/seg_0004.m4s"); }), "floor-frame-count");
// Pieces of at most two seconds: the first segment holds two pieces.
let short = p.replace("max_ms = 4000", "max_ms = 2000").replace("target_ms = 2000", "target_ms = 1000");
assert_eq!(code(pack(&short, sup.clone())), "floor-cmaf-pieces");
// Without supplied_form, the floor is read in the OBU form, which holds no stream.
assert_eq!(code(pack(&p.replace("supplied_form = \"cmaf\"\n", ""), sup)), "floor-stream-missing");
}
/// A package whose segment is not the supplied one is refused, though it is a segment of
/// the same samples: the receipt binds the supplied bytes through `floor.listing`.
#[test]
fn the_package_binds_the_supplied_bytes() {
let mut f = golden();
let seg = f.keys().find(|k| k.starts_with("floor/hi/") && k.ends_with(".m4s")).unwrap().clone();
let mut b = vec![0, 0, 0, 8, b'f', b'r', b'e', b'e'];
b.extend_from_slice(&f[&seg]);
f.remove(&seg);
let replaced = format!("floor/hi/{}.m4s", wai_pack::sha256_hex(&b));
f.insert(replaced.clone(), b);
let key = wai_pack::unhex(std::fs::read_to_string(corpus().join("packs/video-cmaf-01/key.hex")).unwrap().trim()).unwrap();
// Resealed with catalogs naming the replacement, so the one fault is its binding.
let renames = BTreeMap::from([(seg.clone(), replaced)]);
wai_pack::golden::reseal_renamed(&mut f, &ed25519_dalek::SigningKey::from_bytes(&key.try_into().unwrap()), None, &renames).unwrap();
let got: Vec<String> = wai_pack::verify::verify_files(&f).err().unwrap_or_default().into_iter().map(|r| r.code).collect();
assert_eq!(got.first().map(String::as_str), Some("floor-cmaf-mismatch"), "{got:?}");
}
/// Packed into a directory with `link_supplied`, the floor's CMAF files are the supplied
/// files themselves (hard links where the file system allows): the package verifies, and
/// on a file system of inodes each shares the supplied file's.
#[test]
fn a_linked_floor_is_the_supplied_files() {
let dir = std::env::temp_dir().join(format!("wai-pack-cmaf-link-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let policy = std::fs::read(corpus().join("packs/video-cmaf-01/policy.toml")).unwrap();
let key = wai_pack::unhex(std::fs::read_to_string(corpus().join("packs/video-cmaf-01/key.hex")).unwrap().trim()).unwrap();
let key = ed25519_dalek::SigningKey::from_bytes(&key.try_into().unwrap());
let id = wai::signer_id::did_key(&key.verifying_key().to_bytes());
// The supplied floor copied beside the output, so a link is on one file system.
let sup = dir.join("floor");
for (p, b) in supplied() {
std::fs::create_dir_all(sup.join(&p).parent().unwrap()).unwrap();
std::fs::write(sup.join(&p), b).unwrap();
}
let mut o = wai_pack::pack::PackOpts::with_floor(key, id, wai_pack::files::FileSet::Dir(sup.clone()));
o.link_supplied = true;
let fixture = wai_pack::fixture::generate("video-base-01").unwrap();
let mut src = BTreeMap::new();
src.insert("v".to_string(), wai_pack::files::Input::Bytes(&fixture));
let out = dir.join("out");
let mut sink = wai_pack::files::DirSink::new(&out).unwrap();
wai_pack::pack::pack_into(&policy, &src, &mut o, &mut sink).unwrap();
sink.keep();
assert_eq!(held(&out), golden(), "the golden, byte for byte");
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt as _;
let ino = |p: &Path| std::fs::metadata(p).unwrap().ino();
assert_eq!(ino(&out.join("floor/hi/init.mp4")), ino(&sup.join("hi/init.mp4")));
let s0 = std::fs::read(sup.join("lo/seg_0000.m4s")).unwrap();
assert_eq!(ino(&out.join(format!("floor/lo/{}.m4s", wai_pack::sha256_hex(&s0)))), ino(&sup.join("lo/seg_0000.m4s")));
}
let _ = std::fs::remove_dir_all(&dir);
}