From 2566dad1d398a73e2ec0b4e895237a2a5abd937e Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 7 Sep 2026 01:38:56 +0200 Subject: [PATCH] =?UTF-8?q?test(vt):=20round=203=20item=208=20=E2=80=94=20?= =?UTF-8?q?nav=20byte-identity=20coverage=20+=20s-cell=20strip=20+=20.ast?= =?UTF-8?q?=20round-trip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The already-committed nav_briennecarlus.af/nav_empyrean.af/nav_lockandkeyjaw.af fixtures (checkpoint/recall/portal2 node coverage, clean metaf headers) were exercised by the model-equality proofs (parse, parse-write-parse, binary-import-matches-af) but never by the REAL byte-identical writer-output theory, which only ever covered nav_ab.af. Converted WriterOutputMatchesMetafCanonicalEmissionNavOnly into a Theory over all four nav-only fixtures. Added two more coverage gaps this round's audit named: - StringCellStripsEmbeddedNewlineAtConstructionNotJustOnWrite pins VtankCell.String's gy.cs:84 newline strip (construction-time, not just at WriteTo). - RealAstFixturesRoundTripByteIdentical extends the existing schema-only owner-{a,b,c}.ast coverage to a real parse -> Render() byte-identity round trip, the same proof-4 rigor MetafSerializerTests already applies to .af fixtures. No writer bug surfaced in the extended nav theory or the .ast round trip — both already passed; these are coverage-closing additions, not fixes. Verified each new test has teeth via a temporary synthetic mutation rather than a real revert (there is no production fix to revert here): the string-cell test was checked against a temporarily-unstripped VtankCell.String (failed, restored), and the .ast round-trip was checked against a temporarily LF-only VtankWriter.AppendLine (all three .ast cases failed, the four nav cases were unaffected since MetafSerializer's .af writer is a separate code path from VtankUsdDocument's "y" grammar writer — restored). Co-Authored-By: Claude Fable 5.1 --- .../MetafSerializerTests.cs | 28 +++++++++++++++---- .../VtankProfileDirectoryTests.cs | 22 +++++++++++++++ .../VtankUsdDocumentTests.cs | 21 ++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/tests/AcDream.Plugins.MossTank.Tests/MetafSerializerTests.cs b/tests/AcDream.Plugins.MossTank.Tests/MetafSerializerTests.cs index 8e31beb41..989f76ec9 100644 --- a/tests/AcDream.Plugins.MossTank.Tests/MetafSerializerTests.cs +++ b/tests/AcDream.Plugins.MossTank.Tests/MetafSerializerTests.cs @@ -234,12 +234,30 @@ public sealed class MetafSerializerTests // Proof (4), nav-only case: SaveNav's own header (navHeader) and // single-Nav fold-marker wrap are byte-identical to a real nav-only - // .af fixture. nav_ab.af's header is metaf's own fresh banner (first - // bytes "~~ {\n~~ ", matching every clean meta fixture above). - [Fact] - public void WriterOutputMatchesMetafCanonicalEmissionNavOnly() + // .af fixture. Every listed fixture carries metaf's own fresh banner + // (first bytes "~~ {\n~~ "); round 3 item 8 extended this from a + // single fixture (nav_ab.af) to the three checkpoint/recall/portal2 + // fixtures copied from the metas repo (nav_briennecarlus.af carries + // chk+jmp, nav_empyrean.af carries rcl+ptl, nav_lockandkeyjaw.af + // carries rcl+chk) so the theory actually exercises every node kind + // metaf's own grammar supports, not just plain point waypoints. + public static TheoryData NavByteIdenticalFixtureData() + { + var data = new TheoryData(); + foreach (string name in new[] + { + "nav_ab", "nav_briennecarlus", "nav_empyrean", "nav_lockandkeyjaw", + }) + { + data.Add(Path.Combine(FixturesRoot, "af", name + ".af")); + } + return data; + } + + [Theory] + [MemberData(nameof(NavByteIdenticalFixtureData))] + public void WriterOutputMatchesMetafCanonicalEmissionNavOnly(string path) { - string path = Path.Combine(FixturesRoot, "af", "nav_ab.af"); string original = File.ReadAllText(path); var settings = new NavigationSettings(); Assert.True( diff --git a/tests/AcDream.Plugins.MossTank.Tests/VtankProfileDirectoryTests.cs b/tests/AcDream.Plugins.MossTank.Tests/VtankProfileDirectoryTests.cs index 76862fabe..c2cac139d 100644 --- a/tests/AcDream.Plugins.MossTank.Tests/VtankProfileDirectoryTests.cs +++ b/tests/AcDream.Plugins.MossTank.Tests/VtankProfileDirectoryTests.cs @@ -324,6 +324,28 @@ public sealed class VtankProfileDirectoryTests spells!.ColumnNames); } + /// + /// Round 3 item 8: the shared "y" database grammar's writer must be + /// REAL byte-identical against real installed data, not just the + /// hand-built synthetic fixtures in VtankUsdDocumentTests — the three + /// real owner-*.ast per-character spell-tracking caches round-trip + /// through parse -> Render() unchanged. + /// + [Theory] + [InlineData("owner-a.ast")] + [InlineData("owner-b.ast")] + [InlineData("owner-c.ast")] + public void RealAstFixturesRoundTripByteIdentical(string fileName) + { + string original = File.ReadAllText( + Path.Combine(AppContext.BaseDirectory, "Fixtures", "vtank", fileName)); + + VtankDatabase database = VtankDatabase.Parse(original); + string rewritten = database.Render(); + + Assert.Equal(original, rewritten); + } + private sealed class MemoryStorage : IPluginStorage { private readonly Dictionary _text = new(StringComparer.Ordinal); diff --git a/tests/AcDream.Plugins.MossTank.Tests/VtankUsdDocumentTests.cs b/tests/AcDream.Plugins.MossTank.Tests/VtankUsdDocumentTests.cs index 90279bf58..191944690 100644 --- a/tests/AcDream.Plugins.MossTank.Tests/VtankUsdDocumentTests.cs +++ b/tests/AcDream.Plugins.MossTank.Tests/VtankUsdDocumentTests.cs @@ -110,4 +110,25 @@ public sealed class VtankUsdDocumentTests ["Apple", "Mango", "Zebra"], reparsed.Tables.Select(static entry => entry.Name).ToArray()); } + + /// + /// Round 3 item 8: pins VtankCell.String's embedded-newline strip + /// (gy.cs:84: text.Replace("\n", "") — VTank's own writer + /// never emits a multi-line "s" cell; only a ba blob can carry + /// embedded newlines, see ). + /// The strip happens at construction, not only at write time, so a + /// caller reading the cell back via + /// never sees the stripped character either. + /// + [Fact] + public void StringCellStripsEmbeddedNewlineAtConstructionNotJustOnWrite() + { + VtankCell cell = VtankCell.String("line1\nline2"); + + Assert.Equal("line1line2", cell.AsString()); + + var sb = new System.Text.StringBuilder(); + cell.WriteTo(sb); + Assert.Equal("s\r\nline1line2\r\n", sb.ToString()); + } }