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

142 lines
5.5 KiB
C#

using Dalamud.Bindings.ImGui;
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;
/// <summary>
/// Test setup for SeStringEvaluator.
/// </summary>
internal class SeStringEvaluatorSelfTestStep : ISelfTestStep
{
private int step = 0;
/// <inheritdoc/>
public string Name => "Test SeStringEvaluator";
/// <inheritdoc/>
public SelfTestStepResult RunStep()
{
var seStringEvaluator = Service<SeStringEvaluator>.Get();
switch (this.step)
{
case 0:
ImGui.Text("Is this the current time, and is it ticking?"u8);
// This checks that EvaluateFromAddon fetches the correct Addon row,
// that MacroDecoder.GetMacroTime()->SetTime() has been called
// and that local and global parameters have been read correctly.
ImGui.Text(seStringEvaluator.EvaluateFromAddon(31, [(uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds()]).ExtractText());
if (ImGui.Button("Yes"u8))
this.step++;
ImGui.SameLine();
if (ImGui.Button("No"u8))
return SelfTestStepResult.Fail;
break;
case 1:
ImGui.Text("Checking pcname macro using the local player name..."u8);
// This makes sure that NameCache.Instance()->TryGetCharacterInfoByEntityId() has been called,
// that it returned the local players name by using its EntityId,
// and that it didn't include the world name by checking the HomeWorldId against AgentLobby.Instance()->LobbyData.HomeWorldId.
var clientState = Service<ClientState>.Get();
var localPlayer = clientState.LocalPlayer;
if (localPlayer is null)
{
ImGui.Text("You need to be logged in for this step."u8);
if (ImGui.Button("Skip"u8))
return SelfTestStepResult.NotRan;
return SelfTestStepResult.Waiting;
}
var evaluatedPlayerName = seStringEvaluator.Evaluate(ReadOnlySeString.FromMacroString("<pcname(lnum1)>"), [localPlayer.EntityId]).ExtractText();
var localPlayerName = localPlayer.Name.TextValue;
if (evaluatedPlayerName != localPlayerName)
{
ImGui.Text("The player name doesn't match:"u8);
ImGui.Text($"Evaluated Player Name (got): {evaluatedPlayerName}");
ImGui.Text($"Local Player Name (expected): {localPlayerName}");
if (ImGui.Button("Continue"u8))
return SelfTestStepResult.Fail;
return SelfTestStepResult.Waiting;
}
this.step++;
break;
case 2:
ImGui.Text("Checking AutoTranslatePayload.Text results..."u8);
var config = Service<DalamudConfiguration>.Get();
var originalLanguageOverride = config.LanguageOverride;
Span<(string Language, uint Group, uint Key, string ExpectedText)> tests = [
("en", 49u, 209u, " albino karakul "), // Mount
("en", 62u, 116u, " /echo "), // TextCommand - testing Command
("en", 62u, 143u, " /dutyfinder "), // TextCommand - testing Alias over Command
("en", 65u, 67u, " Minion of Light "), // Companion - testing noun handling for the german language (special case)
("en", 71u, 7u, " Phantom Geomancer "), // MKDSupportJob
("de", 49u, 209u, " Albino-Karakul "), // Mount
("de", 62u, 115u, " /freiegesellschaft "), // TextCommand - testing Alias over Command
("de", 62u, 116u, " /echo "), // TextCommand - testing Command
("de", 65u, 67u, " Begleiter des Lichts "), // Companion - testing noun handling for the german language (special case)
("de", 71u, 7u, " Phantom-Geomant "), // MKDSupportJob
];
try
{
foreach (var (language, group, key, expectedText) in tests)
{
config.LanguageOverride = language;
var payload = new AutoTranslatePayload(group, key);
if (payload.Text != expectedText)
{
ImGui.Text($"Test failed for Group {group}, Key {key}");
ImGui.Text($"Expected: {expectedText}");
ImGui.Text($"Got: {payload.Text}");
if (ImGui.Button("Continue"u8))
return SelfTestStepResult.Fail;
return SelfTestStepResult.Waiting;
}
}
}
finally
{
config.LanguageOverride = originalLanguageOverride;
}
return SelfTestStepResult.Pass;
}
return SelfTestStepResult.Waiting;
}
/// <inheritdoc/>
public void CleanUp()
{
// ignored
this.step = 0;
}
}