//! Exact splats (extensions/splat-exact.md): the corpus regenerates byte for byte, every case //! gets its stated outcome, the extension's reason table lists exactly the reference's codes, //! the renderer's frame does not depend on the tiles' order, and the extension's draft schema //! names the members §3.2 gives the extension object. #![cfg(feature = "splat_exact")] use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use wai::splat_exact::{self as se, corpus, Reason}; fn wai_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") } fn walk(dir: &Path, rel: &str, out: &mut BTreeMap>) { for e in std::fs::read_dir(dir).expect("the corpus") { let e = e.unwrap(); let name = e.file_name().to_string_lossy().into_owned(); let r = if rel.is_empty() { name.clone() } else { format!("{rel}/{name}") }; if e.file_type().unwrap().is_dir() { walk(&e.path(), &r, out); } else if r != "README.md" { out.insert(r, std::fs::read(e.path()).unwrap()); } } } #[test] fn the_corpus_regenerates_and_every_case_gets_its_outcome() { let mut files = BTreeMap::new(); walk(&wai_dir().join("live-conformance/splat"), "", &mut files); let generated: BTreeMap> = corpus::generate().into_iter().collect(); let differ: Vec<&String> = files.keys().chain(generated.keys()).filter(|k| files.get(*k) != generated.get(*k)).collect(); assert!(differ.is_empty(), "files that do not regenerate: {differ:?}"); let fails = corpus::verify(&files); assert!(fails.is_empty(), "{fails:?}"); } #[test] fn the_reason_table_is_the_reference_codes() { let text = std::fs::read_to_string(wai_dir().join("extensions/splat-exact.md")).unwrap(); let start = text.find("## 10. Reasons").expect("§10"); let end = text[start..].find("\n## 11.").map(|i| start + i).unwrap(); let codes: Vec<&str> = text[start..end] .lines() .filter_map(|l| l.strip_prefix("| `")) .filter_map(|l| l.split('`').next()) .collect(); let want: Vec<&str> = Reason::ALL.iter().map(|r| r.code()).collect(); assert_eq!(codes, want); } #[test] fn tiles_do_not_change_a_frame() { for (name, planes, poses) in corpus::scenes() { let gs = se::gaussians(&planes).unwrap(); for p in &poses { let cam = se::Camera::new(p).unwrap(); let list = se::screens(&gs, p, &cam, se::Variant::default()); assert_eq!(se::composite(&list, p.width, p.height), se::composite_tiled(&list, p.width, p.height), "{name}"); } } } #[test] fn the_draft_schema_names_the_members() { let path = wai_dir().join("extensions/gltf/EXT_gaussian_splatting_compression_wai.schema.json"); let s: serde_json::Value = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); let mut props: Vec<&str> = s["properties"].as_object().unwrap().keys().map(String::as_str).collect(); props.sort(); assert_eq!(props, ["bufferView", "capability", "commitment", "extensions", "extras", "object", "set"]); let mut req: Vec<&str> = s["required"].as_array().unwrap().iter().map(|v| v.as_str().unwrap()).collect(); req.sort(); assert_eq!(req, ["bufferView", "capability", "object", "set"]); }