//! The packer, end to end, over the golden `video-base-01` of `wai/pack-conformance`: //! byte-identical packages (test 5), every mutation's reason (test 6), deduplication //! (test 7), what a packager may emit (test 8), the floor's CMAF files (test 14), the //! measurement claim (test 17) and the receipts' JSON (test 18). use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use wai::stage_index::{Role, StageIndex}; use wai_pack::golden; fn corpus() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("../pack-conformance") } fn held(p: &Path) -> BTreeMap> { wai::pin::read_dir_set(p).unwrap() } fn golden_package() -> BTreeMap> { held(&corpus().join("packs/video-base-01/package")) } /// Test 5: packing the golden's inputs gives the committed package byte for byte, its /// LISTING included, and packing again gives the same bytes. #[test] fn the_golden_pack_is_byte_identical() { let a = golden::pack_golden(&corpus(), "video-base-01").unwrap(); let want = golden_package(); assert_eq!(a.files.keys().collect::>(), want.keys().collect::>()); for (p, b) in &want { assert!(a.files[p] == *b, "{p} differs from the committed golden"); } assert_eq!(a.files["LISTING"], want["LISTING"]); let b = golden::pack_golden(&corpus(), "video-base-01").unwrap(); assert_eq!(a.files, b.files, "a second pack writes the same bytes"); wai_pack::verify::verify_files(&want).unwrap(); } /// Test 6: every index, object and receipt case of `mutations/` gets its named reason. #[test] fn every_mutation_names_its_reason() { let mut files = held(&corpus().join("mutations")); let expected: serde_json::Value = serde_json::from_slice(&files.remove("expected.json").unwrap()).unwrap(); let cases = expected["cases"].as_array().unwrap(); for want in [ "refines-mismatch", "layer-gap", "tier-entropy-passthrough", "tier-weakest", "digest-without-tier", "dup-flag", "non-envelope-entry", "refines-outside-window", "index-truncated", "receipt-counts", "receipt-parent-kind", ] { assert!(cases.iter().any(|c| c["reason"] == want), "the corpus has a case for {want}"); } for c in cases { assert_eq!(golden::case_verdict(c, &files).unwrap(), c["reason"].as_str().unwrap(), "{}", c["name"]); } } /// A fresh pack of the golden's inputs. fn fresh_package() -> BTreeMap> { golden::pack_golden(&corpus(), "video-base-01").unwrap().files } /// Test 7: the bumper played twice is one object, so the package stores fewer bytes /// than it references, and every count is what the index gives. #[test] fn a_repeated_piece_is_one_object() { let pkg = fresh_package(); let ix = StageIndex::parse(&pkg["index-v.wsix"]).unwrap(); let base = |p: usize| ix.piece_entries(p).iter().find(|e| e.role == Role::Base).unwrap().content_hash; let floor = |p: usize| ix.piece_entries(p).iter().find(|e| e.role == Role::Floor).unwrap().content_hash; assert_eq!(base(1), base(3), "the bumper's base is one object"); assert_eq!(floor(1), floor(3), "and so is its floor"); assert_ne!(base(0), base(1)); let r = wai::package::PackageReceiptV2::from_json(std::str::from_utf8(&pkg["package.receipt.json"]).unwrap()).unwrap(); assert!(r.unique_objects < r.total_entries, "{} of {}", r.unique_objects, r.total_entries); let mut first: BTreeMap<[u8; 32], u64> = BTreeMap::new(); for e in &ix.entries { first.entry(e.content_hash).or_insert(e.byte_len); } assert_eq!(r.unique_objects, first.len() as u64); assert_eq!(r.stored_bytes, first.values().sum::()); assert_eq!(r.total_bytes, ix.entries.iter().map(|e| e.byte_len).sum::()); assert_eq!(pkg.keys().filter(|k| k.starts_with("objects/")).count() as u64, r.unique_objects); } /// Pack `policy`, with `royalty_free_only` forced on when `force_royalty_free_only`. fn pack_with(policy: &[u8], force_royalty_free_only: bool) -> Result { let d = corpus().join("packs/video-base-01"); let floor = held(&d.join("floor")); let key = ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]); let mut o = wai_pack::pack::PackOpts::new(key, "did:key:test", floor); o.overrides.royalty_free_only = force_royalty_free_only; let mut src = BTreeMap::new(); src.insert("v".to_string(), wai_pack::fixture::generate("video-base-01").unwrap()); wai_pack::pack::pack(policy, &src, &mut o) } fn golden_policy() -> String { String::from_utf8(std::fs::read(corpus().join("packs/video-base-01/policy.toml")).unwrap()).unwrap() } fn with_rung(cap: &str, role: &str) -> Vec { format!("{}\n[[ladder]]\nid = \"x\"\ncomponent = \"v\"\ncapability = \"{cap}\"\nrole = \"{role}\"\n", golden_policy()).into_bytes() } /// A policy with its `royalty_free_only = true` line taken out. fn not_royalty_free_only(p: &[u8]) -> Vec { let t = String::from_utf8(p.to_vec()).unwrap(); assert!(t.contains("royalty_free_only = true\n")); t.replacen("royalty_free_only = true\n", "", 1).into_bytes() } /// Test 8: a packager refuses, before it writes anything, a capability the registry /// does not classify as royalty-free when the policy asks for royalty-free only, and a /// record or a candidate whatever the policy asks. #[test] fn royalty_free_only_and_candidates_are_refused() { let code = |r: Result| r.map(|_| "ok".to_string()).unwrap_or_else(|e| e.code); let avc = with_rung("wai.video.avc", "floor"); assert_eq!(code(pack_with(&avc, false)), "license-refused:wai.video.avc:patent-pool"); // Without royalty_free_only it is not the licence that stops it; forced on by the // command line (§3.2), it is. let off = not_royalty_free_only(&avc); assert_ne!(code(pack_with(&off, false)), "license-refused:wai.video.avc:patent-pool"); assert_eq!(code(pack_with(&off, true)), "license-refused:wai.video.avc:patent-pool"); assert_eq!(code(pack_with(¬_royalty_free_only(&with_rung("wai.video.package", "floor")), false)), "capability-not-payload:wai.video.package"); assert_eq!(code(pack_with(¬_royalty_free_only(&with_rung("wai.splat.sog", "base")), false)), "capability-candidate:wai.splat.sog"); assert_eq!(code(pack_with(¬_royalty_free_only(&with_rung("wai.video.nope", "base")), false)), "capability-unknown:wai.video.nope"); // The command line writes nothing on a refusal. let dir = std::env::temp_dir().join(format!("wai-pack-refusal-{}", std::process::id())); let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); std::fs::write(dir.join("policy.toml"), &avc).unwrap(); std::fs::write(dir.join("key.hex"), "5a".repeat(32)).unwrap(); std::fs::write(dir.join("v.y4m"), wai_pack::fixture::generate("video-base-01").unwrap()).unwrap(); let out = std::process::Command::new(env!("CARGO_BIN_EXE_wai-pack")) .args(["pack", "--policy", dir.join("policy.toml").to_str().unwrap(), "--out", dir.join("out").to_str().unwrap(), "--key", dir.join("key.hex").to_str().unwrap()]) .arg("--source") .arg(format!("v={}", dir.join("v.y4m").display())) .arg("--floor") .arg(corpus().join("packs/video-base-01/floor")) .output() .unwrap(); assert!(!out.status.success()); assert!(String::from_utf8_lossy(&out.stderr).contains("license-refused:wai.video.avc:patent-pool"), "{}", String::from_utf8_lossy(&out.stderr)); assert!(!dir.join("out").exists(), "nothing is written"); let _ = std::fs::remove_dir_all(&dir); } fn boxes(b: &[u8]) -> Vec<([u8; 4], &[u8])> { wai_pack::cmaf::boxes(b).expect("boxes tile the file") } fn child<'a>(b: &'a [u8], path: &[&[u8; 4]]) -> &'a [u8] { let mut cur = b; for k in path { cur = boxes(cur).into_iter().find(|(t, _)| t == *k).unwrap_or_else(|| panic!("no {}", String::from_utf8_lossy(*k))).1; } cur } /// Test 14 (the structure; CI's media job reads the same files with a media prober): /// the initialization segment carries the floor's sequence header in its `av1C`, and /// each fragment holds its piece's frames at its start, a sync sample first and none /// after (the supplied floor has no extra key frame). #[test] fn the_floor_s_cmaf_files_hold_every_frame() { let pkg = fresh_package(); let ix = StageIndex::parse(&pkg["index-v.wsix"]).unwrap(); let init = &pkg["floor/v/init.mp4"]; let top = boxes(init); assert_eq!(&top[0].0, b"ftyp"); assert_eq!(&top[0].1[..4], b"cmfc"); let stsd = child(init, &[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"]); let av01 = &stsd[8..]; assert_eq!(&av01[4..8], b"av01"); let av1c = &av01[8 + 78..]; assert_eq!(&av1c[4..8], b"av1C"); let floor = std::fs::read(corpus().join("packs/video-base-01/floor/v.obu")).unwrap(); let tus = wai_pack::av1::temporal_units(&floor).unwrap(); assert_eq!(&av1c[12..], tus[0].sequence_header().unwrap().bytes); let playlist = String::from_utf8(pkg["hls/classic/floor-v.m3u8"].clone()).unwrap(); let frags: Vec<&str> = playlist.lines().filter(|l| !l.starts_with('#')).map(|l| l.trim_start_matches("../../")).collect(); assert_eq!(frags.len(), ix.pieces.len()); let mut total = 0; for (p, f) in frags.iter().enumerate() { let b = &pkg[*f]; assert_eq!(wai_pack::sha256_hex(b), f.trim_start_matches("floor/v/").trim_end_matches(".m4s"), "named by its SHA-256"); let tfdt = child(b, &[b"moof", b"traf", b"tfdt"]); assert_eq!(u64::from_be_bytes(tfdt[4..12].try_into().unwrap()), ix.pieces[p].start); let trun = child(b, &[b"moof", b"traf", b"trun"]); let n = u32::from_be_bytes(trun[4..8].try_into().unwrap()) as usize; assert_eq!(n as u32, ix.pieces[p].duration); let flags: Vec = (0..n).map(|i| u32::from_be_bytes(trun[12 + 12 * i + 8..12 + 12 * i + 12].try_into().unwrap())).collect(); assert_eq!(flags[0], wai_pack::cmaf::SYNC); assert!(flags[1..].iter().all(|&f| f == wai_pack::cmaf::NON_SYNC)); total += n; } assert_eq!(total, 200); } /// A meter for tests: a clock that advances one second between its reads, at 500 mW. fn model_meter() -> Box { let mut t = 0u64; Box::new(wai::energy_meter::ModelMeter::new( move || { let v = t; t += 1_000_000; v }, 500, )) } fn relist(files: &mut BTreeMap>) { files.remove("LISTING"); let view: BTreeMap = files.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect(); let l = wai::pin::listing_from(&view).unwrap(); files.insert("LISTING".into(), l); } /// Test 17: a metered pack writes its measurement claim and verifies; without the claim /// it is refused (`measurement-claim-missing`), with another key's claim too /// (`measurement-claim-invalid`); an unmetered pack needs none. #[test] fn a_metered_pack_carries_its_measurement_claim() { let d = corpus().join("packs/video-base-01"); let policy = std::fs::read(d.join("policy.toml")).unwrap(); let key = ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]); let id = wai::signer_id::did_key(&key.verifying_key().to_bytes()); let mut o = wai_pack::pack::PackOpts::new(key, id, held(&d.join("floor"))); o.meter = Some(model_meter()); o.power_reader = "model:500mW".into(); o.issued_at = "2026-10-06T00:00:00Z".into(); let mut src = BTreeMap::new(); src.insert("v".to_string(), wai_pack::fixture::generate("video-base-01").unwrap()); let out = wai_pack::pack::pack(&policy, &src, &mut o).unwrap(); assert_eq!(out.receipt.joules_micro, 500_000); let claim = wai_pack::pack::claim_path(&out.receipt); assert!(out.files.contains_key(&claim), "{claim}"); let rep = wai_pack::verify::verify_files(&out.files).unwrap(); assert!(rep.metered); let codes = |f: &BTreeMap>| wai_pack::verify::verify_files(f).err().unwrap_or_default().into_iter().map(|r| r.code).collect::>(); let mut missing = out.files.clone(); missing.remove(&claim); relist(&mut missing); assert_eq!(codes(&missing), vec!["measurement-claim-missing"]); // A claim about the figure signed by another key. let other = ed25519_dalek::SigningKey::from_bytes(&[0x77; 32]); let ix: Vec<&[u8]> = vec![&out.files["index-v.wsix"]]; use wai::energy_figure::EnergyFigures; let view = (&out.receipt, ix.as_slice()).figure_view().unwrap(); let forged = wai::claim_receipt::ClaimReceipt::seal_unchecked(&other, "x", view_subject_type(), view.identity, 0, "2026-10-06T00:00:00Z", forged_body(&view), None).unwrap(); let mut bad = out.files.clone(); bad.insert(claim.clone(), forged.to_json().into_bytes()); relist(&mut bad); assert_eq!(codes(&bad), vec!["measurement-claim-invalid"]); // The golden is unmetered and carries none. let g = golden_package(); assert!(!g.keys().any(|k| k.starts_with("claims/"))); assert!(!wai_pack::verify::verify_files(&g).unwrap().metered); } fn view_subject_type() -> wai::claim_receipt::SubjectType { wai::claim_receipt::SubjectType::Receipt } fn forged_body(view: &wai::energy_figure::FigureView) -> wai::claim_receipt::ClaimBody { let f = view.figure(None).unwrap(); wai::claim_receipt::ClaimBody::Measurement(wai::claim_receipt::MeasurementClaim { figure: wai::claim_receipt::FigureHeader::new(&view.profile, None, None, f.joules_micro, f.class.as_ref().unwrap()), bracket: None, build: wai::energy_report::BuildRecord::of_this_build(), power_reader: Some("model".into()), }) } /// Test 18: the golden's receipt JSON is RFC 8785 canonical and reads back to itself; the /// version-1 goldens read back to receipts that verify with their pinned hashes, but for /// those marked `"signature": "refused"`, whose signature WAI's rule refuses (SPEC §7 /// *Signatures*). #[test] fn the_receipts_json_round_trips() { let pkg = golden_package(); let text = std::str::from_utf8(&pkg["package.receipt.json"]).unwrap(); let r = wai::package::PackageReceiptV2::from_json(text).unwrap(); assert_eq!(r.to_json(), text); let v: serde_json::Value = serde_json::from_str(text).unwrap(); assert_eq!(wai::jcs::jcs(&v).unwrap(), text); let keys: Vec<&String> = v.as_object().unwrap().keys().collect(); let mut sorted = keys.clone(); sorted.sort(); assert_eq!(keys, sorted, "members in code-unit order"); let mut files = held(&corpus().join("receipts")); let expected: serde_json::Value = serde_json::from_slice(&files.remove("expected.json").unwrap()).unwrap(); for c in expected["cases"].as_array().unwrap() { let t = std::str::from_utf8(&files[c["file"].as_str().unwrap()]).unwrap(); let r1 = wai::wai_video_package::PackageReceipt::from_json(t).unwrap(); assert_eq!(r1.verify(), c["signature"] != "refused", "{}", c["file"]); assert_eq!(wai_pack::hex(&r1.receipt_hash()), c["receipt_hash"].as_str().unwrap()); assert_eq!(r1.to_json().unwrap(), t); } assert!(expected["cases"].as_array().unwrap().iter().any(|c| c["receipt_hash"] == "71a9da59a3202abb2c3fd22ee692b1485a9c674bb8c311f4c82455e6be29ae2b"), "the legacy pin is unchanged"); assert_eq!(expected["cases"].as_array().unwrap().iter().filter(|c| c["signature"] == "refused").count(), 3, "a key of small order, its second spelling, an R of small order"); }