Dalamud/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs
GrittyFrog ae777000e2 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.
2025-10-13 19:04:08 +11:00

124 lines
3.8 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Types;
namespace Dalamud.Plugin.SelfTest.Internal;
/// <summary>
/// Registry for self-tests that can be run in the SelfTest window.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal class SelfTestRegistry : IServiceType
{
/// <summary>
/// The name of the Dalamud test group.
/// </summary>
public const string DalamudTestGroup = "Dalamud";
private static readonly ModuleLog Log = new("SelfTestRegistry");
private List<SelfTestWithResults> dalamudSelfTests = new();
private List<SelfTestWithResults> pluginSelfTests = new();
private Dictionary<string, SelfTestGroup> allGroups = new();
/// <summary>
/// Initializes a new instance of the <see cref="SelfTestRegistry"/> class.
/// </summary>
[ServiceManager.ServiceConstructor]
public SelfTestRegistry()
{
}
/// <summary>
/// Gets all available self test groups.
/// </summary>
public IEnumerable<SelfTestGroup> 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;
}
}
}
}
/// <summary>
/// Gets all self tests from all groups.
/// </summary>
public IEnumerable<SelfTestWithResults> SelfTests => this.dalamudSelfTests.Concat(this.pluginSelfTests);
/// <summary>
/// Registers Dalamud self test steps.
/// </summary>
/// <param name="steps">The steps to register.</param>
public void RegisterDalamudSelfTestSteps(IEnumerable<ISelfTestStep> 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)));
}
/// <summary>
/// Registers plugin self test steps.
/// </summary>
/// <param name="plugin">The plugin registering the tests.</param>
/// <param name="steps">The steps to register.</param>
public void RegisterPluginSelfTestSteps(LocalPlugin plugin, IEnumerable<ISelfTestStep> 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)));
}
/// <summary>
/// Unregisters all self test steps for a plugin.
/// </summary>
/// <param name="plugin">The plugin to unregister tests for.</param>
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;
}
}
}