//! **The reference file is the test oracle** (decision 0675) — the shared half of it. //! //! A transcribed window's geometry errors are invisible to every behavioural test: the frames //! load, the buttons click, the lists populate, and only the *look* is wrong. 0675 built the //! detector for `FriendsFrame` after two rounds of the director's eye caught what a one-number //! diff would have caught on its first run, and closed with "for the next transcribed window, //! write the diff test first". This is that scraper, lifted out of `friends_tests` so the next //! window costs a call rather than a copy (`QuestTimerFrame` is the first to pay the lower price). //! //! Deliberately narrow: it does not try to understand the XML, only to notice that a number moved. //! Two properties make it a guard rather than a comfort, and both are the caller's to keep — //! **known-benign differences are an explicit list, never a pattern**, so a new difference cannot //! hide inside a tolerance; and **it must be verified to fail** (perturb one number by 1 px and //! watch it name the element). use std::collections::HashMap; use std::path::{Path, PathBuf}; /// The extracted reference FrameXML file of that name, or `None` when the install isn't there /// (these tests skip cleanly without it — it is a gitignored Blizzard asset). pub(super) fn reference(file: &str) -> Option { let path = Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../WoW/_extracted_framexml") .join(file); path.is_file().then_some(path) } /// Named element → the `` pairs inside it, up to the next named element. /// /// Hand-rolled rather than a regex: this crate has no regex dependency, and adding one for a scan /// of two files is a worse trade than twenty lines of `find`. /// /// `$parent*` names repeat across templates (every row template has a `$parentText`), so a bare /// name would compare one template's column against another's. Each is qualified by the template /// it lives in — the nearest preceding real name. fn scrape(text: &str) -> Vec<(String, Vec<(f32, f32)>)> { const TAGS: &[&str] = &[ "