acdream/tests/AcDream.Plugins.MossTank.Tests/MossTankPanelTests.cs

251 lines
8.1 KiB
C#

using AcDream.Plugin.Abstractions;
namespace AcDream.Plugins.MossTank.Tests;
public sealed class MossTankPanelTests
{
[Fact]
public void RetainedLabelReads_UseUpdateSideSnapshotsWithoutAllocating()
{
var automation = new FakeAutomation
{
CurrentHealth = 90,
MaxHealth = 100,
CurrentStamina = 80,
MaxStamina = 110,
CurrentMana = 70,
MaxMana = 120,
Skills =
[
new PluginSkillInfo(1, "Life Magic", PluginSkillTraining.Trained, 300),
new PluginSkillInfo(2, "War Magic", PluginSkillTraining.Specialized, 350),
new PluginSkillInfo(3, "Run", PluginSkillTraining.Untrained, 100),
],
Attributes =
[
new PluginAttributeInfo(0, "Strength", 100),
new PluginAttributeInfo(1, "Endurance", 100),
],
KnownSelfBuffs =
[
Spell(1, 10, "Increases the caster's Life Magic skill by 10 points."),
],
};
var panel = new MossTankPanel(new FakeHost(automation));
panel.OnTick(0.0);
Assert.Equal("Health 90/100 Stam 80/110 Mana 70/120", panel.Vitals);
Assert.Equal("2 attributes, 2 trained skills, 1 buff lines", panel.Coverage);
string expectedVitals = panel.Vitals;
string expectedCoverage = panel.Coverage;
_ = panel.Vitals;
_ = panel.Coverage;
long before = GC.GetAllocatedBytesForCurrentThread();
bool sameReferences = true;
for (int i = 0; i < 10_000; i++)
{
sameReferences &= ReferenceEquals(expectedVitals, panel.Vitals);
sameReferences &= ReferenceEquals(expectedCoverage, panel.Coverage);
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.True(sameReferences);
Assert.Equal(0, allocated);
Assert.Equal(1, automation.KnownSelfBuffReads);
}
[Fact]
public void UpdateTick_RefreshesRareCoverageAndChangedVitals()
{
var automation = new FakeAutomation
{
CurrentHealth = 90,
MaxHealth = 100,
CurrentStamina = 80,
MaxStamina = 110,
CurrentMana = 70,
MaxMana = 120,
Skills =
[
new PluginSkillInfo(1, "Life Magic", PluginSkillTraining.Trained, 300),
],
Attributes = [new PluginAttributeInfo(0, "Strength", 100)],
KnownSelfBuffs =
[
Spell(1, 10, "Increases the caster's Life Magic skill by 10 points."),
],
};
var panel = new MossTankPanel(new FakeHost(automation));
panel.OnTick(0.0);
automation.CurrentHealth = 75;
automation.Skills =
[
new PluginSkillInfo(1, "Life Magic", PluginSkillTraining.Trained, 300),
new PluginSkillInfo(2, "War Magic", PluginSkillTraining.Specialized, 350),
];
panel.OnTick(0.5);
Assert.StartsWith("Health 75/100", panel.Vitals, StringComparison.Ordinal);
Assert.Equal("1 attributes, 1 trained skills, 1 buff lines", panel.Coverage);
panel.OnTick(0.5);
Assert.Equal("1 attributes, 2 trained skills, 1 buff lines", panel.Coverage);
automation.KnownSelfBuffs =
[
Spell(1, 10, "Increases the caster's Life Magic skill by 10 points."),
Spell(2, 11, "Increases the caster's War Magic skill by 10 points."),
];
panel.OnTick(0.0);
Assert.Equal("1 attributes, 2 trained skills, 2 buff lines", panel.Coverage);
}
private static PluginSpellInfo Spell(uint id, uint family, string description) => new(
id,
$"Spell {id}",
family,
Tier: 1,
Difficulty: 10,
ManaCost: 5,
DurationSeconds: 60f,
School: 1,
description,
IsSelfTargeted: true,
IsBeneficial: true);
private sealed class FakeHost(IAutomationSurface automation) : IPluginHost
{
public bool HasUi => false;
public IPluginLogger Log { get; } = new FakeLogger();
public IGameState State { get; } = new FakeState();
public IEvents Events { get; } = new FakeEvents();
public ISelectionService Selection { get; } = new FakeSelection();
public IUiRegistry Ui => NoOpUiRegistry.Instance;
public IAutomationSurface Automation { get; } = automation;
}
private sealed class FakeAutomation
: IAutomationSurface, ICharacterInfo, ISpellCatalog, IMagicCommands, IPluginChat
{
private IReadOnlyList<PluginSpellInfo> _knownSelfBuffs = [];
public bool IsAvailable { get; set; } = true;
public ICharacterInfo Character => this;
public ISpellCatalog Spells => this;
public IMagicCommands Magic => this;
public IPluginChat Chat => this;
public bool IsInWorld => IsAvailable;
public uint ObjectId { get; set; } = 1;
public uint CurrentHealth { get; set; }
public uint MaxHealth { get; set; }
public uint CurrentStamina { get; set; }
public uint MaxStamina { get; set; }
public uint CurrentMana { get; set; }
public uint MaxMana { get; set; }
public IReadOnlyList<PluginSkillInfo> Skills { get; set; } = [];
public IReadOnlyList<PluginAttributeInfo> Attributes { get; set; } = [];
public IReadOnlyList<PluginActiveEnchantment> ActiveEnchantments { get; set; } = [];
public int KnownSelfBuffReads { get; private set; }
public IReadOnlyList<PluginSpellInfo> KnownSelfBuffs
{
get
{
KnownSelfBuffReads++;
return _knownSelfBuffs;
}
set => _knownSelfBuffs = value;
}
public bool TryGetSkill(uint skillId, out PluginSkillInfo skill)
{
foreach (PluginSkillInfo candidate in Skills)
{
if (candidate.SkillId == skillId)
{
skill = candidate;
return true;
}
}
skill = default;
return false;
}
public bool TryGet(uint spellId, out PluginSpellInfo info)
{
foreach (PluginSpellInfo candidate in _knownSelfBuffs)
{
if (candidate.SpellId == spellId)
{
info = candidate;
return true;
}
}
info = default;
return false;
}
public bool IsCasting => false;
public PluginCastGate EvaluateGate(uint spellId) => PluginCastGate.Ready;
public bool Cast(uint spellId) => true;
public void PostSystemMessage(string text) { }
}
private sealed class FakeLogger : IPluginLogger
{
public void Info(string message) { }
public void Warn(string message) { }
public void Error(string message, Exception? exception = null) { }
}
private sealed class FakeState : IGameState
{
public IReadOnlyList<WorldEntitySnapshot> Entities => [];
}
private sealed class FakeEvents : IEvents
{
public event Action<WorldEntitySnapshot> EntitySpawned
{
add { }
remove { }
}
public event Action<double> Tick
{
add { }
remove { }
}
}
private sealed class FakeSelection : ISelectionService
{
public uint? SelectedObjectId { get; private set; }
public uint? PreviousObjectId { get; private set; }
public event Action<SelectionChangedEvent> Changed
{
add { }
remove { }
}
public bool Select(uint objectId)
{
PreviousObjectId = SelectedObjectId;
SelectedObjectId = objectId;
return true;
}
public bool Clear()
{
PreviousObjectId = SelectedObjectId;
SelectedObjectId = null;
return true;
}
}
}