From ae777000e29c6d58dc0813b5cb585e45f6344664 Mon Sep 17 00:00:00 2001 From: GrittyFrog Date: Sat, 11 Oct 2025 16:50:29 +1100 Subject: [PATCH] Plugin-registerable self tests The goal of this change is to let plugins register their own self-tests. We do this through the `ISelfTestRegistry` interface. For a plugin it would look like this: ```csharp [PluginService] public ISelfTestRegistry SelfTestRegistry // Somewhere that gets called by your plugin SelfTestRegistry.RegisterTestSteps([ new MySelfTestStep(), new MyOtherSelfTestStep() ]) ``` Where `MySelfTest` and `MyOtherSelfTest` are instances of the existing `ISelfTestStep` interface. The biggest changes are to `SelfTestWindow` and the introduction of `SelfTestWithResults`. I wanted to make sure test state wasn't lost when changing the dropdown state and I was finding it a bit annoying to work with the Dictionary now that we can't just rely on the index of the item. To fix this I moved all the "test run" state into `SelfTestWithResults`, most of the changes to `SelfTestWindow` are derived from that, other then the addition of the combo box. The documentation for this service is a bit sparse, but I wanted to put it up for review first before I invest a bunch of time making nice documentation. I'm keen to hear if we think this is useful or if any changes are needed. --- .../Windows/SelfTest/SelfTestWindow.cs | 279 ++++++++++-------- .../SelfTest/Steps/ActorTableSelfTestStep.cs | 1 + .../Steps/AddonLifecycleSelfTestStep.cs | 1 + .../Steps/AetheryteListSelfTestStep.cs | 1 + .../SelfTest/Steps/ChatSelfTestStep.cs | 1 + .../SelfTest/Steps/CompletionSelfTestStep.cs | 1 + .../SelfTest/Steps/ConditionSelfTestStep.cs | 1 + .../SelfTest/Steps/ContextMenuSelfTestStep.cs | 1 + .../Windows/SelfTest/Steps/DalamudSelfTest.cs | 51 ++++ .../SelfTest/Steps/DutyStateSelfTestStep.cs | 1 + .../Steps/EnterTerritorySelfTestStep.cs | 1 + .../SelfTest/Steps/FateTableSelfTestStep.cs | 1 + .../FrameworkTaskSchedulerSelfTestStep.cs | 1 + .../SelfTest/Steps/GameConfigSelfTestStep.cs | 1 + .../Steps/GamepadStateSelfTestStep.cs | 2 +- .../Steps/HandledExceptionSelfTestStep.cs | 2 + .../SelfTest/Steps/HoverSelfTestStep.cs | 1 + .../SelfTest/Steps/ItemPayloadSelfTestStep.cs | 1 + .../SelfTest/Steps/KeyStateSelfTestStep.cs | 1 + .../SelfTest/Steps/LoginEventSelfTestStep.cs | 1 + .../SelfTest/Steps/LogoutEventSelfTestStep.cs | 1 + .../SelfTest/Steps/LuminaSelfTestStep.cs | 1 + .../SelfTest/Steps/MarketBoardSelfTestStep.cs | 1 + .../SelfTest/Steps/NamePlateSelfTestStep.cs | 1 + .../Steps/NounProcessorSelfTestStep.cs | 1 + .../SelfTest/Steps/PartyFinderSelfTestStep.cs | 1 + .../Steps/SeStringEvaluatorSelfTestStep.cs | 2 +- .../SheetRedirectResolverSelfTestStep.cs | 1 + .../SelfTest/Steps/TargetSelfTestStep.cs | 1 + .../SelfTest/Steps/ToastSelfTestStep.cs | 1 + .../SelfTest/Steps/WaitFramesSelfTestStep.cs | 2 + Dalamud/Plugin/DalamudPluginInterface.cs | 2 + Dalamud/Plugin/IDalamudPluginInterface.cs | 1 + Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs | 54 ++++ .../SelfTest}/ISelfTestStep.cs | 4 +- .../Plugin/SelfTest/Internal/SelfTestGroup.cs | 28 ++ .../SelfTest/Internal/SelfTestRegistry.cs | 124 ++++++++ .../Internal/SelfTestRegistryPluginScoped.cs | 51 ++++ .../SelfTest/Internal/SelfTestWithResults.cs | 173 +++++++++++ .../SelfTest/SelfTestStepResult.cs | 4 +- Dalamud/Utility/Util.cs | 3 +- 41 files changed, 674 insertions(+), 133 deletions(-) create mode 100644 Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs create mode 100644 Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs rename Dalamud/{Interface/Internal/Windows/SelfTest/Steps => Plugin/SelfTest}/ISelfTestStep.cs (85%) create mode 100644 Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs create mode 100644 Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs create mode 100644 Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs create mode 100644 Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs rename Dalamud/{Interface/Internal/Windows => Plugin}/SelfTest/SelfTestStepResult.cs (81%) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index 86036b2ed..b19b4cf96 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -6,11 +6,13 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; -using Dalamud.Interface.Internal.Windows.SelfTest.Steps; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; +using Dalamud.Plugin.SelfTest; +using Dalamud.Plugin.SelfTest.Internal; +using Dalamud.Utility; using Lumina.Excel.Sheets; namespace Dalamud.Interface.Internal.Windows.SelfTest; @@ -22,49 +24,14 @@ internal class SelfTestWindow : Window { private static readonly ModuleLog Log = new("AGING"); - private readonly List steps = - [ - new LoginEventSelfTestStep(), - new WaitFramesSelfTestStep(1000), - new FrameworkTaskSchedulerSelfTestStep(), - new EnterTerritorySelfTestStep(148, "Central Shroud"), - new ItemPayloadSelfTestStep(), - new ContextMenuSelfTestStep(), - new NamePlateSelfTestStep(), - new ActorTableSelfTestStep(), - new FateTableSelfTestStep(), - new AetheryteListSelfTestStep(), - new ConditionSelfTestStep(), - new ToastSelfTestStep(), - new TargetSelfTestStep(), - new KeyStateSelfTestStep(), - new GamepadStateSelfTestStep(), - new ChatSelfTestStep(), - new HoverSelfTestStep(), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(false), - new AddonLifecycleSelfTestStep(), - new PartyFinderSelfTestStep(), - new HandledExceptionSelfTestStep(), - new DutyStateSelfTestStep(), - new GameConfigSelfTestStep(), - new MarketBoardSelfTestStep(), - new SheetRedirectResolverSelfTestStep(), - new NounProcessorSelfTestStep(), - new SeStringEvaluatorSelfTestStep(), - new CompletionSelfTestStep(), - new LogoutEventSelfTestStep() - ]; + private readonly SelfTestRegistry selfTestRegistry; - private readonly Dictionary testIndexToResult = new(); + private List visibleSteps = new(); private bool selfTestRunning = false; - private int currentStep = 0; - private int scrollToStep = -1; - private DateTimeOffset lastTestStart; + private SelfTestGroup? currentTestGroup = null; + private SelfTestWithResults? currentStep = null; + private SelfTestWithResults? scrollToStep = null; /// /// Initializes a new instance of the class. @@ -72,6 +39,7 @@ internal class SelfTestWindow : Window public SelfTestWindow() : base("Dalamud Self-Test", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse) { + this.selfTestRegistry = Service.Get(); this.Size = new Vector2(800, 800); this.SizeCondition = ImGuiCond.FirstUseEver; @@ -81,6 +49,55 @@ internal class SelfTestWindow : Window /// public override void Draw() { + // Initialize to first group if not set (first time drawing) + if (this.currentTestGroup == null) + { + this.currentTestGroup = this.selfTestRegistry.SelfTestGroups.FirstOrDefault(); // Should always be "Dalamud" + if (this.currentTestGroup != null) + { + this.SelectTestGroup(this.currentTestGroup); + } + } + + // Update visible steps based on current group + if (this.currentTestGroup != null) + { + this.visibleSteps = this.selfTestRegistry.SelfTests + .Where(test => test.Group == this.currentTestGroup.Name).ToList(); + + // Stop tests if no steps available or if current step is no longer valid + if (this.visibleSteps.Count == 0 || (this.currentStep != null && !this.visibleSteps.Contains(this.currentStep))) + { + this.StopTests(); + } + } + + using (var dropdown = ImRaii.Combo("###SelfTestGroupCombo"u8, this.currentTestGroup?.Name ?? string.Empty)) + { + if (dropdown) + { + foreach (var testGroup in this.selfTestRegistry.SelfTestGroups) + { + if (ImGui.Selectable(testGroup.Name)) + { + this.SelectTestGroup(testGroup); + } + + if (!testGroup.Loaded) + { + ImGui.SameLine(); + this.DrawUnloadedIcon(); + } + } + } + } + + if (this.currentTestGroup?.Loaded == false) + { + ImGui.SameLine(); + this.DrawUnloadedIcon(); + } + if (this.selfTestRunning) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) @@ -92,13 +109,10 @@ internal class SelfTestWindow : Window if (ImGuiComponents.IconButton(FontAwesomeIcon.StepForward)) { - this.testIndexToResult[this.currentStep] = (SelfTestStepResult.NotRan, null); - this.steps[this.currentStep].CleanUp(); - this.currentStep++; + this.currentStep.Reset(); + this.MoveToNextTest(); this.scrollToStep = this.currentStep; - this.lastTestStart = DateTimeOffset.Now; - - if (this.currentStep >= this.steps.Count) + if (this.currentStep == null) { this.StopTests(); } @@ -106,38 +120,50 @@ internal class SelfTestWindow : Window } else { + var canRunTests = this.currentTestGroup?.Loaded == true && this.visibleSteps.Count > 0; + + using var disabled = ImRaii.Disabled(!canRunTests); if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { this.selfTestRunning = true; - this.currentStep = 0; + this.currentStep = this.visibleSteps.FirstOrDefault(); this.scrollToStep = this.currentStep; - this.testIndexToResult.Clear(); - this.lastTestStart = DateTimeOffset.Now; + foreach (var test in this.visibleSteps) + { + test.Reset(); + } } } ImGui.SameLine(); - ImGui.Text($"Step: {this.currentStep} / {this.steps.Count}"); + var stepNumber = this.currentStep != null ? this.visibleSteps.IndexOf(this.currentStep) : 0; + ImGui.Text($"Step: {stepNumber} / {this.visibleSteps.Count}"); ImGui.Spacing(); + if (this.currentTestGroup?.Loaded == false) + { + ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, $"Plugin '{this.currentTestGroup.Name}' is unloaded. No tests available."); + ImGui.Spacing(); + } + this.DrawResultTable(); ImGui.Spacing(); - if (this.currentStep >= this.steps.Count) + if (this.currentStep == null) { if (this.selfTestRunning) { this.StopTests(); } - if (this.testIndexToResult.Any(x => x.Value.Result == SelfTestStepResult.Fail)) + if (this.visibleSteps.Any(test => test.Result == SelfTestStepResult.Fail)) { ImGui.TextColoredWrapped(ImGuiColors.DalamudRed, "One or more checks failed!"u8); } - else + else if (this.visibleSteps.All(test => test.Result == SelfTestStepResult.Pass)) { ImGui.TextColoredWrapped(ImGuiColors.HealerGreen, "All checks passed!"u8); } @@ -153,30 +179,14 @@ internal class SelfTestWindow : Window using var resultChild = ImRaii.Child("SelfTestResultChild"u8, ImGui.GetContentRegionAvail()); if (!resultChild) return; - var step = this.steps[this.currentStep]; - ImGui.Text($"Current: {step.Name}"); + ImGui.Text($"Current: {this.currentStep.Name}"); ImGuiHelpers.ScaledDummy(10); - SelfTestStepResult result; - try + this.currentStep.DrawAndStep(); + if (this.currentStep.Result != SelfTestStepResult.Waiting) { - result = step.RunStep(); - } - catch (Exception ex) - { - Log.Error(ex, $"Step failed: {step.Name}"); - result = SelfTestStepResult.Fail; - } - - if (result != SelfTestStepResult.Waiting) - { - var duration = DateTimeOffset.Now - this.lastTestStart; - this.testIndexToResult[this.currentStep] = (result, duration); - this.currentStep++; - this.scrollToStep = this.currentStep; - - this.lastTestStart = DateTimeOffset.Now; + this.MoveToNextTest(); } } @@ -202,85 +212,62 @@ internal class SelfTestWindow : Window ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); - for (var i = 0; i < this.steps.Count; i++) + foreach (var (step, index) in this.visibleSteps.WithIndex()) { - var step = this.steps[i]; ImGui.TableNextRow(); - if (this.selfTestRunning && this.currentStep == i) + if (this.selfTestRunning && this.currentStep == step) { ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(ImGuiCol.TableRowBgAlt)); } ImGui.TableSetColumnIndex(0); ImGui.AlignTextToFramePadding(); - ImGui.Text(i.ToString()); + ImGui.Text(index.ToString()); - if (this.selfTestRunning && this.scrollToStep == i) + if (this.selfTestRunning && this.scrollToStep == step) { ImGui.SetScrollHereY(); - this.scrollToStep = -1; + this.scrollToStep = null; } ImGui.TableSetColumnIndex(1); ImGui.AlignTextToFramePadding(); ImGui.Text(step.Name); - if (this.testIndexToResult.TryGetValue(i, out var result)) + ImGui.TableSetColumnIndex(2); + ImGui.AlignTextToFramePadding(); + switch (step.Result) { - ImGui.TableSetColumnIndex(2); - ImGui.AlignTextToFramePadding(); - - switch (result.Result) - { - case SelfTestStepResult.Pass: - ImGui.TextColored(ImGuiColors.HealerGreen, "PASS"u8); - break; - case SelfTestStepResult.Fail: - ImGui.TextColored(ImGuiColors.DalamudRed, "FAIL"u8); - break; - default: - ImGui.TextColored(ImGuiColors.DalamudGrey, "NR"u8); - break; - } - - ImGui.TableSetColumnIndex(3); - if (result.Duration.HasValue) - { - ImGui.AlignTextToFramePadding(); - ImGui.Text(this.FormatTimeSpan(result.Duration.Value)); - } - } - else - { - ImGui.TableSetColumnIndex(2); - ImGui.AlignTextToFramePadding(); - if (this.selfTestRunning && this.currentStep == i) - { + case SelfTestStepResult.Pass: + ImGui.TextColored(ImGuiColors.HealerGreen, "PASS"u8); + break; + case SelfTestStepResult.Fail: + ImGui.TextColored(ImGuiColors.DalamudRed, "FAIL"u8); + break; + case SelfTestStepResult.Waiting: ImGui.TextColored(ImGuiColors.DalamudGrey, "WAIT"u8); - } - else - { + break; + default: ImGui.TextColored(ImGuiColors.DalamudGrey, "NR"u8); - } + break; + } - ImGui.TableSetColumnIndex(3); + ImGui.TableSetColumnIndex(3); + if (step.Duration.HasValue) + { ImGui.AlignTextToFramePadding(); - if (this.selfTestRunning && this.currentStep == i) - { - ImGui.Text(this.FormatTimeSpan(DateTimeOffset.Now - this.lastTestStart)); - } + ImGui.Text(this.FormatTimeSpan(step.Duration.Value)); } ImGui.TableSetColumnIndex(4); - using var id = ImRaii.PushId($"selfTest{i}"); + using var id = ImRaii.PushId($"selfTest{index}"); if (ImGuiComponents.IconButton(FontAwesomeIcon.FastForward)) { this.StopTests(); - this.testIndexToResult.Remove(i); - this.currentStep = i; + this.currentStep = step; + this.currentStep.Reset(); this.selfTestRunning = true; - this.lastTestStart = DateTimeOffset.Now; } if (ImGui.IsItemHovered()) @@ -293,12 +280,14 @@ internal class SelfTestWindow : Window private void StopTests() { this.selfTestRunning = false; + this.currentStep = null; + this.scrollToStep = null; - foreach (var agingStep in this.steps) + foreach (var agingStep in this.visibleSteps) { try { - agingStep.CleanUp(); + agingStep.Finish(); } catch (Exception ex) { @@ -307,6 +296,46 @@ internal class SelfTestWindow : Window } } + /// + /// Makes the active test group. + /// + /// The test group to make active. + private void SelectTestGroup(SelfTestGroup testGroup) + { + this.currentTestGroup = testGroup; + this.StopTests(); + } + + /// + /// Move `currentTest` to the next test. If there are no tests left, set `currentTest` to null. + /// + private void MoveToNextTest() + { + if (this.currentStep == null) + { + this.currentStep = this.visibleSteps.FirstOrDefault(); + return; + } + + var currentIndex = this.visibleSteps.IndexOf(this.currentStep); + this.currentStep = this.visibleSteps.ElementAtOrDefault(currentIndex + 1); + } + + /// + /// Draws the unloaded plugin icon with tooltip. + /// + private void DrawUnloadedIcon() + { + ImGui.PushFont(UiBuilder.IconFont); + ImGui.TextColored(ImGuiColors.DalamudGrey, FontAwesomeIcon.Unlink.ToIconString()); + ImGui.PopFont(); + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Plugin is unloaded"); + } + } + private string FormatTimeSpan(TimeSpan ts) { var str = ts.ToString("g", CultureInfo.InvariantCulture); diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs index 0f0e10f10..013333935 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs index 9ba3c4f2b..d9c9facc7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs @@ -3,6 +3,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs index 4d4f3fbb1..644c23e88 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Aetherytes; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs index c2351cfd2..7a2631fbf 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs index 463616362..a34b058bd 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Command; using Dalamud.Interface.Utility; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs index 36e544040..89083da48 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Conditions; +using Dalamud.Plugin.SelfTest; using Serilog; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs index 83003f21f..0fe5b4443 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs @@ -8,6 +8,7 @@ using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.Gui.ContextMenu; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Plugin.SelfTest; using Lumina.Excel; using Lumina.Excel.Sheets; using Serilog; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs new file mode 100644 index 000000000..d36aec742 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs @@ -0,0 +1,51 @@ +using Dalamud.Plugin.SelfTest.Internal; +using Lumina.Excel.Sheets; + +namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; + +/// +/// Class handling Dalamud self-test registration. +/// +[ServiceManager.EarlyLoadedService] +internal class DalamudSelfTest : IServiceType +{ + [ServiceManager.ServiceConstructor] + private DalamudSelfTest(SelfTestRegistry registry) + { + registry.RegisterDalamudSelfTestSteps([ + new LoginEventSelfTestStep(), + new WaitFramesSelfTestStep(1000), + new FrameworkTaskSchedulerSelfTestStep(), + new EnterTerritorySelfTestStep(148, "Central Shroud"), + new ItemPayloadSelfTestStep(), + new ContextMenuSelfTestStep(), + new NamePlateSelfTestStep(), + new ActorTableSelfTestStep(), + new FateTableSelfTestStep(), + new AetheryteListSelfTestStep(), + new ConditionSelfTestStep(), + new ToastSelfTestStep(), + new TargetSelfTestStep(), + new KeyStateSelfTestStep(), + new GamepadStateSelfTestStep(), + new ChatSelfTestStep(), + new HoverSelfTestStep(), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(false), + new AddonLifecycleSelfTestStep(), + new PartyFinderSelfTestStep(), + new HandledExceptionSelfTestStep(), + new DutyStateSelfTestStep(), + new GameConfigSelfTestStep(), + new MarketBoardSelfTestStep(), + new SheetRedirectResolverSelfTestStep(), + new NounProcessorSelfTestStep(), + new SeStringEvaluatorSelfTestStep(), + new CompletionSelfTestStep(), + new LogoutEventSelfTestStep() + ]); + } +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs index 9c40aa345..bf3ffa5c9 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.DutyState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs index ff43d7ae3..4cce1e377 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs index ba5f769c2..a413f4932 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Fates; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs index d952be419..eb6909fa7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Dalamud.Game; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; using Microsoft.VisualBasic.Logging; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs index 67faec5c9..fa29068c5 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Config; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs index 04684d521..d272032e7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs @@ -2,7 +2,7 @@ using System.Linq; using Dalamud.Game.ClientState.GamePad; using Dalamud.Interface.Utility; - +using Dalamud.Plugin.SelfTest; using Lumina.Text.Payloads; using LSeStringBuilder = Lumina.Text.SeStringBuilder; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs index 9757481e4..c9f7df8a5 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs @@ -1,5 +1,7 @@ using System.Runtime.InteropServices; +using Dalamud.Plugin.SelfTest; + namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; /// diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs index 8c469d210..e33f16e7f 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs index 261318d6b..c441796a2 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs index 59ffb33ae..2e936884e 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Keys; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs index b5a6337ce..97df6bf94 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs index c8788968e..bc337cf3e 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs index ff9649a14..741dd71b1 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs @@ -1,4 +1,5 @@ using Dalamud.Data; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; using Lumina.Excel; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs index a7e9eb681..6a45f343a 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs @@ -5,6 +5,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.MarketBoard; using Dalamud.Game.Network.Structures; using Dalamud.Interface.Utility; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs index 120335c04..9cc6045a6 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs @@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.NamePlate; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs index 682fe7222..ccb23d395 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game; using Dalamud.Game.Text.Noun; using Dalamud.Game.Text.Noun.Enums; +using Dalamud.Plugin.SelfTest; using LSheets = Lumina.Excel.Sheets; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs index d8bdd6d53..052f65a21 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.PartyFinder; using Dalamud.Game.Gui.PartyFinder.Types; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs index 9853e31d4..7216e04d2 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs @@ -3,7 +3,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Game.ClientState; using Dalamud.Game.Text.Evaluator; using Dalamud.Game.Text.SeStringHandling.Payloads; - +using Dalamud.Plugin.SelfTest; using Lumina.Text.ReadOnly; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs index cd2e270db..c285fda46 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Game; using Dalamud.Game.Text.Evaluator.Internal; +using Dalamud.Plugin.SelfTest; using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI.Misc; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs index f3a1e2aab..4e5e287a4 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs index 4c66e7380..793473ecc 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.Toast; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs index 35c64376d..b0cc4f454 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs @@ -1,3 +1,5 @@ +using Dalamud.Plugin.SelfTest; + namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; /// diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 541071b63..39a4e7e4b 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -16,6 +16,7 @@ using Dalamud.Game.Text.Sanitizer; using Dalamud.Interface; using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.Windows.PluginInstaller; +using Dalamud.Interface.Internal.Windows.SelfTest; using Dalamud.Interface.Internal.Windows.Settings; using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal; @@ -25,6 +26,7 @@ using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Plugin.Services; using Serilog; diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index e98398a16..b8ab55450 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -15,6 +15,7 @@ using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Plugin.Services; namespace Dalamud.Plugin; diff --git a/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs new file mode 100644 index 000000000..af3b583c9 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace Dalamud.Plugin.SelfTest; + +/// +/// Interface for registering and unregistering self-test steps from plugins. +/// +/// +/// Registering custom self-test steps for your plugin: +/// +/// [PluginService] +/// public ISelfTestRegistry SelfTestRegistry { get; init; } +/// +/// // In your plugin initialization +/// this.SelfTestRegistry.RegisterTestSteps([ +/// new MyCustomSelfTestStep(), +/// new AnotherSelfTestStep() +/// ]); +/// +/// +/// Creating a custom self-test step: +/// +/// public class MyCustomSelfTestStep : ISelfTestStep +/// { +/// public string Name => "My Custom Test"; +/// +/// public SelfTestStepResult RunStep() +/// { +/// // Your test logic here +/// if (/* test condition passes */) +/// return SelfTestStepResult.Pass; +/// +/// if (/* test condition fails */) +/// return SelfTestStepResult.Fail; +/// +/// // Still waiting for test to complete +/// return SelfTestStepResult.Waiting; +/// } +/// +/// public void CleanUp() +/// { +/// // Clean up any resources used by the test +/// } +/// } +/// +/// +public interface ISelfTestRegistry +{ + /// + /// Registers the self-test steps for this plugin. + /// + /// The test steps to register. + public void RegisterTestSteps(IEnumerable steps); +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs b/Dalamud/Plugin/SelfTest/ISelfTestStep.cs similarity index 85% rename from Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs rename to Dalamud/Plugin/SelfTest/ISelfTestStep.cs index 529d788ab..2b0de7bf3 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs +++ b/Dalamud/Plugin/SelfTest/ISelfTestStep.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; +namespace Dalamud.Plugin.SelfTest; /// /// Interface for test implementations. /// -internal interface ISelfTestStep +public interface ISelfTestStep { /// /// Gets the name of the test. diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs new file mode 100644 index 000000000..e2bc80f4b --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs @@ -0,0 +1,28 @@ +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Represents a self-test group with its loaded/unloaded state. +/// +internal class SelfTestGroup +{ + /// + /// Initializes a new instance of the class. + /// + /// The name of the test group. + /// Whether the group is currently loaded. + public SelfTestGroup(string name, bool loaded = true) + { + this.Name = name; + this.Loaded = loaded; + } + + /// + /// Gets the name of the test group. + /// + public string Name { get; } + + /// + /// Gets or sets a value indicating whether this test group is currently loaded. + /// + public bool Loaded { get; set; } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs new file mode 100644 index 000000000..aa410b2c4 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Linq; + +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Registry for self-tests that can be run in the SelfTest window. +/// +[ServiceManager.EarlyLoadedService] +internal class SelfTestRegistry : IServiceType +{ + /// + /// The name of the Dalamud test group. + /// + public const string DalamudTestGroup = "Dalamud"; + + private static readonly ModuleLog Log = new("SelfTestRegistry"); + + private List dalamudSelfTests = new(); + private List pluginSelfTests = new(); + private Dictionary allGroups = new(); + + /// + /// Initializes a new instance of the class. + /// + [ServiceManager.ServiceConstructor] + public SelfTestRegistry() + { + } + + /// + /// Gets all available self test groups. + /// + public IEnumerable SelfTestGroups + { + get + { + // Always return Dalamud group first, then plugin groups + if (this.allGroups.TryGetValue(DalamudTestGroup, out var dalamudGroup)) + { + yield return dalamudGroup; + } + + foreach (var group in this.allGroups.Values) + { + if (group.Name != DalamudTestGroup) + { + yield return group; + } + } + } + } + + /// + /// Gets all self tests from all groups. + /// + public IEnumerable SelfTests => this.dalamudSelfTests.Concat(this.pluginSelfTests); + + /// + /// Registers Dalamud self test steps. + /// + /// The steps to register. + public void RegisterDalamudSelfTestSteps(IEnumerable steps) + { + // Ensure Dalamud group exists and is loaded + if (!this.allGroups.ContainsKey(DalamudTestGroup)) + { + this.allGroups[DalamudTestGroup] = new SelfTestGroup(DalamudTestGroup, loaded: true); + } + else + { + this.allGroups[DalamudTestGroup].Loaded = true; + } + + this.dalamudSelfTests.AddRange(steps.Select(step => SelfTestWithResults.FromDalamudStep(step))); + } + + /// + /// Registers plugin self test steps. + /// + /// The plugin registering the tests. + /// The steps to register. + public void RegisterPluginSelfTestSteps(LocalPlugin plugin, IEnumerable steps) + { + // Ensure plugin group exists and is loaded + if (!this.allGroups.ContainsKey(plugin.InternalName)) + { + this.allGroups[plugin.InternalName] = new SelfTestGroup(plugin.InternalName, loaded: true); + } + else + { + this.allGroups[plugin.InternalName].Loaded = true; + } + + this.pluginSelfTests.AddRange(steps.Select(step => SelfTestWithResults.FromPluginStep(plugin, step))); + } + + /// + /// Unregisters all self test steps for a plugin. + /// + /// The plugin to unregister tests for. + public void UnregisterPluginSelfTestSteps(LocalPlugin plugin) + { + // Clean up existing tests for this plugin + this.pluginSelfTests.ForEach(test => + { + if (test.Plugin == plugin) + { + test.Unload(); + } + }); + + this.pluginSelfTests.RemoveAll(test => test.Plugin == plugin); + + // Mark group as unloaded if it exists + if (this.allGroups.ContainsKey(plugin.InternalName)) + { + this.allGroups[plugin.InternalName].Loaded = false; + } + } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs new file mode 100644 index 000000000..18c518879 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Internal.Types; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Plugin-scoped version of SelfTestRegistry. +/// +[PluginInterface] +[ServiceManager.ScopedService] +[ResolveVia] +internal class SelfTestRegistryPluginScoped : ISelfTestRegistry, IInternalDisposableService +{ + [ServiceManager.ServiceDependency] + private readonly SelfTestRegistry selfTestRegistry = Service.Get(); + + private readonly LocalPlugin plugin; + + /// + /// Initializes a new instance of the class. + /// + /// The plugin this service belongs to. + [ServiceManager.ServiceConstructor] + public SelfTestRegistryPluginScoped(LocalPlugin plugin) + { + this.plugin = plugin; + } + + /// + /// Gets the plugin name. + /// + public string PluginName { get; private set; } + + /// + /// Registers test steps for this plugin. + /// + /// The test steps to register. + public void RegisterTestSteps(IEnumerable steps) + { + this.selfTestRegistry.RegisterPluginSelfTestSteps(this.plugin, steps); + } + + /// + void IInternalDisposableService.DisposeService() + { + this.selfTestRegistry.UnregisterPluginSelfTestSteps(this.plugin); + } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs new file mode 100644 index 000000000..3c03f66a8 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs @@ -0,0 +1,173 @@ +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// A self test step with result tracking. +/// +internal class SelfTestWithResults +{ + private static readonly ModuleLog Log = new("SelfTest"); + + /// + /// Initializes a new instance of the class. + /// + /// The plugin providing this test. + /// The test group name. + /// The test step. + public SelfTestWithResults(LocalPlugin plugin, string group, ISelfTestStep step) + { + this.Plugin = plugin; + this.Group = group; + this.Step = step; + } + + /// + /// Gets the test group name. + /// + public string Group { get; private set; } + + /// + /// Gets the plugin that defined these tests. null for Dalamud tests. + /// + public LocalPlugin? Plugin { get; private set; } + + /// + /// Gets the test name. + /// + public string Name { get => this.Step.Name; } + + /// + /// Gets a value indicating whether the test has run and finished. + /// + public bool Finished => this.Result == SelfTestStepResult.Fail || this.Result == SelfTestStepResult.Pass; + + /// + /// Gets a value indicating whether the plugin that provided this test has been unloaded. + /// + public bool Unloaded => this.Step == null; + + /// + /// Gets the most recent result of running this test. + /// + public SelfTestStepResult Result { get; private set; } = SelfTestStepResult.NotRan; + + /// + /// Gets the last time this test was started. + /// + public DateTimeOffset? StartTime { get; private set; } = null; + + /// + /// Gets how long it took (or is taking) for this test to execute. + /// + public TimeSpan? Duration { get; private set; } = null; + + /// + /// Gets or sets the Step that our results are for. + /// + /// If null it means the Plugin that provided this test has been unloaded and we can't use this test anymore. + /// + private ISelfTestStep? Step { get; set; } + + /// + /// Creates a SelfTestWithResults from a Dalamud step. + /// + /// The step to wrap. + /// A new SelfTestWithResults instance. + public static SelfTestWithResults FromDalamudStep(ISelfTestStep step) + { + return new SelfTestWithResults(plugin: null, group: "Dalamud", step: step); + } + + /// + /// Creates a SelfTestWithResults from a plugin step. + /// + /// The plugin providing the step. + /// The step to wrap. + /// A new SelfTestWithResults instance. + public static SelfTestWithResults FromPluginStep(LocalPlugin plugin, ISelfTestStep step) + { + return new SelfTestWithResults(plugin: plugin, group: plugin.InternalName, step: step); + } + + /// + /// Reset the test. + /// + public void Reset() + { + this.Result = SelfTestStepResult.NotRan; + this.StartTime = null; + this.Duration = null; + } + + /// + /// Finish the currently running test and clean up any state. This preserves test run results. + /// + public void Finish() + { + if (this.Step == null) + { + return; + } + + if (this.Result == SelfTestStepResult.NotRan) + { + return; + } + + this.Step.CleanUp(); + } + + /// + /// Steps the state of this Self Test. This should be called every frame that we care about the results of this test. + /// + public void DrawAndStep() + { + // If we've been unloaded then there's nothing to do. + if (this.Step == null) + { + return; + } + + // If we have already finished then there's nothing to do + if (this.Finished) + { + return; + } + + // Otherwise, we assume that calling this functions means we are running the test. + if (this.Result == SelfTestStepResult.NotRan) + { + this.StartTime = DateTimeOffset.Now; + this.Result = SelfTestStepResult.Waiting; + } + + try + { + this.Result = this.Step.RunStep(); + } + catch (Exception ex) + { + Log.Error(ex, $"Step failed: {this.Name} ({this.Group})"); + this.Result = SelfTestStepResult.Fail; + } + + this.Duration = DateTimeOffset.Now - this.StartTime; + + // If we ran and finished we need to clean up + if (this.Finished) + { + this.Finish(); + } + } + + /// + /// Unloads the test and cleans up. + /// + public void Unload() + { + this.Finish(); + this.Step = null; + } +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs b/Dalamud/Plugin/SelfTest/SelfTestStepResult.cs similarity index 81% rename from Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs rename to Dalamud/Plugin/SelfTest/SelfTestStepResult.cs index 5abc8e2ed..0eaf77381 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs +++ b/Dalamud/Plugin/SelfTest/SelfTestStepResult.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Interface.Internal.Windows.SelfTest; +namespace Dalamud.Plugin.SelfTest; /// /// Enum declaring result states of tests. /// -internal enum SelfTestStepResult +public enum SelfTestStepResult { /// /// Test was not ran. diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index ff06618ab..4d578db40 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -18,6 +18,7 @@ using Dalamud.Game; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Colors; +using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Support; @@ -28,8 +29,6 @@ using Windows.Win32.System.Memory; using Windows.Win32.System.Ole; using Windows.Win32.UI.WindowsAndMessaging; -using Dalamud.Interface.Internal; - using FLASHWINFO = Windows.Win32.UI.WindowsAndMessaging.FLASHWINFO; using HWND = Windows.Win32.Foundation.HWND; using MEMORY_BASIC_INFORMATION = Windows.Win32.System.Memory.MEMORY_BASIC_INFORMATION;