#![allow( clippy::unwrap_used, clippy::expect_used, clippy::panic, reason = "golden-table runner: assert-and-continue reporting is the point" )] //! The golden-table runner. //! //! Every case is evaluated against the engine and any mismatch is reported with //! enough context to diagnose without re-running: what was expected, what //! happened, and — for wakeups — why the difference matters. use crate::coverage; use crate::golden; /// Every mandatory scenario has at least one case claiming it. #[test] fn every_required_scenario_is_covered() { let uncovered = coverage::uncovered(); assert!( uncovered.is_empty(), "these CLAUDE.md section 7 scenarios have no golden case: {uncovered:?}" ); } /// No case claims a scenario that is not on the mandatory list. #[test] fn no_case_claims_an_unknown_scenario() { let unknown = coverage::unknown_claims(); assert!( unknown.is_empty(), "these `covers` tags are not in coverage::REQUIRED — a typo here means a \ case is testing something unnamed, which is how coverage quietly rots: {unknown:?}" ); } /// Every case passes. /// /// Written as one test with all failures reported rather than one test per case: /// a table that has drifted usually drifts in several places at once, and fixing /// them one rebuild at a time hides the pattern. #[test] fn every_golden_case_passes() { let cases = golden::all(); assert!(!cases.is_empty(), "the table set must not be empty"); let mut failures = Vec::new(); for case in &cases { if let Some(reason) = case.check() { failures.push(format!(" [{}] {}\n{reason}", case.covers, case.name)); } } assert!( failures.is_empty(), "{} of {} golden cases failed:\n{}", failures.len(), cases.len(), failures.join("\n\n") ); } /// Case names are unique, so a failure message identifies exactly one case. #[test] fn case_names_are_unique() { let mut seen = std::collections::BTreeMap::new(); for case in golden::all() { let key = case.name.clone(); let n = seen.entry(key).or_insert(0); *n += 1; } let dupes: Vec<&String> = seen.iter().filter(|(_, n)| **n > 1).map(|(k, _)| k).collect(); assert!(dupes.is_empty(), "duplicate case names make failure messages ambiguous: {dupes:?}"); } /// The fixture list in `coverage` still matches reality. /// /// `FIXTURES` is maintained by hand, so a rename that misses it would silently /// drop the coverage claim. Rather than re-parsing the source, this checks the /// shapes those fixtures are responsible for still hold. #[test] fn fixture_backed_scenarios_are_still_claimed() { use blocker_core::lock::{LockLevel, MAX_WAIT_MS}; use blocker_core::model::{Schedule, ScheduleMode, Target}; // The 24-hour cap, asserted directly rather than trusting the fixture list. for level in [LockLevel::Normal, LockLevel::Strict, LockLevel::Stubborn] { let state = blocker_core::lock::LockState { level, schedules_locked: false, pending: None, cooldown_remaining: None, attempts_today: 0, keyholder_code: None, }; assert!(state.worst_case_wait() <= MAX_WAIT_MS, "{level:?} exceeds the section 1.5.4 cap"); } // And the never-block guarantee, restated here so removing the corresponding // fixture cannot quietly drop the guarantee it protects. assert!( blocker_core::never_block::is_never_blocked("com.android.dialer") || blocker_core::never_block::is_never_blocked("plasmashell"), "the never-block list lost its entries" ); let _ = Schedule { id: "unused".into(), name: "unused".into(), icon: None, mode: ScheduleMode::Blocklist, targets: vec![Target::App("x".into())], conditions: vec![], paused: true, }; } /// The cases that carry the section 5 idle budget must declare their wakeup. /// /// `Wakeup::Any` is the default for a case that is about blocking behaviour, so /// a missing wakeup assertion is not an error in general. It *is* an error for /// the cases below: they exist to prove that the daemon sleeps, and a case that /// silently stops asserting its wakeup is a lost guarantee rather than an /// untested one. Named explicitly so deleting one is a deliberate act. #[test] fn idle_budget_cases_declare_their_wakeup() { use crate::golden::Wakeup; const MUST_ASSERT_WAKEUP: &[&str] = &[ "wakeup: a Wi-Fi schedule arms no timer, only an OS event can change it", "wakeup: an inactive schedule still arms one for when it starts", "start == end is a zero-length window, so it blocks nothing", ]; let cases = golden::all(); for name in MUST_ASSERT_WAKEUP { let case = cases .iter() .find(|c| c.name == *name) .unwrap_or_else(|| panic!("the idle-budget case {name:?} was deleted")); assert_ne!( case.expect.next_wakeup, Wakeup::Any, "{name:?} no longer asserts a wakeup, which is the whole point of it" ); } }