diff --git a/src/AcDream.App/UI/Layout/LayoutImporter.cs b/src/AcDream.App/UI/Layout/LayoutImporter.cs index 0bf2b2bd..9f5d439b 100644 --- a/src/AcDream.App/UI/Layout/LayoutImporter.cs +++ b/src/AcDream.App/UI/Layout/LayoutImporter.cs @@ -129,6 +129,8 @@ public static class LayoutImporter /// ElementInfo tree (no widgets). Exposed for fixture generation + conformance tests. /// Returns null if the layout is missing. /// + /// The dat collection to read the LayoutDesc from. + /// The LayoutDesc dat id to read. public static ElementInfo? ImportInfos(DatCollection dats, uint layoutId) { var ld = dats.Get(layoutId); diff --git a/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs b/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs index de0bd06a..7f0f5eca 100644 --- a/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs +++ b/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs @@ -29,9 +29,14 @@ public static class FixtureLoader if (!File.Exists(fixturePath)) throw new FileNotFoundException($"Vitals fixture not found at: {fixturePath}"); - var json = File.ReadAllText(fixturePath, System.Text.Encoding.UTF8); - var root = JsonSerializer.Deserialize(json, _opts) - ?? throw new InvalidOperationException("Failed to deserialize vitals fixture."); + var bytes = File.ReadAllBytes(fixturePath); + // Strip UTF-8 BOM (EF BB BF) if present so JsonSerializer.Deserialize(ReadOnlySpan) + // does not reject the first byte. + ReadOnlySpan span = bytes; + if (span.Length >= 3 && span[0] == 0xEF && span[1] == 0xBB && span[2] == 0xBF) + span = span[3..]; + var root = JsonSerializer.Deserialize(span, _opts) + ?? throw new InvalidOperationException($"fixture deserialized to null: {fixturePath}"); return LayoutImporter.Build(root, _ => (0u, 0, 0), null); } diff --git a/tests/AcDream.App.Tests/UI/Layout/LayoutConformanceTests.cs b/tests/AcDream.App.Tests/UI/Layout/LayoutConformanceTests.cs index d1ec93e2..b50862bc 100644 --- a/tests/AcDream.App.Tests/UI/Layout/LayoutConformanceTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/LayoutConformanceTests.cs @@ -14,6 +14,7 @@ namespace AcDream.App.Tests.UI.Layout; /// /// Sprite ids sourced from docs/research/2026-06-15-layoutdesc-format.md §11. /// +[Trait("Category", "Conformance")] public class LayoutConformanceTests { // ── Test 1: Three meters at expected rects ──────────────────────────────── @@ -58,40 +59,20 @@ public class LayoutConformanceTests { var layout = FixtureLoader.LoadVitals(); - // Health bar - { - var elem = layout.FindElement(0x100000E6u); - var m = Assert.IsType(elem); - Assert.Equal(0x0600747Eu, m.BackLeft); - Assert.Equal(0x0600747Fu, m.BackTile); - Assert.Equal(0x06007480u, m.BackRight); - Assert.Equal(0x06007481u, m.FrontLeft); - Assert.Equal(0x06007482u, m.FrontTile); - Assert.Equal(0x06007483u, m.FrontRight); - } + // 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 + ]; - // Stamina bar + foreach (var (meterId, s) in cases) { - var elem = layout.FindElement(0x100000ECu); - var m = Assert.IsType(elem); - Assert.Equal(0x06007484u, m.BackLeft); - Assert.Equal(0x06007485u, m.BackTile); - Assert.Equal(0x06007486u, m.BackRight); - Assert.Equal(0x06007487u, m.FrontLeft); - Assert.Equal(0x06007488u, m.FrontTile); - Assert.Equal(0x06007489u, m.FrontRight); - } - - // Mana bar - { - var elem = layout.FindElement(0x100000EEu); - var m = Assert.IsType(elem); - Assert.Equal(0x0600748Au, m.BackLeft); - Assert.Equal(0x0600748Bu, m.BackTile); - Assert.Equal(0x0600748Cu, m.BackRight); - Assert.Equal(0x0600748Du, m.FrontLeft); - Assert.Equal(0x0600748Eu, m.FrontTile); - Assert.Equal(0x0600748Fu, m.FrontRight); + var m = Assert.IsType(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); } }