test(honorific): assert the title gate through the real draw path

This commit is contained in:
2026-06-15 14:35:20 +02:00
parent fe766285ad
commit 72099c8871
3 changed files with 91 additions and 4 deletions
@@ -195,4 +195,23 @@ internal sealed class HonorificService : IDisposable
return false;
return true;
}
// Test seam: the three status fields are private-set and IPC-driven, which a
// headless /xlperf run can't reach (Honorific is usually absent in tests).
// Callers MUST snapshot the prior values and restore them in CleanUp, and
// MUST drive Set -> Draw -> Assert within ONE synchronous RunStep (never
// Waiting between Set and Assert) — a between-frame OnReady/OnTitleChanged
// would otherwise clobber this state and a CleanUp restore can't un-corrupt a
// mid-flight assertion. (A FontsReady precondition gate returning Waiting
// BEFORE the snapshot/Set is fine — nothing is mutated yet.)
internal void TestOnly_SetState(
bool isAvailable,
(uint Major, uint Minor)? detectedApiVersion,
HonorificTitleData? title
)
{
IsAvailable = isAvailable;
DetectedApiVersion = detectedApiVersion;
CurrentTitle = title;
}
}
@@ -1,5 +1,6 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Integrations;
namespace HellionChat.SelfTests;
@@ -20,8 +21,26 @@ internal sealed class HonorificHeaderRenderStep : ISelfTestStep
public string Name => "Hellion Chat - HonorificHeader render";
private HonorificService? _svc;
private bool _prevAvailable;
private (uint Major, uint Minor)? _prevVersion;
private HonorificTitleData? _prevTitle;
private bool _prevToggle;
private bool _snapshotted;
public SelfTestStepResult RunStep()
{
// HonorificHeader.Draw early-returns on !FontsReady (HonorificHeader.cs:40-44)
// and never reaches the gated title branch, which would make assert (a) a
// false FAIL during a font-atlas rebuild. Return Waiting BEFORE any
// snapshot/mutation so the runner re-polls cleanly and no seam state leaks
// (precedent: FoxBannerTextureSmokeStep). This is a pre-Set precondition
// gate, not a mid-test Waiting — the Set->Draw->Assert window stays synchronous.
if (!plugin.FontManager.FontsReady)
{
return SelfTestStepResult.Waiting;
}
var header = plugin.MainWindow.GetHonorificHeaderForSelfTest();
if (header is null)
{
@@ -29,9 +48,48 @@ internal sealed class HonorificHeaderRenderStep : ISelfTestStep
return SelfTestStepResult.Fail;
}
_svc = header.GetServiceForSelfTest();
_prevAvailable = _svc.IsAvailable;
_prevVersion = _svc.DetectedApiVersion;
_prevTitle = _svc.CurrentTitle;
_prevToggle = Plugin.Config.ShowHonorificTitleInHeader;
_snapshotted = true;
var valid = new HonorificTitleData("Champion", false, false, null, null, null, null, null);
var original = new HonorificTitleData("Champion", false, true, null, null, null, null, null);
// Draw at a deliberately wide 420px so the title never hits the truncation
// clamp — LastTitleRendered then reflects the GATE outcome, not the width.
try
{
// (a) available + valid title + toggle on -> title renders
Plugin.Config.ShowHonorificTitleInHeader = true;
_svc.TestOnly_SetState(true, (3, 1), valid);
header.Draw(420f);
if (!header.LastTitleRendered)
{
ImGui.Text("Gate failed: valid title did not render");
return SelfTestStepResult.Fail;
}
// (b) toggle off -> title suppressed (crown stays, untestable headless)
Plugin.Config.ShowHonorificTitleInHeader = false;
header.Draw(420f);
if (header.LastTitleRendered)
{
ImGui.Text("Gate failed: title rendered with toggle off");
return SelfTestStepResult.Fail;
}
// (c) IsOriginal title -> suppressed even with toggle on
Plugin.Config.ShowHonorificTitleInHeader = true;
_svc.TestOnly_SetState(true, (3, 1), original);
header.Draw(420f);
if (header.LastTitleRendered)
{
ImGui.Text("Gate failed: original title rendered");
return SelfTestStepResult.Fail;
}
}
catch (Exception ex)
{
@@ -42,5 +100,12 @@ internal sealed class HonorificHeaderRenderStep : ISelfTestStep
return SelfTestStepResult.Pass;
}
public void CleanUp() { }
public void CleanUp()
{
if (!_snapshotted || _svc is null)
return;
Plugin.Config.ShowHonorificTitleInHeader = _prevToggle;
_svc.TestOnly_SetState(_prevAvailable, _prevVersion, _prevTitle);
_snapshotted = false;
}
}
+6 -3
View File
@@ -37,9 +37,12 @@ step explicitly as smoke-only instead of faking a headless pass.
## Anti-pattern of record
`HonorificService.ShouldRenderSlot` had zero production callers and was green
only because the test called it directly — a test passing on a path the game
never runs. That is the failure this standard prevents.
`HonorificService.ShouldRenderSlot` once had zero production callers and was
green only because the test called it directly — a test passing on a path the
game never runs. v1.8.7 retired it: the gate is now wired into the real
`HonorificHeader.Draw` and asserted through it via
`HonorificHeader.LastTitleRendered` (see `HonorificHeaderRenderStep`). Kept here
as the canonical example of the failure this standard prevents.
## Step classification