Dalamud/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.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

90 lines
2.3 KiB
C#

using System.Threading.Tasks;
using Dalamud.Game;
using Dalamud.Plugin.SelfTest;
using Dalamud.Utility;
using Microsoft.VisualBasic.Logging;
using Log = Serilog.Log;
namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps;
/// <summary>
/// Test setup for Framework task scheduling.
/// </summary>
internal class FrameworkTaskSchedulerSelfTestStep : ISelfTestStep
{
private bool passed = false;
private Task? task;
/// <inheritdoc/>
public string Name => "Test Framework Task Scheduler";
/// <inheritdoc/>
public SelfTestStepResult RunStep()
{
var framework = Service<Framework>.Get();
this.task ??= Task.Run(async () =>
{
ThreadSafety.AssertNotMainThread();
await framework.Run(async () =>
{
ThreadSafety.AssertMainThread();
await Task.Delay(100).ConfigureAwait(true);
ThreadSafety.AssertMainThread();
await Task.Delay(100).ConfigureAwait(false);
ThreadSafety.AssertNotMainThread();
}).ConfigureAwait(true);
ThreadSafety.AssertNotMainThread();
await framework.RunOnTick(async () =>
{
ThreadSafety.AssertMainThread();
await Task.Delay(100).ConfigureAwait(true);
ThreadSafety.AssertNotMainThread();
await Task.Delay(100).ConfigureAwait(false);
ThreadSafety.AssertNotMainThread();
}).ConfigureAwait(true);
ThreadSafety.AssertNotMainThread();
await framework.RunOnTick(() =>
{
ThreadSafety.AssertMainThread();
});
ThreadSafety.AssertMainThread();
this.passed = true;
}).ContinueWith(
t =>
{
if (t.IsFaulted)
{
Log.Error(t.Exception, "Framework Task scheduler test failed");
}
});
if (this.task is { IsFaulted: true } or { IsCanceled: true })
{
return SelfTestStepResult.Fail;
}
return this.passed ? SelfTestStepResult.Pass : SelfTestStepResult.Waiting;
}
/// <inheritdoc/>
public void CleanUp()
{
this.passed = false;
this.task = null;
}
}