- the raid and reputation panes are the reference's own files: two manifest lines replace our `RaidFrame.xml` (83 of 84 frame names were already the reference's; the tear-off pullouts arrive) and one replaces our `ReputationFrame.xml`; the raid swap surfaced a loader defect (a font object inherited one level down was the 26 unexplained warnings), rehomed `RaidFrame_LoadUI` and two popup dialogs, and reproduces one reference DEFECT: an emptied lockout list keeps its last row painted (1874); the reputation swap moved the WATCH BAR out of `ActionBar.xml`, where it would have shadowed the chain's own, and exposed an ENGINE gap: `CollapseFactionHeader`/`ExpandFactionHeader` fire `UPDATE_FACTION` (`0x4d5dab`) and ours fired nothing, so with the stock pane repainting on that event alone, a fold painted nothing and the header kept its minus — invisible while our own file repainted itself (1875) - a wanted poster is a questgiver on the wire as a creature is, and benilla never asked about one; the RE round came back in two halves: the reference DOES send `CMSG_QUESTGIVER_STATUS_QUERY` for a GameObject, gated on `GAMEOBJECT_FLAGS` bit 2, seen in 20 of 62 sniffed sessions — and it can never render the answer (the status handler's typemask excludes a GameObject, its status slot lies past the object's end, and no poster, sign or plaque carries attachment 18); vmangos DOES answer, so the sweep gains a GameObject arm on the reference's own gate and the apply side refuses a non-unit guid, without which a gold `!` would hang over a poster no 1.12 client draws; two shipped claims fall with it (`0x5eb3c0` is the FULL re-query sweep; a status never suppresses a GameObject's minimap dot), and `WOW_PROBE_GOQUEST` at the Goldshire wanted poster reads the server's 1 then 5, both refused (1872) - the money entry comes off the chain: Trade, Mail and Auction switch to the reference's `MoneyInputFrameTemplate` and its verbs, and our `BenillaMoneyInput_*` kit retires — it was the exact answer while benilla fired `OnTextChanged` synchronously inside `SetText`, and the engine now raises it deferred and coalesced, matching `0x77d3e0` (1831), so the reference's `expectChanges` guard works and our bracket flag was a duplicate; most of the 430 deleted lines are verbatim art — all three windows carried an inline copy of the template's three edit boxes, AuctionFrame a whole second copy; one visible change: TradeFrame now makes the reference's own `MoneyInputFrame_SetMode(this, "compact")` call, which tightens the trade money boxes (Gold 58→56 wide, Silver's offset 26→22, Copper's 16→11), and two stale claims in the kit's comments are corrected, and a verb nothing called goes (1882) - the `UIFrameFlash` family is transcribed — six FrameXML names from `UIParent.lua`, which we do not load — unblocking SpellBookFrame and HelpFrame, which called it into silence; re-arming a flashing frame is a RETURN and `-1` flashes forever (1879); UIParent is recounted — `UpdateNameplates` and the `*_Show`/`*_Hide` family around it are FrameXML, not engine, missed by a survey that skipped the LoadOnDemand addons — so its engine surface is four verbs, not a wall: `GetLocale` answers `enUS`, `IsMacClient` answers nil (the whole verb on the PC build), and `CheckReadyCheckTime` and `RequestBattlefieldPositions` are the two it waits on (1880); and `BasicControls.xml` comes off the chain by pure deletion — ours differed by nothing but line-wrapping and one comment — with `FailedAddOnLoad` and `UIParentLoadAddOn` moving to `UIParent.xml`, their reference home, below their four callers (1881) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
181 lines
6.5 KiB
Rust
181 lines
6.5 KiB
Rust
//! The fade kit's driver (`UIFrameFade` / `BenillaFadeDriver`, UiPanels.xml) — the park/wake
|
|
//! lifecycle that keeps the per-frame fade walk off the tick while nothing is fading. Split from
|
|
//! `panel_tests.rs` (1507's round): the slot manager and the fade kit share a source file, not a
|
|
//! concern.
|
|
|
|
use benilla_ui::script::UiScript;
|
|
|
|
use super::test_ui::load_ui as load_xml;
|
|
|
|
/// **The idle fade driver parks itself** (decision 1396's class, the audit's driver-hide item):
|
|
/// with `FADEFRAMES` empty there is nothing to walk, so the driver's OnUpdate hides its own frame
|
|
/// — and the engine dispatches OnUpdate only to visible frames (`tick.rs`), which stops the
|
|
/// per-frame walk cold. The probe wraps `UIFrameFadeUpdate` in a counter BEFORE the first tick:
|
|
/// ten idle ticks later it has never run, and the driver is hidden.
|
|
#[test]
|
|
fn an_idle_fade_driver_parks_itself_off_the_tick() {
|
|
let mut s = UiScript::new().unwrap();
|
|
s.set_screen_size(1024.0, 768.0);
|
|
load_xml(&s, "MoneyFrame.xml");
|
|
load_xml(&s, "UiPanels.xml");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
|
s.resolve();
|
|
s.run(
|
|
"BENILLA_TEST_FADE_TICKS = 0\n\
|
|
local real = UIFrameFadeUpdate\n\
|
|
function UIFrameFadeUpdate(elapsed)\n\
|
|
BENILLA_TEST_FADE_TICKS = BENILLA_TEST_FADE_TICKS + 1\n\
|
|
real(elapsed)\n\
|
|
end",
|
|
)
|
|
.unwrap();
|
|
for _ in 0..10 {
|
|
s.tick(0.016);
|
|
s.resolve();
|
|
}
|
|
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
|
assert!(
|
|
!s.eval::<bool>("return BenillaFadeDriver:IsShown()")
|
|
.unwrap(),
|
|
"an empty FADEFRAMES parks the driver"
|
|
);
|
|
assert_eq!(
|
|
s.eval::<i64>("return BENILLA_TEST_FADE_TICKS").unwrap(),
|
|
0,
|
|
"a parked driver never runs the fade walk"
|
|
);
|
|
}
|
|
|
|
/// The control for the guard above: the gate must not cost the fades it gates. `UIFrameFade` (the
|
|
/// kit's single insertion point — `UIFrameFadeIn`/`Out` both land there) wakes the driver beside
|
|
/// its `table.insert`; the fade then ramps on real ticks, completes, and the driver re-parks on
|
|
/// the first empty tick after.
|
|
#[test]
|
|
fn a_started_fade_still_ramps_and_the_driver_reparks_after() {
|
|
let mut s = UiScript::new().unwrap();
|
|
s.set_screen_size(1024.0, 768.0);
|
|
load_xml(&s, "MoneyFrame.xml");
|
|
load_xml(&s, "UiPanels.xml");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
|
s.resolve();
|
|
s.run(r#"CreateFrame("Frame", "BenillaFadeProbe")"#)
|
|
.unwrap();
|
|
for _ in 0..2 {
|
|
s.tick(0.016); // settle: the driver parks
|
|
s.resolve();
|
|
}
|
|
assert!(!s
|
|
.eval::<bool>("return BenillaFadeDriver:IsShown()")
|
|
.unwrap());
|
|
|
|
s.run("UIFrameFadeIn(BenillaFadeProbe, 1.0)").unwrap();
|
|
assert!(
|
|
s.eval::<bool>("return BenillaFadeDriver:IsShown()")
|
|
.unwrap(),
|
|
"starting a fade wakes the driver"
|
|
);
|
|
assert_eq!(
|
|
s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap(),
|
|
0.0,
|
|
"the fade armed at its IN startAlpha"
|
|
);
|
|
|
|
s.tick(0.5);
|
|
s.resolve();
|
|
let mid = s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap();
|
|
assert!(
|
|
(mid - 0.5).abs() < 1e-3,
|
|
"half the timeToFade in, half the ramp: {mid}"
|
|
);
|
|
|
|
s.tick(0.6); // past timeToFade: the fade completes and leaves the list
|
|
s.resolve();
|
|
assert_eq!(
|
|
s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap(),
|
|
1.0,
|
|
"the fade completes at its endAlpha"
|
|
);
|
|
s.tick(0.016); // the first empty tick re-parks the driver
|
|
s.resolve();
|
|
assert!(
|
|
!s.eval::<bool>("return BenillaFadeDriver:IsShown()")
|
|
.unwrap(),
|
|
"the driver re-parks once the list is empty"
|
|
);
|
|
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
|
}
|
|
|
|
/// **The FLASH kit alternates and stops** — the fade kit's twin, transcribed from `UIParent.lua`
|
|
/// because two stock windows call it and nothing answered (1879).
|
|
///
|
|
/// Driven rather than merely loaded: a kit that is present but never exercised is exactly how
|
|
/// twelve faux lists shipped unable to scroll (1868), so this runs a real flash to completion.
|
|
#[test]
|
|
fn a_flash_alternates_then_stops_and_the_driver_reparks() {
|
|
let mut s = UiScript::new().unwrap();
|
|
s.set_screen_size(1024.0, 768.0);
|
|
load_xml(&s, "MoneyFrame.xml");
|
|
load_xml(&s, "UiPanels.xml");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
|
|
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
|
|
s.resolve();
|
|
|
|
// Parked until something asks — the whole point of the driver over UIParent's own OnUpdate.
|
|
assert!(
|
|
!s.eval::<bool>("return BenillaFlashDriver:IsShown()")
|
|
.unwrap(),
|
|
"an empty FLASHFRAMES parks the driver"
|
|
);
|
|
|
|
s.run(
|
|
r#"Probe = CreateFrame("Frame", "BenillaFlashProbe", UIParent)
|
|
Probe:SetWidth(10) Probe:SetHeight(10) Probe:SetPoint("TOPLEFT")
|
|
UIFrameFlash(Probe, 0.05, 0.05, 0.3, nil, 0, 0)"#,
|
|
)
|
|
.unwrap();
|
|
assert!(
|
|
s.eval::<bool>("return UIFrameIsFlashing(BenillaFlashProbe) == 1")
|
|
.unwrap(),
|
|
"the frame is on the flash list"
|
|
);
|
|
assert!(
|
|
s.eval::<bool>("return BenillaFlashDriver:IsShown()")
|
|
.unwrap(),
|
|
"UIFrameFlash is the single inserter and wakes the driver"
|
|
);
|
|
|
|
// Re-arming an already-flashing frame is a no-op, not a second entry (ref UIParent.lua:1234).
|
|
s.run("UIFrameFlash(BenillaFlashProbe, 1, 1, 1, nil, 0, 0)")
|
|
.unwrap();
|
|
assert_eq!(
|
|
s.eval::<i64>("return table.getn(FLASHFRAMES)").unwrap(),
|
|
1,
|
|
"already flashing: the reference returns rather than re-arming"
|
|
);
|
|
|
|
// Run past flashDuration: the frame leaves both lists, is restored to full alpha, and — with
|
|
// `showWhenDone` nil — ends hidden.
|
|
for _ in 0..40 {
|
|
s.tick(0.016);
|
|
s.resolve();
|
|
}
|
|
assert!(s.errors().is_empty(), "flashing raised: {:?}", s.errors());
|
|
assert_eq!(
|
|
s.eval::<i64>("return table.getn(FLASHFRAMES)").unwrap(),
|
|
0,
|
|
"the flash finished and the frame left the list"
|
|
);
|
|
assert!(
|
|
!s.eval::<bool>("return BenillaFlashProbe:IsShown()")
|
|
.unwrap(),
|
|
"showWhenDone was nil, so it ends hidden"
|
|
);
|
|
assert!(
|
|
!s.eval::<bool>("return BenillaFlashDriver:IsShown()")
|
|
.unwrap(),
|
|
"and the driver re-parks on the first empty tick"
|
|
);
|
|
}
|