The strings pass for `benilla-app`, the last crate: string literals and test names lose the private record numbers and the workspace's words that a comment pass cannot touch. Fixes #92. Fixes #36. - **Messages.** About 230 assertion, panic, `expect` and log messages, printed tool labels, and comments inside embedded Lua fixtures drop their record numbers; the eight that named "the director" keep their fact in plain words. Stale claims in a few messages are corrected on the way (a cast-bar step, a font line, the every-shot recompute). - **Names.** Seven test and helper names that carried a record number or "director" now say what they check. `git grep` found no other use of any of them. - **Player files.** The key-bindings file's header and `config.toml`'s header each lose a record citation. Neither header is compared when the file is read back: the bindings reader skips every `#` line, and `config.toml`'s header is a TOML comment. - **Fixtures.** Test fixtures use neutral character, account and frame names. - **#36.** The quest-material test labels GameObject type 10 GOOBER (in 1.12, 3 is CHEST, 9 TEXT, 10 GOOBER); the code under test was already right, and the comments were fixed with the comment pass. - **Verification.** With comments stripped and every literal's contents masked, each file is identical to main and keeps its literals' line counts, apart from the renamed functions and one rustfmt reflow. `cargo fmt --check` is clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
164 lines
8.8 KiB
Rust
164 lines
8.8 KiB
Rust
//! The integration tests' interface loader: integration tests cannot reach
|
|
//! `ui_script::test_ui::load_ui`, which is `#[cfg(test)]`. A bare filename is a file we ship
|
|
//! under `assets/ui`, a path is the reference's own off the installed chain, and `<Script file>`
|
|
//! includes resolve through the same provider. A test that names a chain entry opens with
|
|
//! `benilla_formats::wow_data_or_skip!()`.
|
|
|
|
use benilla_ui::script::UiScript;
|
|
|
|
/// Load one manifest entry into `script`, panicking on any loader error. A `.lua` entry runs as a
|
|
/// chunk of raw bytes, as `GlobalStrings.lua` does in the real manifest; only XML is decoded.
|
|
pub fn load_ui(script: &UiScript, entry: &str) {
|
|
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/ui");
|
|
let chain = |req: &str| -> Option<Vec<u8>> {
|
|
let data = benilla_formats::wow_data()?;
|
|
benilla_formats::open_chain(&data).ok()?.read(req).ok()
|
|
};
|
|
let read = |req: &str| -> Option<Vec<u8>> {
|
|
if req.contains('\\') || req.contains('/') {
|
|
return chain(req);
|
|
}
|
|
std::fs::read(dir.join(req)).ok()
|
|
};
|
|
|
|
let bytes = read(entry).unwrap_or_else(|| panic!("{entry}: not found"));
|
|
if entry.to_ascii_lowercase().ends_with(".lua") {
|
|
script
|
|
.run_chunk_named(&bytes, &format!("@{entry}"))
|
|
.unwrap_or_else(|e| panic!("{entry}: {e}"));
|
|
return;
|
|
}
|
|
let doc = benilla_ui::framexml::parse(&benilla_ui::source::decode(&bytes))
|
|
.unwrap_or_else(|e| panic!("{entry}: {e}"));
|
|
let report = benilla_ui::loader::load_in(script, &doc, &entry.replace('\\', "/"), &read);
|
|
assert!(
|
|
report.errors.is_empty(),
|
|
"{entry}: loader errors: {:#?}",
|
|
report.errors
|
|
);
|
|
let leaf = entry.rsplit(['\\', '/']).next().unwrap_or("");
|
|
if leaf.eq_ignore_ascii_case("MainMenuBarMicroButtons.xml") {
|
|
script
|
|
.run(MICRO_BUTTON_STAND_INS)
|
|
.expect("the micro-button stand-ins");
|
|
}
|
|
if leaf.eq_ignore_ascii_case("UIParent.xml") && entry.contains('\\') {
|
|
script
|
|
.run(UIPARENT_STAND_INS)
|
|
.expect("the UIParent stand-ins");
|
|
}
|
|
}
|
|
|
|
/// The stock `UIParent.xml`'s unguarded callees, stood in for at load; the copy of
|
|
/// `ui_script::test_ui::UIPARENT_STAND_INS`.
|
|
const UIPARENT_STAND_INS: &str = r#"
|
|
-- Callees of the stock UIParent.xml's <OnUpdate> and of UIParent_OnEvent's arms that live in
|
|
-- files a kit may stop short of, plus the bag verbs the stock ShowUIPanel calls and the two
|
|
-- container constants its window walks read: no-op stand-ins, each overwritten by the real
|
|
-- definition when its file loads (a chunk's `function X()` is a plain global write, unlike a
|
|
-- frame's non-overwriting publish — which is why the FRAMES below are seated on first use).
|
|
FCF_OnUpdate = FCF_OnUpdate or function() end
|
|
FCF_DockUpdate = FCF_DockUpdate or function() end
|
|
UnitPopup_OnUpdate = UnitPopup_OnUpdate or function() end
|
|
BattlefieldFrame_OnUpdate = BattlefieldFrame_OnUpdate or function() end
|
|
MultiActionBar_Update = MultiActionBar_Update or function() end
|
|
RaidOptionsFrame_UpdatePartyFrames = RaidOptionsFrame_UpdatePartyFrames or function() end
|
|
LocalizeFrames = LocalizeFrames or function() end
|
|
updateContainerFrameAnchors = updateContainerFrameAnchors or function() end
|
|
-- 1.12 keeps UpdateNameplates in UIOptionsFrame.lua, which a kit reaches only at manifest
|
|
-- l.21; our own OptionsFrame.xml re-declares it below that. Both are plain
|
|
-- `function X()` writes, so a full kit ends on ours and a short one keeps this no-op.
|
|
UpdateNameplates = UpdateNameplates or function() end
|
|
CloseAllBags = CloseAllBags or function() end
|
|
OpenBackpack = OpenBackpack or function() end
|
|
CloseBackpack = CloseBackpack or function() end
|
|
NUM_CONTAINER_FRAMES = NUM_CONTAINER_FRAMES or 0
|
|
-- The reference's own initial values (`ContainerFrame.lua:11-12`), not zeroes: the tooltip's
|
|
-- default corner is `-CONTAINER_OFFSET_X - 13, CONTAINER_OFFSET_Y`, so a kit reading zero here
|
|
-- would seat every default-anchored plate 70 units low.
|
|
CONTAINER_OFFSET_X = CONTAINER_OFFSET_X or 0
|
|
CONTAINER_OFFSET_Y = CONTAINER_OFFSET_Y or 70
|
|
BATTLEFIELD_TAB_OFFSET_Y = BATTLEFIELD_TAB_OFFSET_Y or 210
|
|
-- The pass writes its `isVar` rows with `setglobal`, but only for a name that already reads
|
|
-- non-nil (`frame = getglobal(index); if frame then`), so an unseeded one is never written at
|
|
-- all. These four are the reference's own seeds, from the files that declare them
|
|
-- (ContainerFrame.lua, PetActionBarFrame.lua, WorldStateFrame.lua).
|
|
PETACTIONBAR_YPOS = PETACTIONBAR_YPOS or 98
|
|
PETACTIONBAR_XPOS = PETACTIONBAR_XPOS or 36
|
|
|
|
-- The frames these three read UNGUARDED, seated on the call rather than at load: a frame's
|
|
-- publish to _G is non-overwriting (0x701bd0), so a stand-in seated before the real file loads
|
|
-- would shadow the real window for good.
|
|
local function benilla_seat(names)
|
|
for _, name in ipairs(names) do
|
|
if not getglobal(name) then local f = CreateFrame("Frame") f:Hide() setglobal(name, f) end
|
|
end
|
|
end
|
|
local real_manage = UIParent_ManageFramePositions
|
|
function UIParent_ManageFramePositions()
|
|
benilla_seat({ "MainMenuBar", "MultiBarLeft", "MultiBarRight", "MultiBarBottomLeft",
|
|
"PetActionBarFrame", "ShapeshiftBarFrame", "ReputationWatchBar", "MainMenuExpBar",
|
|
"MainMenuBarMaxLevelBar", "CastingBarFrame", "QuestTimerFrame", "QuestWatchFrame",
|
|
"DurabilityFrame", "DurabilityShield", "DurabilityOffWeapon", "DurabilityRanged",
|
|
"MinimapCluster", "ChatFrame1", "ChatFrame2", "ShapeshiftBarLeft",
|
|
"ShapeshiftBarMiddle", "ShapeshiftBarRight", "BattlefieldMinimapTab" })
|
|
-- …and every FRAME the managed table itself names (`frame:IsObjectType` on a nil is what
|
|
-- a kit missing one raises) — read off the table, so a row added there needs nothing here.
|
|
-- The `isVar` rows are skipped: those keys are global NUMBERS the pass writes
|
|
-- (CONTAINER_OFFSET_X/Y, PETACTIONBAR_YPOS…), and a frame seated under one of those names
|
|
-- would be arithmetic's problem two files later.
|
|
local named = {}
|
|
for name, row in pairs(UIPARENT_MANAGED_FRAME_POSITIONS) do
|
|
if not row.isVar then table.insert(named, name) end
|
|
end
|
|
benilla_seat(named)
|
|
for _, name in ipairs({ "SlidingActionBarTexture0", "SlidingActionBarTexture1" }) do
|
|
if not getglobal(name) then setglobal(name, UIParent:CreateTexture()) end
|
|
end
|
|
return real_manage()
|
|
end
|
|
-- The four options/menu windows `IsOptionFrameOpen` (l.997) and `ToggleGameMenu` (l.1467)
|
|
-- index unguarded. `IsOptionFrameOpen` is on the path of every window close, so a kit that
|
|
-- loads no options window raised on the first bag click. In the shipped manifest all four
|
|
-- names are real, and all three options windows are the REFERENCE's own files,
|
|
-- loaded hidden — including `OptionsFrame`, the video window, which used to be our own
|
|
-- window's name. Ours is `BenillaOptionsFrame` now and is not in this list: it is not a name
|
|
-- the reference indexes, and the wrappers in `GameMenuFrame.xml` are what tell these two
|
|
-- functions about it. A KIT is a prefix of the manifest and may load none of the four, which
|
|
-- is what these stand-ins are for.
|
|
local function benilla_seat_options()
|
|
benilla_seat({ "GameMenuFrame", "OptionsFrame", "UIOptionsFrame", "SoundOptionsFrame" })
|
|
if not OptionsFrameCancel then
|
|
OptionsFrameCancel = CreateFrame("Button")
|
|
OptionsFrameCancel:Hide()
|
|
end
|
|
end
|
|
local real_option_open = IsOptionFrameOpen
|
|
function IsOptionFrameOpen()
|
|
benilla_seat_options()
|
|
return real_option_open()
|
|
end
|
|
local real_toggle_menu = ToggleGameMenu
|
|
function ToggleGameMenu(clicked)
|
|
benilla_seat_options()
|
|
return real_toggle_menu(clicked)
|
|
end
|
|
"#;
|
|
|
|
/// The stock micro-button row's unguarded reads, stood in for on the row's first call; the copy of
|
|
/// `ui_script::test_ui::MICRO_BUTTON_STAND_INS`.
|
|
const MICRO_BUTTON_STAND_INS: &str = r#"
|
|
local real = UpdateMicroButtons
|
|
function UpdateMicroButtons()
|
|
for _, name in ipairs({ "CharacterFrame", "SpellBookFrame", "QuestLogFrame", "GameMenuFrame",
|
|
"OptionsFrame", "SoundOptionsFrame", "UIOptionsFrame", "FriendsFrame", "WorldMapFrame",
|
|
"HelpFrame" }) do
|
|
if not getglobal(name) then local f = CreateFrame("Frame") f:Hide() setglobal(name, f) end
|
|
end
|
|
if not KeyRingButton then KeyRingButton = CreateFrame("Button") KeyRingButton:Hide() end
|
|
if not KEYRING_CONTAINER then KEYRING_CONTAINER = -2 end
|
|
if not IsBagOpen then function IsBagOpen() return nil end end
|
|
return real()
|
|
end
|
|
"#;
|