test(selftest): add PayloadHandler and ChunkRenderer ctor smoke steps
This commit is contained in:
@@ -118,6 +118,14 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
|||||||
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
||||||
internal Integrations.CustomAudioPlayer CustomAudioPlayer { get; private set; } = null!;
|
internal Integrations.CustomAudioPlayer CustomAudioPlayer { get; private set; } = null!;
|
||||||
|
|
||||||
|
// Ctor-smoke anchors (B0-2). Exposed so the Payload/Chunk ctor-smoke steps
|
||||||
|
// can drive the real per-frame Lender path (Borrow()) and the eager
|
||||||
|
// singletons through the container, never via new(). Mirror of the
|
||||||
|
// FontManager property pattern — every SelfTest reaches services this way.
|
||||||
|
internal PayloadHandler PayloadHandler { get; private set; } = null!;
|
||||||
|
internal Util.Lender<PayloadHandler> PayloadHandlerLender { get; private set; } = null!;
|
||||||
|
internal Ui.Components.ChunkRenderer ChunkRenderer { get; private set; } = null!;
|
||||||
|
|
||||||
// Platform indirection over Dalamud.Utility.Util. Wired in Phase-1 ctor so
|
// Platform indirection over Dalamud.Utility.Util. Wired in Phase-1 ctor so
|
||||||
// any service allocated in LoadAsync can read Plugin.PlatformUtil.
|
// any service allocated in LoadAsync can read Plugin.PlatformUtil.
|
||||||
internal static IPlatformUtil PlatformUtil { get; private set; } = null!;
|
internal static IPlatformUtil PlatformUtil { get; private set; } = null!;
|
||||||
@@ -304,6 +312,16 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
|||||||
DebuggerWindow = _host.Services.GetRequiredService<DebuggerWindow>();
|
DebuggerWindow = _host.Services.GetRequiredService<DebuggerWindow>();
|
||||||
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
|
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
|
||||||
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
|
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
|
||||||
|
|
||||||
|
// Ctor-smoke anchors (B0-2). Resolved last, against the fully built
|
||||||
|
// container: every MakePayloadHandler dep (MainWindow, InputBar,
|
||||||
|
// ChunkRenderer, ...) is resolvable here, and the ChunkRenderer resolve
|
||||||
|
// below just reuses the same cached singleton. These are plain
|
||||||
|
// post-build container resolves (no new factory-lambda edge) — they add
|
||||||
|
// no DI cycle. See feedback_di_factory_callsite_cycles.
|
||||||
|
PayloadHandler = _host.Services.GetRequiredService<PayloadHandler>();
|
||||||
|
PayloadHandlerLender = _host.Services.GetRequiredService<Util.Lender<PayloadHandler>>();
|
||||||
|
ChunkRenderer = _host.Services.GetRequiredService<Ui.Components.ChunkRenderer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadAsync(CancellationToken cancellationToken)
|
public async Task LoadAsync(CancellationToken cancellationToken)
|
||||||
@@ -340,6 +358,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
|||||||
new SelfTests.ThemeSwitchSelfTestStep(this),
|
new SelfTests.ThemeSwitchSelfTestStep(this),
|
||||||
new SelfTests.ThemeCrossfadeSelfTestStep(this),
|
new SelfTests.ThemeCrossfadeSelfTestStep(this),
|
||||||
new SelfTests.FontManagerCtorSmokeStep(this),
|
new SelfTests.FontManagerCtorSmokeStep(this),
|
||||||
|
new SelfTests.PayloadHandlerCtorSmokeStep(this),
|
||||||
|
new SelfTests.ChunkRendererCtorSmokeStep(this),
|
||||||
new SelfTests.FontPushSmokeStep(this),
|
new SelfTests.FontPushSmokeStep(this),
|
||||||
new SelfTests.WizardStateSmokeStep(this),
|
new SelfTests.WizardStateSmokeStep(this),
|
||||||
new SelfTests.FoxBannerTextureSmokeStep(this),
|
new SelfTests.FoxBannerTextureSmokeStep(this),
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Plugin.SelfTest;
|
||||||
|
|
||||||
|
namespace HellionChat.SelfTests;
|
||||||
|
|
||||||
|
// ChunkRenderer is a plain singleton (PluginHostFactory.cs:247) consumed by the
|
||||||
|
// real render path (MainWindow/MessageList/InputPreview DrawChunks). One
|
||||||
|
// resolution path is enough — unlike PayloadHandler there is no Lender. The
|
||||||
|
// type exposes no post-ctor observables (no LoadException-style state), so the
|
||||||
|
// honest assertion is "the DI ctor resolved a non-null instance". If a
|
||||||
|
// dependency registration breaks, Plugin's eager resolve throws before this
|
||||||
|
// step; the step pins that the singleton is reachable through the real
|
||||||
|
// container property, not via new().
|
||||||
|
internal sealed class ChunkRendererCtorSmokeStep : ISelfTestStep
|
||||||
|
{
|
||||||
|
private readonly Plugin plugin;
|
||||||
|
|
||||||
|
public ChunkRendererCtorSmokeStep(Plugin plugin)
|
||||||
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => "Hellion Chat - ChunkRenderer ctor smoke";
|
||||||
|
|
||||||
|
public SelfTestStepResult RunStep()
|
||||||
|
{
|
||||||
|
if (this.plugin.ChunkRenderer is null)
|
||||||
|
{
|
||||||
|
ImGui.Text("Plugin.ChunkRenderer is null");
|
||||||
|
return SelfTestStepResult.Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SelfTestStepResult.Pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanUp() { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Plugin.SelfTest;
|
||||||
|
|
||||||
|
namespace HellionChat.SelfTests;
|
||||||
|
|
||||||
|
// Drives the per-frame Lender<PayloadHandler> path the same way MainWindow.Draw
|
||||||
|
// and InputPreview do (Borrow() + ResetCounter()), NOT the eager singleton.
|
||||||
|
// PayloadHandler is registered twice (PluginHostFactory.cs:253/254): an eager
|
||||||
|
// singleton for the init HostedServices, and a Lender<T> factory-lambda for
|
||||||
|
// per-frame isolation. MS.DI resolves factory lambdas lazily and does not
|
||||||
|
// detect cycles through them, so a Borrow() that throws is the only automated
|
||||||
|
// signal of a broken lazy ctor before the first real frame renders. A
|
||||||
|
// singleton-only smoke would resolve the eager instance and mask exactly that
|
||||||
|
// failure. Resolve through the container/Lender, never new().
|
||||||
|
internal sealed class PayloadHandlerCtorSmokeStep : ISelfTestStep
|
||||||
|
{
|
||||||
|
private readonly Plugin plugin;
|
||||||
|
|
||||||
|
public PayloadHandlerCtorSmokeStep(Plugin plugin)
|
||||||
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => "Hellion Chat - PayloadHandler ctor smoke";
|
||||||
|
|
||||||
|
public SelfTestStepResult RunStep()
|
||||||
|
{
|
||||||
|
var lender = this.plugin.PayloadHandlerLender;
|
||||||
|
if (lender is null)
|
||||||
|
{
|
||||||
|
ImGui.Text("Plugin.PayloadHandlerLender is null");
|
||||||
|
return SelfTestStepResult.Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrow() runs MakePayloadHandler's factory lambda on first use; a
|
||||||
|
// throw or null here means a broken lazy ctor. This is the real
|
||||||
|
// per-frame construction path, not the eager singleton.
|
||||||
|
var borrowed = lender.Borrow();
|
||||||
|
|
||||||
|
// Keep the probe idempotent and avoid perturbing the frame path:
|
||||||
|
// MainWindow.Draw resets this same shared Lender every frame, so
|
||||||
|
// resetting here leaves a closed-MainWindow /xlperf run clean too.
|
||||||
|
lender.ResetCounter();
|
||||||
|
|
||||||
|
if (borrowed is null)
|
||||||
|
{
|
||||||
|
ImGui.Text("Lender<PayloadHandler>.Borrow() returned null");
|
||||||
|
return SelfTestStepResult.Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second construction path: the eager singleton the init HostedServices
|
||||||
|
// consume (PluginHostFactory.cs:253, :356). Assert it resolved too.
|
||||||
|
if (this.plugin.PayloadHandler is null)
|
||||||
|
{
|
||||||
|
ImGui.Text("Plugin.PayloadHandler (singleton) is null");
|
||||||
|
return SelfTestStepResult.Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we deliberately do NOT assert HandleTooltips == false /
|
||||||
|
// HoveredItem == 0u. MainWindow and InputPreview share this Lender, so a
|
||||||
|
// warm pool can hand back a reused instance whose hover state was set by
|
||||||
|
// a prior frame. The honest ctor-smoke assertion is "constructs through
|
||||||
|
// the real lazy path and is reachable" — a non-default warm value does
|
||||||
|
// not contradict that.
|
||||||
|
return SelfTestStepResult.Pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanUp() { }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user