open-standards/wai/wai-rs/tests/live_compose.rs
2026-10-17 09:31:53 -04:00

81 lines
3.7 KiB
Rust

//! `wai.live.compose` (extensions/live-compose.md): the committed corpus
//! `live-conformance/compose/vectors.json` regenerates byte for byte from its fixed seeds,
//! every case in it gets the outcome it states, every reason the extension adds refuses a
//! case of it, the cases §11 names are there, and the extension's §10 lists exactly the
//! reasons of the reference.
#![cfg(feature = "live_compose")]
use std::collections::BTreeSet;
use std::path::PathBuf;
use wai::live_compose::{corpus, Reason};
fn wai_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}
fn committed() -> String {
std::fs::read_to_string(wai_dir().join("live-conformance/compose/vectors.json")).expect("the committed corpus")
}
#[test]
fn the_committed_corpus_regenerates_from_its_seeds() {
assert!(committed() == corpus::generate(), "live-conformance/compose/vectors.json differs from `wai_live_compose_vectors gen`");
}
#[test]
fn every_case_gets_its_stated_outcome() {
let fails = corpus::verify(&committed());
assert!(fails.is_empty(), "{}", fails.join("\n"));
}
/// Every reason this extension adds refuses a case; grids of 0 to 12 tiles, every fill
/// mode, every corner under both behaviours, and both sessions are there; and the grid
/// session lays its room out anew at the guest's grant exchange.
#[test]
fn the_corpus_covers_every_reason_and_case() {
let doc: serde_json::Value = serde_json::from_str(&committed()).unwrap();
let mut seen: BTreeSet<String> = BTreeSet::new();
let mut names: BTreeSet<String> = BTreeSet::new();
for section in ["layouts", "mixes", "descriptors", "compositions", "receipts"] {
for v in doc[section].as_array().unwrap() {
names.insert(v["name"].as_str().unwrap().to_string());
if let Some(r) = v["reason"].as_str() {
seen.insert(r.into());
}
}
}
for r in Reason::ALL {
assert!(seen.contains(r.code()), "nothing in the corpus is refused {r}");
}
let mut want: Vec<String> = (0..=12).map(|n| format!("grid_{n}")).collect();
want.extend(["fill_fill", "fill_cover", "fill_contain", "featured_4", "featured_absent", "order_key_tie_break", "stopped_omitted"].map(String::from));
for c in ["top_left", "top_right", "bottom_left", "bottom_right"] {
for b in ["static", "dynamic"] {
want.push(format!("pip_{c}_{b}"));
}
}
want.extend(["pip_alone_static", "pip_alone_dynamic", "sum_saturates_high", "grid_session", "pip_session", "video", "audio"].map(String::from));
for n in &want {
assert!(names.contains(n), "no case {n}");
}
let session = doc["compositions"].as_array().unwrap().iter().find(|v| v["name"] == "grid_session").unwrap();
let frames = session["frames"].as_array().unwrap();
let at = |t: u64| frames.iter().find(|f| f["t"] == t).unwrap();
assert_ne!(at(4_000_000)["order"], at(5_000_000)["order"], "the guest's exchange at 5 s reorders the room");
assert_ne!(at(4_000_000)["set_digest"], at(5_000_000)["set_digest"]);
}
/// §10 of the extension lists exactly the codes `Reason::ALL` has, each once, in order.
#[test]
fn the_extension_lists_the_reasons() {
let text = std::fs::read_to_string(wai_dir().join("extensions/live-compose.md")).expect("the extension");
let at = text.find("\n## 10. Reasons").expect("a §10");
let end = text[at + 1..].find("\n## ").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> = Reason::ALL.iter().map(|r| r.code().to_string()).collect();
assert_eq!(listed, want, "§10's table and Reason::ALL differ");
}