//! `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 = BTreeSet::new(); let mut names: BTreeSet = 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 = (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 = text[at..end] .lines() .filter(|l| l.starts_with("| `")) .filter_map(|l| l.split('`').nth(1).map(String::from)) .collect(); let want: Vec = Reason::ALL.iter().map(|r| r.code().to_string()).collect(); assert_eq!(listed, want, "§10's table and Reason::ALL differ"); }