//! The published adaptive vector is what the oracles produce, the reference //! engine passes it, and the harness notices when it is tampered with. use std::path::PathBuf; use aac_conformance::{Report, adaptive, default_vector_dir, load_vector, run_with_vectors}; use serde_json::Value; fn published() -> Value { load_vector(&default_vector_dir(), "adaptive.json").expect("conformance/v1/adaptive.json") } /// A copy of the published vectors with `adaptive.json` replaced by `doc`. fn with_adaptive(doc: &Value, tag: &str) -> PathBuf { let dir = std::env::temp_dir().join(format!("aac-conformance-adaptive-{tag}-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); for f in ["cascade.json", "scan_rate.json", "dwell.json"] { std::fs::copy(default_vector_dir().join(f), dir.join(f)).unwrap(); } std::fs::write(dir.join("adaptive.json"), serde_json::to_vec_pretty(doc).unwrap()).unwrap(); dir } fn failures(r: &Report, prefix: &str) -> Vec { r.lines.iter().filter(|l| l.contains("FAIL") && l.contains(prefix)).cloned().collect() } #[test] fn the_published_vector_is_the_oracles_output() { // Byte for byte, as gen-vectors writes it (serde_json's default parser is // not correctly rounded, so comparing parsed floats would be unsound). let on_disk = std::fs::read_to_string(default_vector_dir().join("adaptive.json")).unwrap(); let generated = serde_json::to_string_pretty(&adaptive::adaptive_vector()).unwrap() + "\n"; assert!(on_disk == generated, "run gen-vectors: conformance/v1/adaptive.json is stale"); } #[test] fn the_reference_engine_passes_c8_to_c11() { let r = run_with_vectors(&default_vector_dir()); for c in ["C8", "C9", "C10", "C11"] { assert!(failures(&r, c).is_empty(), "{:?}", failures(&r, c)); assert!(r.lines.iter().any(|l| l.contains(&format!("PASS {c} "))), "{c} ran no checks"); } } #[test] fn a_tampered_order_fails_c8() { let mut doc = published(); let units = doc["order_cases"][0]["expected_units"].as_array_mut().unwrap(); units.swap(0, 1); let r = run_with_vectors(&with_adaptive(&doc, "order")); assert!(!failures(&r, "C8").is_empty()); } #[test] fn a_tampered_decision_fails_c11() { let mut doc = published(); doc["selection_cases"][0]["expected_decision"]["option"] = 2.into(); let r = run_with_vectors(&with_adaptive(&doc, "decision")); assert!(!failures(&r, "C11").is_empty()); } #[test] fn a_vector_claiming_adaptation_is_on_by_default_fails_c9() { let mut doc = published(); doc["rule"]["adaptive_order_default"] = true.into(); let r = run_with_vectors(&with_adaptive(&doc, "default")); assert!(!failures(&r, "C9").is_empty()); } #[test] fn a_dwell_outside_the_bounds_fails_c10() { let mut doc = published(); doc["dwell_cases"][0]["expected_dwell_ms"][0][2] = 500.into(); // below 640 let r = run_with_vectors(&with_adaptive(&doc, "dwell")); assert!(!failures(&r, "C10").is_empty()); } #[test] fn a_vector_publishing_another_offset_window_fails_c11() { let mut doc = published(); doc["rule"]["selection"]["offset_window_intervals"] = serde_json::json!([-0.5, 10.0]); let r = run_with_vectors(&with_adaptive(&doc, "window")); assert!(!failures(&r, "C11").is_empty()); } #[test] fn proposed_checks_are_labelled_and_counted() { let r = run_with_vectors(&default_vector_dir()); let labelled = r.lines.iter().filter(|l| l.contains("[proposed]")).count(); assert!(labelled > 0 && labelled as u32 == r.proposed, "{labelled} labelled, {} counted", r.proposed); assert!(r.lines.iter().filter(|l| l.contains(" C5 ")).all(|l| !l.contains("[proposed]"))); }