Fix 1: replace 3 copy-paste meter blocks in VitalsTree_MetersHaveExpectedSliceIds
with a single table-driven loop — a 4th meter is now a one-liner and failures
name the failing meter id directly.
Fix 2: FixtureLoader now reads the fixture as bytes and strips the UTF-8 BOM
(EF BB BF) before passing the span to JsonSerializer, so a BOM-bearing fixture
file never causes a spurious JsonReaderException.
Fix 3: add [Trait("Category", "Conformance")] at the class level so conformance
tests are selectable by category filter.
Fix 4: add missing <param name="layoutId"> doc tag to LayoutImporter.ImportInfos.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
96 lines
3.9 KiB
C#
96 lines
3.9 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Golden conformance tests for the vitals LayoutDesc importer.
|
|
/// Uses the committed JSON fixture (<c>vitals_2100006C.json</c>) — no dats, no GL.
|
|
///
|
|
/// These tests lock the importer's tree-building (factory dispatch, meter slice
|
|
/// extraction, rects) against the real portal.dat values captured when the
|
|
/// fixture was generated. Any regression in <see cref="LayoutImporter"/>,
|
|
/// <see cref="DatWidgetFactory"/>, or <see cref="ElementReader"/> will surface here.
|
|
///
|
|
/// Sprite ids sourced from <c>docs/research/2026-06-15-layoutdesc-format.md §11</c>.
|
|
/// </summary>
|
|
[Trait("Category", "Conformance")]
|
|
public class LayoutConformanceTests
|
|
{
|
|
// ── Test 1: Three meters at expected rects ────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// The three vital bars must be UiMeters positioned at x=5, width=150, height=16,
|
|
/// at y=5 (health), y=21 (stamina), y=37 (mana).
|
|
/// </summary>
|
|
[Fact]
|
|
public void VitalsTree_HasThreeMetersAtExpectedRects()
|
|
{
|
|
var layout = FixtureLoader.LoadVitals();
|
|
|
|
(uint Id, float Y)[] expected =
|
|
[
|
|
(0x100000E6u, 5f), // health
|
|
(0x100000ECu, 21f), // stamina
|
|
(0x100000EEu, 37f), // mana
|
|
];
|
|
|
|
foreach (var (id, y) in expected)
|
|
{
|
|
var elem = layout.FindElement(id);
|
|
Assert.NotNull(elem);
|
|
var meter = Assert.IsType<UiMeter>(elem);
|
|
Assert.Equal(5f, meter.Left);
|
|
Assert.Equal(y, meter.Top);
|
|
Assert.Equal(150f, meter.Width);
|
|
Assert.Equal(16f, meter.Height);
|
|
}
|
|
}
|
|
|
|
// ── Test 2: All 18 slice ids ──────────────────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// The six back+front 3-slice sprite ids for each of the three meters must
|
|
/// match the values confirmed from the dat dump (format doc §11).
|
|
/// This proves the factory's grandchild slice extraction against committed data.
|
|
/// </summary>
|
|
[Fact]
|
|
public void VitalsTree_MetersHaveExpectedSliceIds()
|
|
{
|
|
var layout = FixtureLoader.LoadVitals();
|
|
|
|
// Columns: MeterId, then 6 slice ids in order:
|
|
// BackLeft, BackTile, BackRight, FrontLeft, FrontTile, FrontRight
|
|
(uint MeterId, uint[] Slices)[] cases =
|
|
[
|
|
(0x100000E6u, [0x0600747Eu, 0x0600747Fu, 0x06007480u, 0x06007481u, 0x06007482u, 0x06007483u]), // health
|
|
(0x100000ECu, [0x06007484u, 0x06007485u, 0x06007486u, 0x06007487u, 0x06007488u, 0x06007489u]), // stamina
|
|
(0x100000EEu, [0x0600748Au, 0x0600748Bu, 0x0600748Cu, 0x0600748Du, 0x0600748Eu, 0x0600748Fu]), // mana
|
|
];
|
|
|
|
foreach (var (meterId, s) in cases)
|
|
{
|
|
var m = Assert.IsType<UiMeter>(layout.FindElement(meterId));
|
|
Assert.Equal(s[0], m.BackLeft); Assert.Equal(s[1], m.BackTile); Assert.Equal(s[2], m.BackRight);
|
|
Assert.Equal(s[3], m.FrontLeft); Assert.Equal(s[4], m.FrontTile); Assert.Equal(s[5], m.FrontRight);
|
|
}
|
|
}
|
|
|
|
// ── Test 3: Chrome TL corner sprite ───────────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// The top-left chrome corner element (id <c>0x10000633</c>) must be a
|
|
/// <see cref="UiDatElement"/> whose active media file id is <c>0x060074C3</c>.
|
|
/// </summary>
|
|
[Fact]
|
|
public void VitalsTree_ChromeCornerHasExpectedSprite()
|
|
{
|
|
var layout = FixtureLoader.LoadVitals();
|
|
|
|
var elem = layout.FindElement(0x10000633u);
|
|
Assert.NotNull(elem);
|
|
var datElem = Assert.IsType<UiDatElement>(elem);
|
|
var (file, _) = datElem.ActiveMedia();
|
|
Assert.Equal(0x060074C3u, file);
|
|
}
|
|
}
|