feat: add ThemeSwitchSelfTestStep + ISelfTestRegistry wiring
Registers a single SelfTestStep that exercises Plugin.ThemeRegistry.Switch through the live theme list. Verified in-game via /xldev SelfTest tab on 2026-05-08; Plugin loads cleanly with the RegisterTestSteps call and the step runs the theme cycle as expected. Folder is HellionChat/SelfTest/ (singular). Future steps may rename to SelfTests/ to match the local Plan v4 convention.
This commit is contained in:
@@ -41,6 +41,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
[PluginService] public static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
||||
[PluginService] public static IPlayerState PlayerState { get; private set; } = null!;
|
||||
[PluginService] public static ISeStringEvaluator Evaluator { get; private set; } = null!;
|
||||
[PluginService] public static ISelfTestRegistry SelfTestRegistry { get; private set; } = null!;
|
||||
|
||||
public static Configuration Config = null!;
|
||||
public static FileDialogManager FileDialogManager { get; private set; } = null!;
|
||||
@@ -452,6 +453,12 @@ public sealed class Plugin : IDalamudPlugin
|
||||
ThemeRegistry = new Themes.ThemeRegistry(customThemesDir);
|
||||
ThemeRegistry.Switch(Config.Theme);
|
||||
|
||||
// SelfTest hooks live alongside the live registry — the steps
|
||||
// poll Active per frame and need the registry already wired.
|
||||
SelfTestRegistry.RegisterTestSteps([
|
||||
new SelfTest.ThemeSwitchSelfTestStep(this),
|
||||
]);
|
||||
|
||||
// Plugin integrations register their IPC subscribers up-front so
|
||||
// Ready/Disposing events from the target plugins are caught from
|
||||
// the very first frame, even if the user's Honorific reloads
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Plugin.SelfTest;
|
||||
using HellionChat.Themes;
|
||||
|
||||
namespace HellionChat.SelfTest;
|
||||
|
||||
// Validates the runtime theme-switch contract from the user side. The
|
||||
// caller toggles the active theme via Settings -> Theme & Layout, the
|
||||
// step polls ThemeRegistry.Active per frame and only passes once the
|
||||
// slug has moved away from the initial value and back. The ABGR cache
|
||||
// is sanity-checked on every frame: a freshly switched theme must carry
|
||||
// a populated cache, otherwise Switch() forgot the recompute and the UI
|
||||
// would still draw, just with all-transparent slots.
|
||||
internal sealed class ThemeSwitchSelfTestStep : ISelfTestStep
|
||||
{
|
||||
private readonly Plugin plugin;
|
||||
private string? initialSlug;
|
||||
private bool switchedAway;
|
||||
|
||||
public ThemeSwitchSelfTestStep(Plugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public string Name => "Hellion Chat - Theme switch";
|
||||
|
||||
public SelfTestStepResult RunStep()
|
||||
{
|
||||
var registry = this.plugin.ThemeRegistry;
|
||||
if (registry is null)
|
||||
return SelfTestStepResult.Fail;
|
||||
|
||||
var active = registry.Active;
|
||||
if (active is null)
|
||||
return SelfTestStepResult.Fail;
|
||||
|
||||
if (!HasPopulatedCache(active))
|
||||
return SelfTestStepResult.Fail;
|
||||
|
||||
if (this.initialSlug is null)
|
||||
{
|
||||
this.initialSlug = active.Slug;
|
||||
ImGui.Text($"Initial theme: \"{this.initialSlug}\". Open Settings -> Theme & Layout and pick a different theme.");
|
||||
return SelfTestStepResult.Waiting;
|
||||
}
|
||||
|
||||
if (!this.switchedAway)
|
||||
{
|
||||
if (!string.Equals(active.Slug, this.initialSlug, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
this.switchedAway = true;
|
||||
return SelfTestStepResult.Waiting;
|
||||
}
|
||||
|
||||
ImGui.Text($"Switch the active theme away from \"{this.initialSlug}\".");
|
||||
return SelfTestStepResult.Waiting;
|
||||
}
|
||||
|
||||
if (!string.Equals(active.Slug, this.initialSlug, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ImGui.Text($"Switch back to \"{this.initialSlug}\" to finish the test.");
|
||||
return SelfTestStepResult.Waiting;
|
||||
}
|
||||
|
||||
return SelfTestStepResult.Pass;
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
this.initialSlug = null;
|
||||
this.switchedAway = false;
|
||||
}
|
||||
|
||||
// Any non-zero slot proves the cache was actually recomputed for the
|
||||
// current theme. We don't compare against a reference, because custom
|
||||
// themes can legitimately share slot values with a built-in.
|
||||
private static bool HasPopulatedCache(Theme theme)
|
||||
{
|
||||
var cache = theme.AbgrCache;
|
||||
return (cache.Primary
|
||||
| cache.WindowBg
|
||||
| cache.TextPrimary
|
||||
| cache.Border) != 0u;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user