acdream/tests/AcDream.App.Tests/UI/Layout/CharacterManagementLiveDatTests.cs

181 lines
7.1 KiB
C#

using System.IO;
using AcDream.App.UI;
using AcDream.App.UI.Layout;
using AcDream.Content;
using DatReaderWriter;
using DatReaderWriter.Options;
using StringTable = DatReaderWriter.DBObjs.StringTable;
namespace AcDream.App.Tests.UI.Layout;
/// <summary>
/// Installed-retail-DAT acceptance gate for LA8. Opt in with
/// <c>ACDREAM_PROBE_LIVE_MOUNT=1</c>; <c>ACDREAM_DAT_DIR</c> can override the
/// ordinary Documents/Asheron's Call location. Reads the DATs read-only.
/// </summary>
public sealed class CharacterManagementLiveDatTests
{
[InstalledDatFact]
public void EnumTable5_ResolvesAndImportsTheExactRetailScreenAndDialogs()
{
string datDirectory = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Documents",
"Asheron's Call");
using var dats = new DatCollection(datDirectory, DatAccessType.Read);
const uint expectedLayoutDid = 0x21000004u;
uint layoutDid = RetailDataIdResolver.Resolve(
dats,
CharacterManagementUiController.RootEnum,
5u);
Assert.Equal(expectedLayoutDid, layoutDid);
Console.WriteLine(
"[LA8-DAT] category=5 enum=0x10000005 -> DID=0x21000004; "
+ "selected-root=0x1000039A");
ElementInfo rootInfo = Assert.IsType<ElementInfo>(
LayoutImporter.ImportInfos(
dats,
layoutDid,
CharacterManagementUiController.RootElementId));
Assert.Equal(800f, rootInfo.Width);
Assert.Equal(600f, rootInfo.Height);
Assert.Equal(8, rootInfo.Children.Count);
Assert.Equal(0x06007576u, rootInfo.StateMedia[""].File);
ImportedLayout screen = LayoutImporter.Build(
rootInfo,
_ => (0u, 0, 0),
null,
null,
new DatStringResolver(dats).Resolve);
var list = Assert.IsType<UiTemplateListBox>(screen.FindElement(
CharacterManagementUiController.ListElementId));
UiTemplateListEntry template = Assert.Single(list.Templates);
Assert.Equal(expectedLayoutDid, template.TemplateLayoutId);
Assert.Equal(0x100003A5u, template.TemplateElementId);
AssertButton(screen, CharacterManagementUiController.CreateElementId,
"Create Character");
AssertButton(screen, CharacterManagementUiController.EnterElementId,
"ENTER");
AssertButton(screen, CharacterManagementUiController.DeleteElementId,
"DELETE");
AssertButton(screen, CharacterManagementUiController.RestoreElementId,
"RESTORE");
Assert.DoesNotContain(
Descendants(screen.Root),
static element => element is UiViewport);
ElementInfo rowInfo = Assert.IsType<ElementInfo>(
LayoutImporter.ImportInfos(dats, layoutDid, template.TemplateElementId));
Assert.Equal(1u, rowInfo.Type);
Assert.Equal(160f, rowInfo.Width);
Assert.Equal(16f, rowInfo.Height);
Assert.Equal(0x40000009u, rowInfo.FontDid);
Assert.Equal(
[
UiButtonStateMachine.Normal,
UiButtonStateMachine.NormalRollover,
UiButtonStateMachine.NormalPressed,
UiButtonStateMachine.Highlight,
UiButtonStateMachine.HighlightRollover,
uint.MaxValue,
],
rowInfo.States.Keys.Order().ToArray());
uint dialogDid = RetailDataIdResolver.Resolve(dats, 2u, 5u);
Assert.Equal(0x2100003Cu, dialogDid);
ImportedLayout message = BuildSelected(dats, dialogDid, 0x24u);
Assert.IsType<UiDialogRoot>(message.Root);
Assert.IsType<UiText>(message.FindElement(0x3Eu));
Assert.IsType<UiButton>(message.FindElement(0x26u));
ImportedLayout delete = BuildSelected(dats, dialogDid, 0x2Cu);
Assert.IsType<UiDialogRoot>(delete.Root);
Assert.IsType<UiField>(delete.FindElement(0x2Cu));
Assert.IsType<UiButton>(delete.FindElement(0x2Eu));
Assert.IsType<UiButton>(delete.FindElement(0x2Fu));
var strings = new DatStringResolver(dats);
const uint table = 0x23000002u;
Assert.Equal("DELETE", Resolve(strings, table,
"ID_CharacterManagement_DeleteCharacterResponse"));
Assert.Equal("Please Wait", Resolve(strings, table,
"ID_CharacterManagement_PleaseWait"));
Assert.Equal("Entering World", Resolve(strings, table,
"ID_Character_EnteringWorld"));
string confirmation = Assert.IsType<string>(strings.ResolveTemplate(
table,
"ID_CharacterManagement_DeleteCharacterConfirmation",
new Dictionary<uint, string>
{
[DatStringResolver.PlayerVariable] = "Test Character",
}));
Assert.Contains("Test Character", confirmation);
Assert.Contains("'DELETE'", confirmation);
StringTable stringTable = Assert.IsType<StringTable>(dats.Get<StringTable>(table));
var deleteEntry = stringTable.Strings[
DatStringResolver.ComputeHash(
"ID_CharacterManagement_DeleteCharacterConfirmation")];
Assert.Equal([DatStringResolver.PlayerVariable], deleteEntry.Variables);
}
private static ImportedLayout BuildSelected(
IDatReaderWriter dats,
uint layoutDid,
uint rootId)
{
ElementInfo info = Assert.IsType<ElementInfo>(
LayoutImporter.ImportInfos(dats, layoutDid, rootId));
return LayoutImporter.Build(
info,
_ => (0u, 0, 0),
null,
null,
new DatStringResolver(dats).Resolve);
}
private static string Resolve(
DatStringResolver strings,
uint table,
string key) => Assert.IsType<string>(strings.Resolve(
table,
DatStringResolver.ComputeHash(key)));
private static void AssertButton(
ImportedLayout layout,
uint elementId,
string label) => Assert.Equal(
label,
Assert.IsType<UiButton>(layout.FindElement(elementId)).Label);
private static IEnumerable<UiElement> Descendants(UiElement root)
{
yield return root;
foreach (UiElement child in root.Children)
foreach (UiElement descendant in Descendants(child))
yield return descendant;
}
}
internal sealed class InstalledDatFactAttribute : FactAttribute
{
public InstalledDatFactAttribute()
{
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
{
Skip = "Set ACDREAM_PROBE_LIVE_MOUNT=1 to run the installed-DAT LA8 gate.";
return;
}
string datDirectory = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Documents",
"Asheron's Call");
if (!File.Exists(Path.Combine(datDirectory, "client_portal.dat")))
Skip = $"Installed client_portal.dat is required at '{datDirectory}'.";
}
}