- the buff bar is the reference's own `BuffFrame.xml`, and the swap found an engine defect: an empty `<OnLoad>` in 1.12's XML is a BLANK that takes an inherited handler away again (seven such sites across four stock files), which our loader read as "none given" — so the two enchant slots would have painted the player's first buff in the corner and cancelled it on right-click; the first fix then had to be corrected at the bytes (`SetScript 0x7025c0` tests only NULL and empty, and nothing trims, so `<OnLoad>↵</OnLoad>` compiles to a valid empty function rather than storing nil, which any addon calling `GetScript` can observe); `SHOW_BUFF_DURATIONS` is applied once post-load because 1.12 leaves that to `UIOptionsFrame.lua`, which benilla does not build — without it the bar ships a 10px pitch error on every buff - gossip quests come through the reference's four verbs — `GetGossipAvailableQuests`/`GetGossipActiveQuests` returning (title, level) PAIRS, with the two `Select*` verbs indexing into that list — replacing three invented names that sat on the beyond-1.12 exception list; the quest LEVEL was on the wire, parsed and dropped because the invented verb had nowhere to put it, and the reference's order (available before active, whatever the packet interleaved) comes with them - eight more files off the chain as groundwork: `LocaleProperties.lua` (the gendered/plural `GetText` selector that eleven stock call sites read their words through — fifty lines of FrameXML that looked like an engine binding), `CinematicFrame.xml`, the UIMenu kit, the `UnitFrame.lua`/`CombatFeedback.lua` kits (the only two names blocking the stock player, party, target and pet frames), the money-input kit, and `ItemButtonTemplate.xml` — the base every bag, bank, merchant, loot, trade and mail slot inherits; `GetMerchantItemMaxStack` (on the wire the whole time, dropped) and the two trade item-link verbs join the engine - two instruments for the migration: a gap report that reads a stock window's call sites against a LOADED VM and says which engine verbs it still needs (28 windows need none, the rest name 1–5), and the readiness probe's documented false positive — loading a stock window on top of our same-named one leaves `getglobal(name)` pointing at ours while the stock handlers run against theirs, so a failure inside a colliding name is a question, not an answer Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
56 lines
2.7 KiB
Rust
56 lines
2.7 KiB
Rust
//! **The integration tests' interface loader — one copy, both stores** (decision 1751).
|
|
//!
|
|
//! The in-crate sibling of `ui_script::test_ui::load_ui`, which integration tests cannot reach:
|
|
//! they link this crate as a library, so its `#[cfg(test)]` items are not compiled for them. Each
|
|
//! `tests/*.rs` therefore grew its own reader off `assets/ui`, and every one of them broke the
|
|
//! first time a file it names became the reference's own.
|
|
//!
|
|
//! The rule is the manifest's, verbatim: **a bare filename is a file we ship, a path is the
|
|
//! reference's own off the player's installed chain.** Our shipped tree is flat, so a separator
|
|
//! decides, and nothing here needs to know which windows have migrated.
|
|
//!
|
|
//! The provider half matters as much as the loop. A sourced document pulls its Lua through its own
|
|
//! `<Script file="X.lua"/>`, which the loader resolves against the *including document's*
|
|
//! directory — `Interface\FrameXML\X.lua`, a chain path. A disk-only provider leaves every one of
|
|
//! those globals nil and the failures land nowhere near the cause.
|
|
//!
|
|
//! **A chain entry needs client data**, so a test that names one opens with
|
|
//! `benilla_formats::wow_data_or_skip!()`, like every other archive-backed test.
|
|
|
|
use benilla_ui::script::UiScript;
|
|
|
|
/// Load one manifest entry into `script`, panicking on any loader error.
|
|
///
|
|
/// A `.lua` entry is run as a chunk rather than parsed as a document — `GlobalStrings.lua` and
|
|
/// `LocaleProperties.lua` are entries of that shape in the real manifest too. Bytes, not text: a
|
|
/// chunk goes to Lua as it sits in the archive and only an XML parse decodes (1193).
|
|
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
|
|
);
|
|
}
|