Twelve more, all of them numbers the realm moved out from under

Both clusters left after the chat-window shadow were stale numbers written into
tests rather than anything of ours, and both are stale because the realm's
FrameXML moved — which is the thing to suspect first in this crate.

Seven in `battlefield_minimap_tests`: the manifest was expected to warn exactly
once, about `gxRefresh`. This client does not register that CVar because no
target offers the exclusive mode-set a refresh rate needs, and the only reader
was stock `OptionsFrame.lua`'s `OptionsFrameRefreshDropDown_OnLoad` — which the
realm replaced with a rewrite that has no refresh-rate dropdown at all: three
mentions of the CVar in `patch.MPQ`'s stock copy, none in `patch-4.mpq`'s. The
list is now empty rather than gone, so a realm that restores the stock window
brings the row back and that is where it shows.

Five in `questlog_tests`, from three separate numbers in the one file:

`"Quests: 8/20"` — the realm sets `MAX_QUESTLOG_QUESTS = 25`, and this file had
already learned that once: another test reads the cap off the global with a
comment saying why. This call site had been missed, so it is read the same way.

A wheel notch was asserted to scroll the list, with eight fixture quests
described as "two more than the list shows". The realm shows
`QUESTS_DISPLAYED = 24` where 1.12 shows six, so eight fit with room to spare and
the notch had nothing to move — the `assert_ne!` was passing only while the row
count read as six, which is to say it stopped testing anything a while ago. The
log is re-seated past the window for that leg, sized off the live global, and put
back to the eight the rest of the test reads.

And `frame_rect(quads, 300.0, 261.0)` / `(384.0, 512.0)` hunted the quad list for
a bare frame of a given size, which is a number the realm can change and did.
`named_rect` asks the frame where it is instead. Nothing any of those tests
asserts was ever about the size: they clip reward plates and text against the
pane and compare the window's own edges.

269 → 160, still nothing newly failing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-10-28 21:44:50 -06:00
co-authored by Claude Opus 5
parent eeebb5abf0
commit b39d901257
2 changed files with 80 additions and 26 deletions
@@ -8,9 +8,16 @@ use benilla_ui::script::{
WorldStateUiView, ARROW_MODEL,
};
/// The manifest's one warning. Deviation: `gxRefresh`, read at `OptionsFrame.lua:300`, is not
/// registered, because nothing here can set a refresh rate.
const MANIFEST_WARNINGS: [&str; 1] = ["unknown CVar 'gxRefresh' (not host-registered) — ignored"];
/// The whole manifest, loaded, warns about **nothing**.
///
/// It held one row until now: `gxRefresh`, which this client does not register because no target
/// offers the exclusive mode-set a refresh rate needs. The reader was stock `OptionsFrame.lua`'s
/// `OptionsFrameRefreshDropDown_OnLoad` (l.300) — and the realm has replaced that file with
/// Turtle's rewrite, which has no refresh-rate dropdown and reads the CVar nowhere: 3 mentions in
/// `patch.MPQ`'s stock copy, **0** in `patch-4.mpq`'s. So the deviation is still a deviation and
/// nothing reaches it, which is why this list is empty rather than deleted — a realm that puts the
/// stock window back brings the row back with it, and this is where that shows.
const MANIFEST_WARNINGS: [&str; 0] = [];
fn quiet(s: &UiScript) {
assert!(s.errors().is_empty(), "script errors: {:#?}", s.errors());
@@ -3,23 +3,32 @@
//! the list and detail scroll frames inherit (`QuestLogFrame.xml:589`, `:606`).
use benilla_ui::script::{
ExtractedQuad, PartyMemberInfo, PartyState, QuadContent, QuestItemView, QuestLogDetail,
QuestLogEntryView, QuestLogObjectiveView, QuestLogState, SoundRequest, UiScript,
PartyMemberInfo, PartyState, QuadContent, QuestItemView, QuestLogDetail, QuestLogEntryView,
QuestLogObjectiveView, QuestLogState, SoundRequest, UiScript,
};
use super::test_ui::load_ui as load_xml;
/// The rect of the frame sized `w` by `h`, from the `QuadContent::Frame` quad every frame emits.
fn frame_rect(quads: &[ExtractedQuad], w: f32, h: f32) -> benilla_ui::layout::Rect {
quads
/// A named frame's own resolved rect, asked of the frame instead of hunted for among the quads by
/// size.
///
/// This replaces a `frame_rect(quads, w, h)` that hunted the quad list for a bare frame of a
/// given size, because a size written in a test is a number the realm can change: the detail
/// scroll frame was 300x261 and the window 384x512 when these tests were written, and the realm's
/// `QuestLogFrame` is a different shape now — it shows `QUESTS_DISPLAYED = 24` rows where 1.12
/// shows six. Five tests died looking for quads that no longer exist. Nothing any of them asserts
/// is about the size: they clip reward plates and text against the pane and compare the window's
/// own edges, so the frame is asked where it is.
fn named_rect(s: &UiScript, name: &str) -> benilla_ui::layout::Rect {
let edges: Vec<f32> = ["GetBottom", "GetLeft", "GetTop", "GetRight"]
.iter()
.find_map(|q| match &q.content {
QuadContent::Frame => q
.rect
.filter(|r| (r.width() - w).abs() < 0.5 && (r.height() - h).abs() < 0.5),
_ => None,
.map(|getter| {
s.eval::<f32>(&format!("return {name}:{getter}()"))
.unwrap_or_else(|e| panic!("{name}:{getter}(): {e}"))
})
.unwrap_or_else(|| panic!("no bare-frame quad sized {w}x{h}"))
.collect();
benilla_ui::layout::Rect::new(edges[0], edges[1], edges[2], edges[3])
}
thread_local! {
@@ -44,10 +53,21 @@ thread_local! {
};
}
/// Eight quests, two more than the list shows (`QUESTS_DISPLAYED`, `QuestLogFrame.lua:1`); only
/// quest 1 has an objective, and each has its own `quest_id`, the watch set's key.
/// Eight quests; only quest 1 has an objective, and each has its own `quest_id`, the watch set's
/// key.
///
/// Eight **no longer overflows the list**: the realm shows `QUESTS_DISPLAYED = 24` rows at once
/// where 1.12 shows 6, so a fixture this size fits with room to spare and nothing about it can
/// scroll. Anything testing the scroll seats [`entries`] off the live global instead — see
/// `the_wheel_scrolls_a_list_longer_than_the_realm_displays`.
fn eight_entries() -> QuestLogState {
let entries = (1..=8)
entries(8)
}
/// `n` quests in the shape [`eight_entries`] describes, so a test that needs to overflow the list
/// can size itself off `QUESTS_DISPLAYED` rather than off 1.12's row count.
fn entries(n: u32) -> QuestLogState {
let entries = (1..=n)
.map(|i| QuestLogEntryView {
quest_id: i,
title: format!("Quest {i}"),
@@ -70,7 +90,7 @@ fn eight_entries() -> QuestLogState {
})
.collect();
QuestLogState {
num_quests: 8,
num_quests: n,
entries,
hidden_quest_ids: Vec::new(),
}
@@ -153,10 +173,16 @@ fn shipped_questlog_frame_drives_end_to_end() {
.eval::<String>("return QuestLogTitle1NormalText:GetText()")
.unwrap()
.contains("Quest 1"));
// The cap off the global, for the reason `the_quest_count_reads_the_realms_own_cap` gives
// below: OctoWoW's `QuestLogFrame.lua` sets `MAX_QUESTLOG_QUESTS = 25`, and pinning 1.12's 20
// here was the same mistake made twice in one file.
let cap = s
.eval::<i64>("return MAX_QUESTLOG_QUESTS")
.expect("the realm's quest-log cap");
assert_eq!(
s.eval::<String>("return QuestLogQuestCount:GetText()")
.unwrap(),
"Quests: |cffffffff8/20|r"
format!("Quests: |cffffffff8/{cap}|r")
);
assert_eq!(s.eval::<i64>("return GetQuestLogSelection()").unwrap(), 1);
@@ -201,7 +227,21 @@ fn shipped_questlog_frame_drives_end_to_end() {
// Choices and rewards share one pool: item 1 is the reward, item 2 unused.
assert!(!s.eval::<bool>("return QuestLogItem2:IsShown()").unwrap());
// Wheel down over row 3, a list row (the detail pane shows quest 1).
// Wheel down over a list row — on a log long enough to scroll.
//
// Eight quests used to be "two more than the list shows", which was 1.12's six. The realm's
// `QuestLogFrame.lua` shows **`QUESTS_DISPLAYED = 24`**, so eight fit with room to spare and
// the notch had nothing to move: this `assert_ne!` was passing on a stale row count and went
// red the moment the count was read off the realm. So the log is re-seated past the window
// for this leg, sized off the live global rather than off any number written here, and put
// back to the eight the rest of the test reads.
let displayed = s
.eval::<i64>("return QUESTS_DISPLAYED")
.expect("the realm's visible row count");
#[allow(clippy::cast_sign_loss)]
let seated = u32::try_from(displayed).expect("a positive row count") + 4;
s.set_quest_log(entries(seated));
s.run("QuestLog_Update()").unwrap();
s.resolve();
let quads = s.extract();
let (wx, wy) = quads
@@ -223,8 +263,16 @@ fn shipped_questlog_frame_drives_end_to_end() {
s.eval::<String>("return QuestLogTitle1NormalText:GetText()")
.unwrap(),
before,
"wheel-down scrolled the list"
"wheel-down scrolled {seated} quests in a {displayed}-row window"
);
// Back to the eight, scrolled to the top and quest 1 selected, which is the state the rest of
// this test was written against.
s.set_quest_log(eight_entries());
s.run(
"QuestLogListScrollFrameScrollBar:SetValue(0) QuestLog_Update() QuestLog_SetSelection(1)",
)
.unwrap();
s.resolve();
assert_eq!(s.eval::<i64>("return GetQuestLogSelection()").unwrap(), 1);
// Abandon, then No: the ABANDON_QUEST popup closes and queues nothing.
@@ -338,7 +386,7 @@ fn shift_click_toggles_the_watch_checkbox_and_the_tracker_hud() {
// Row 1, quest 1, the one with an objective: 300x16 at the window's TOPLEFT +19,-75
// (`QuestLogFrame.xml:4-7`, `:535-539`).
s.resolve();
let win = frame_rect(&s.extract(), 384.0, 512.0);
let win = named_rect(&s, "QuestLogFrame");
let (rx, ry) = (win.left + 19.0 + 150.0, win.top - 75.0 - 8.0);
s.set_modifiers(true, false, false);
s.mouse_button(rx, ry, "LeftButton", true);
@@ -451,7 +499,7 @@ fn watch_guards_no_op_without_erroring() {
// Rows are 300x16 from the window's TOPLEFT +19,-75, each overlapping the last by 1, a 15 px
// pitch (`QuestLogFrame.xml:535-548`).
s.resolve();
let win = frame_rect(&s.extract(), 384.0, 512.0);
let win = named_rect(&s, "QuestLogFrame");
let row_center = |n: u32| win.top - 75.0 - 15.0 * (n - 1) as f32 - 8.0;
let x = win.left + 19.0 + 150.0;
@@ -1010,7 +1058,7 @@ fn overflowing_detail_content_clips_to_the_scrollframe_rect() {
s.resolve();
let quads = s.extract();
let scroll_rect = frame_rect(&quads, 300.0, 261.0);
let scroll_rect = named_rect(&s, "QuestLogDetailScrollFrame");
let reward_plate_clips: Vec<Option<benilla_ui::layout::Rect>> = quads
.iter()
@@ -1082,8 +1130,7 @@ fn wheel_over_the_detail_pane_changes_vertical_scroll() {
s.resolve(); // the measures reach the scroll range and the bar
s.resolve();
let quads = s.extract();
let scroll_rect = frame_rect(&quads, 300.0, 261.0);
let scroll_rect = named_rect(&s, "QuestLogDetailScrollFrame");
// Near the pane's top-left, clear of the reward row.
let (x, y) = (scroll_rect.left + 10.0, scroll_rect.top - 10.0);
@@ -1441,7 +1488,7 @@ fn shift_click_on_a_title_posts_the_quest_name_with_chat_open_and_watches_with_i
// Row 1's centre: 300x16 at the window's TOPLEFT +19,-75.
s.resolve();
let win = frame_rect(&s.extract(), 384.0, 512.0);
let win = named_rect(&s, "QuestLogFrame");
let (rx, ry) = (win.left + 19.0 + 150.0, win.top - 75.0 - 8.0);
let shift_click = |s: &mut UiScript| {
s.set_modifiers(true, false, false);