the interface labels announce their writes, and the idle tick goes quiet

- a text write names its own label, so the per-frame measure sweep drains a short ledger instead of hashing every font string twice — ~0.3 ms a frame at the Stormwind gates, gone
- the idle interface tick stops re-writing settled state: buff durations and the weapon-enchant frame write only on change, and the fade and unit-popup drivers park until woken
- the interface extract skips a hidden region before cloning its row, not after
- WOW_UPLOAD_BUDGET=<MB> brackets the arrival burst's upload pacing — a lever for measuring world entry, not a fix yet

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-08-20 18:48:12 +07:00
co-authored by Claude Fable 5
parent b932302d79
commit 19e861bb96
25 changed files with 923 additions and 77 deletions
@@ -750,6 +750,186 @@ fn the_temporary_enchant_row_shows_a_weapon_enchant_and_moves_the_top_row_aside(
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// **The idle enchant row writes nothing** (decision 1396's class, the audit's item on
/// `TemporaryEnchantFrame:OnUpdate` — 1396 measured it at ~14 µs/frame as the post-fix lead). With
/// neither hand enchanted, `BuffFrame_Enchant_OnUpdate` used to clear both slots and re-park the
/// bar every frame of every session; now the no-enchant branch latches on `benillaCleared` after
/// its first pass.
///
/// The probes are sentinels the branch would overwrite if it still ran: the bar re-anchored 41px
/// left of its park, and slot 1 shown. Ten ticks later both survive. Then the row is DRIVEN — both
/// hands at once — proving the latch releases, the straight-line hand packer keeps the reference's
/// off-hand-first order, and the `filled`-gated width/anchor writes fire on the 0→2 edge; the drop
/// back to empty re-parks the bar (the first pass after a transition always writes).
#[test]
fn an_idle_enchant_row_stops_rewriting_the_bar() {
let mut s = harness();
push(&mut s, mixed_bar());
frame(&mut s, 0.1); // first pass: the clear + park write once and latch
frame(&mut s, 0.1);
s.resolve();
let resting = s.eval::<f64>("return BuffFrame:GetRight()").unwrap();
// Sentinels the no-enchant branch can never leave standing if it still writes.
s.run(
r#"BuffFrame:SetPoint("TOPRIGHT", "TemporaryEnchantFrame", "TOPRIGHT", -41, 0)
TempEnchant1:Show()"#,
)
.unwrap();
for _ in 0..10 {
frame(&mut s, 0.1);
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
assert!(
shown(&s, "TempEnchant1"),
"a latched idle row must not re-hide the slot every frame"
);
let displaced = s.eval::<f64>("return BuffFrame:GetRight()").unwrap();
assert!(
(displaced - (resting - 41.0)).abs() < 1e-3,
"a latched idle row must not re-park the bar every frame — got {displaced}, resting {resting}"
);
// The control: both hands enchant. Off hand packs slot 1 (the reference's order — main hand
// sits to its LEFT in slot 2), the row is 64 wide, the bar starts one 5px gutter left of it.
s.set_weapon_enchants(
Some(benilla_ui::script::WeaponEnchant {
remaining_ms: Some(480_000),
charges: 0,
}),
Some(benilla_ui::script::WeaponEnchant {
remaining_ms: Some(120_000),
charges: 0,
}),
);
frame(&mut s, 0.1);
assert!(shown(&s, "TempEnchant1") && shown(&s, "TempEnchant2"));
assert_eq!(
s.eval::<i64>("return TempEnchant1:GetID()").unwrap(),
17,
"off hand first: slot 1 is the OffHandSlot id"
);
assert_eq!(s.eval::<i64>("return TempEnchant2:GetID()").unwrap(), 16);
assert_eq!(text(&s, "TempEnchant1Duration"), "2 m");
assert_eq!(text(&s, "TempEnchant2Duration"), "8 m");
s.resolve();
assert_eq!(
s.eval::<f64>("return TemporaryEnchantFrame:GetWidth()")
.unwrap(),
64.0,
"two 32px columns"
);
let both = s.eval::<f64>("return BuffFrame:GetRight()").unwrap();
assert!(
(resting - both - 69.0).abs() < 1e-3,
"the bar clears both columns + the gutter: {resting} -> {both}"
);
// And back to empty: the transition still writes (the latch re-arms, not re-fires).
s.set_weapon_enchants(None, None);
frame(&mut s, 0.1);
assert!(!shown(&s, "TempEnchant1") && !shown(&s, "TempEnchant2"));
s.resolve();
let parked = s.eval::<f64>("return BuffFrame:GetRight()").unwrap();
assert!(
(parked - resting).abs() < 1e-3,
"the drop-to-empty pass re-parks the bar: {parked} vs {resting}"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// **A settled buff button writes nothing per frame** (the same 1396 class, one row down): the
/// poll (`GetPlayerBuffTimeLeft` every frame — load-bearing, decision 0846) stays, but the alpha
/// and the duration-region writes are gated on what was last written. The probes are sentinels the
/// handler can never produce: alpha 0.42 (it writes only 1.0 or the warning ramp, and at 5 min out
/// the ramp is not running) and the text "X" (it writes only `SecondsToTimeAbbrev` strings — and
/// the compare is against the computed STRING, never a measured extent, which is 1396's fixture
/// trap). The controls: the minute rollover still rewrites the text, the warning band still turns
/// the number white, and inside the last 31s the pulse still writes a fresh alpha per tick.
#[test]
fn a_settled_buff_button_stops_rewriting_alpha_and_duration() {
let mut s = harness();
push(
&mut s,
vec![aura(
1126,
"Mark of the Wild",
"Interface\\Icons\\Spell_Nature_Regeneration",
true,
1,
None,
300.0,
true,
)],
);
frame(&mut s, 0.1); // the event repaint + the poll's first write ("5 m", alpha 1.0)
frame(&mut s, 0.1);
assert_eq!(text(&s, "BuffButton0Duration"), "5 m");
s.run(r#"BuffButton0:SetAlpha(0.42); BuffButton0Duration:SetText("X")"#)
.unwrap();
for _ in 0..10 {
frame(&mut s, 0.016);
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
assert!(
(alpha(&s, "BuffButton0") - 0.42).abs() < 1e-6,
"a settled button must not rewrite its alpha every frame — got {}",
alpha(&s, "BuffButton0")
);
assert_eq!(
text(&s, "BuffButton0Duration"),
"X",
"a settled button must not rewrite its unchanged duration text every frame"
);
// Control 1: a REAL text change flows — the abbreviation drops to "4 m" past the boundary.
s.tick(60.0);
assert_eq!(
text(&s, "BuffButton0Duration"),
"4 m",
"the minute rollover overwrites the sentinel: the gate passes real changes"
);
// Control 2: inside the last 31s the pulse writes a fresh ramp value every tick, and the
// number wears the sub-minute HIGHLIGHT white (the band is part of the write tuple).
s.tick(210.0); // ~29.5s left
s.resolve();
let a1 = alpha(&s, "BuffButton0");
s.tick(0.2);
let a2 = alpha(&s, "BuffButton0");
s.tick(0.3);
let a3 = alpha(&s, "BuffButton0");
assert!(
a1 != a2 && a2 != a3,
"the warning pulse flows through the gate per tick: {a1} {a2} {a3}"
);
s.resolve();
let seconds_text = text(&s, "BuffButton0Duration");
assert!(
seconds_text.ends_with(" s"),
"inside the last minute the timer counts seconds: {seconds_text}"
);
let white = s.extract().iter().any(|q| match &q.content {
QuadContent::Text {
text: Some(t),
color: Some(c),
..
} => {
*t == seconds_text
&& (c[0] - 1.0).abs() < 1e-3
&& (c[1] - 1.0).abs() < 1e-3
&& (c[2] - 1.0).abs() < 1e-3
}
_ => false,
});
assert!(
white,
"the sub-minute band rewrote the vertex color to HIGHLIGHT white"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// The duration line against the **real shipped strings**, end to end — the leg the engine's own
/// synthetic-template tests cannot reach.
///
@@ -716,3 +716,69 @@ fn hovering_the_dock_still_reveals_the_tabs() {
"a stationary hover reveals the selected tab at full alpha — got {alpha}"
);
}
/// **A view at the bottom writes nothing** — `ChatFrame_OnUpdate`'s at-bottom branch used to call
/// `flash:Hide()` and zero `flashTimer` every frame; the 1.12 reference gates both behind
/// `flash:IsVisible()` (1396's residual item 3, now restored — our `flashTimer = 0` rides inside
/// the reference's gate, so a return-to-bottom during a hidden blink phase keeps the residual
/// phase, a divergence that costs nothing at rest).
///
/// The probe is a sentinel the gated branch can only zero: `flashTimer = 0.42` with the flash
/// hidden survives ten ticks. The controls: scrolled up, the bottom-button blink still runs its
/// 0.5s toggle; and scrolling back while the flash is LIT still hides it and zeroes the timer.
#[test]
fn a_chat_view_at_the_bottom_stops_rewriting_the_flash() {
let mut s = chat_frame();
for t in ["L0", "L1", "L2"] {
s.add_chat_message("ChatFrame1", t, 1.0, 1.0, 1.0);
}
for _ in 0..3 {
s.tick(0.016);
s.resolve();
}
// At the bottom, flash hidden: the sentinel must survive.
s.run("ChatFrame1.flashTimer = 0.42").unwrap();
for _ in 0..10 {
s.tick(0.016);
s.resolve();
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
let timer: f64 = s.eval("return ChatFrame1.flashTimer").unwrap();
assert!(
(timer - 0.42).abs() < 1e-6,
"a settled view must not rewrite flashTimer every frame — got {timer}"
);
assert!(
!s.eval::<bool>("return ChatFrame1BottomButtonFlash:IsVisible()")
.unwrap(),
"the flash stays hidden at rest"
);
// Control 1: scrolled up, the blink still runs — the timer accumulates past 0.5 and shows
// the flash (a point inside the docked frame, as the wheel test uses).
s.mouse_wheel(100.0, 150.0, 1.0);
s.tick(0.3); // 0.42 + 0.3 = 0.72 >= 0.5 -> toggle on
s.resolve();
assert!(
s.eval::<bool>("return ChatFrame1BottomButtonFlash:IsVisible()")
.unwrap(),
"scrolled up, the bottom-button blink still lights"
);
// Control 2: back to the bottom while the flash is LIT — the transition still writes.
s.mouse_wheel(100.0, 150.0, -1.0);
s.tick(0.016);
s.resolve();
assert!(
!s.eval::<bool>("return ChatFrame1BottomButtonFlash:IsVisible()")
.unwrap(),
"returning to the bottom hides a lit flash"
);
assert_eq!(
s.eval::<f64>("return ChatFrame1.flashTimer").unwrap(),
0.0,
"and zeroes the timer with it (our reset rides inside the reference's gate)"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
@@ -979,3 +979,99 @@ fn an_addons_own_frame_registered_in_uipanelwindows_takes_the_left_slot() {
s.run("HideUIPanel(AddonPanel)").unwrap();
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
// ── The fade kit's driver (UIFrameFade / BenillaFadeDriver) ──────────────────────────────────
/// **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, "UiPanels.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, "UiPanels.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());
}
@@ -727,6 +727,146 @@ fn the_pet_details_row_opens_the_pet_paper_doll() {
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
// ── The enable-pass driver (BenillaUnitPopupDriver) ─────────────────────────────────────────────
/// **The idle popup driver parks itself** (decision 1396's class, the audit's driver-hide item):
/// with no unit menu of ours open, `UnitPopup_OnUpdate`'s early-out hides the driver's own frame —
/// and the engine dispatches OnUpdate only to visible frames (`tick.rs`), so the per-frame scan
/// stops entirely. `UnitPopup_ShowMenu` — the one opener every unit menu goes through (grep:
/// UnitFrames/PartyFrame/FriendsFrame/ItemRef all call it) — wakes it again.
///
/// The probe wraps `UnitPopup_OnUpdate` in a counter: after the first tick parks the shipped-shown
/// driver, ten idle ticks never run the pass. The control: an open target menu still runs the
/// per-frame enable pass — a disconnected target's Whisper row greys, and greys back live when the
/// unit reconnects mid-open — and closing the menu re-parks the driver.
#[test]
fn an_idle_unit_popup_driver_parks_itself_off_the_tick() {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
bake_strings(&s);
load_popup_frames(&s);
s.set_unit(
"player",
Some(UnitState {
exists: true,
name: Some("Me".into()),
is_player: true,
..UnitState::default()
}),
);
// A friendly player target, DISCONNECTED (is_connected defaults to false): the Whisper row's
// grey predicate is live from the first enable pass.
s.set_unit(
"target",
Some(UnitState {
exists: true,
name: Some("Ally".into()),
health: 40,
max_health: 40,
is_player: true,
reaction: 5,
..UnitState::default()
}),
);
s.fire_event("PLAYER_TARGET_CHANGED", vec![]);
s.resolve();
assert!(s.errors().is_empty(), "load errors: {:?}", s.errors());
s.run(
"BENILLA_TEST_POPUP_TICKS = 0\n\
local real = UnitPopup_OnUpdate\n\
function UnitPopup_OnUpdate(elapsed)\n\
BENILLA_TEST_POPUP_TICKS = BENILLA_TEST_POPUP_TICKS + 1\n\
real(elapsed)\n\
end",
)
.unwrap();
// The driver ships shown; the first tick's early-out parks it, and then nothing runs.
s.tick(0.016);
s.resolve();
assert!(
!s.eval::<bool>("return BenillaUnitPopupDriver:IsShown()")
.unwrap(),
"no menu open: the first tick parks the driver"
);
s.run("BENILLA_TEST_POPUP_TICKS = 0").unwrap();
for _ in 0..10 {
s.tick(0.016);
s.resolve();
}
assert_eq!(
s.eval::<i64>("return BENILLA_TEST_POPUP_TICKS").unwrap(),
0,
"a parked driver never runs the enable pass"
);
// Open the PLAYER menu through the real hit path: the driver wakes...
let (cx, cy) = s
.eval::<(f64, f64)>("return TargetFrame:GetCenter()")
.unwrap();
s.mouse_button(cx as f32, cy as f32, "RightButton", true);
s.mouse_button(cx as f32, cy as f32, "RightButton", false);
s.resolve();
assert!(
s.eval::<bool>("return DropDownList1:IsVisible()").unwrap(),
"the menu opened"
);
assert!(
s.eval::<bool>("return BenillaUnitPopupDriver:IsShown()")
.unwrap(),
"UnitPopup_ShowMenu wakes the driver"
);
// ...and the pass runs per frame: the disconnected target's Whisper row greys...
s.tick(0.016);
s.resolve();
assert!(
s.eval::<i64>("return BENILLA_TEST_POPUP_TICKS").unwrap() > 0,
"an open menu ticks the enable pass"
);
assert!(
!s.eval::<bool>("return DropDownList1Button2:IsEnabled()")
.unwrap(),
"Whisper greys against a disconnected target"
);
// ...and un-greys LIVE when the unit reconnects while the menu is up — the whole reason the
// pass is per-frame rather than baked at open.
s.set_unit(
"target",
Some(UnitState {
exists: true,
name: Some("Ally".into()),
health: 40,
max_health: 40,
is_player: true,
reaction: 5,
is_connected: true,
..UnitState::default()
}),
);
s.tick(0.016);
s.resolve();
assert!(
s.eval::<bool>("return DropDownList1Button2:IsEnabled()")
.unwrap(),
"Whisper enables the frame the target reconnects"
);
// Closing the menu re-parks the driver on its next tick.
s.run("CloseDropDownMenus()").unwrap();
s.tick(0.016);
s.resolve();
assert!(
!s.eval::<bool>("return BenillaUnitPopupDriver:IsShown()")
.unwrap(),
"a closed menu parks the driver again"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// Click an open dropdown row through the real hit path.
fn click_row(s: &mut UiScript, row: u32) {
click_frame(s, &format!("DropDownList1Button{row}"));