acdream/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
Erik 02a8288172 feat(render): S3 chunk 1 — print-only walk transcript, OH fixtures, offline signature diff
Campaign OVERHAUL S3 chunk 1 (docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md
§11): the transcript-kinds/fixtures/print-only-emitter half of the walk work,
built AFTER chunk 3 landed LC/SC (the per-land-cell interleave).

B1 — the emitter (print-only, never gates admission/depth/order):
- ACDREAM_DUMP_WALK_TRANSCRIPT=1 is read once into RuntimeOptions.DumpWalkTranscript
  (rule 4) and handed to RenderingDiagnostics.DumpWalkTranscriptEnabled (rule 5,
  a settable static, not a second env read) once at GameWindow construction — the
  deep walk call sites have no reachable RuntimeOptions reference.
- WalkTranscriptDump (new) prints the OH line kinds — F/P/LS/LC/SC/BLD/DI/DC/EC/OC —
  to Console at the exact points retail's cdb breakpoints sit
  (tools/walk-oracle/oh/oh-capture-walk.cdb.template), gated internally so every
  method bails out before any string work when the flag is off.
- Every call site lives in WalkFrameDriver.cs, at the point the driver already
  processes that turn: Collect (F/P, after BeginFrame), Emit's DI/LS/DC/BLD cases,
  OnLandCellTurn/OnLandscapeCellTurn (LC/SC, at LOD resolution via the new
  WalkTranscriptDump.LodCellId helper, before the 8x8-bucket expansion), and the
  EmitFloodTurns/EmitCellContentsTurn loops (EC/OC — both UNCONDITIONAL per flood
  visit, matching the OH captures' always-equal EC/OC counts; retail's own
  DrawEnvCell stamp dedupe sits past the breakpoint, inside the function).
- DC's "pv=" field encodes interior(0)/outdoor(1) as an 8-hex-digit 0/1 so it
  satisfies the same pv=[0-9a-f]{8} regex real captures use; derived from
  _currentDcStage at the DC event (CellStatic = interior pview, else outdoor).
- The frame-root pose (origin, quaternion) is a reasonable orthonormal basis built
  from the walk's own CyPlane.Normal forward vector and WalkLandscape's own
  ViewerWorldOriginX/Y block origin — self-consistent for the round-trip parser,
  not a byte-exact reproduction of retail's Frame (B4's diff never compares P).

B2 — WalkOracleTrace learns EC/OC event kinds (LC/SC already existed from chunk 3).
New WalkOraclePartsTrace.cs holds two small read-only parsers for the parts log
(PD/DM) and the alpha-depth log (AM/FL/PM/PC) — records only, no validator, no
canonical JSONL, no new tool.

B3 — fixtures: the five OH walk captures join WalkOracleTraceTests.AllFixtures
(now (root, name) pairs — FW0's own root plus the OH capture directory) for
parse + complete-frame pins. The four kit poses join WalkTraceConformanceTests'
still-fixture coverage as NEW rows (the OH cathedral-arrival root is f4180108,
not FW0's f4180106): terrace-edge/cathedral-arrival extend the existing theory
(now (root, fixture) parameterized); holtburg-doorway-still and foundry-deep get
dedicated tests mirroring their FW0 siblings' own structure. Finding: the OH
foundry-deep capture's own retail transcript draws 12 real town buildings through
its exit chain (unlike the FW0 capture, which apparently reached none at that
pose) — the FW0 test's stub 1x1 landscape undershoots it (first divergence:
nothing after "LS" vs retail's real BLD content); fixed by building the full
landscape/building world via WalkLandscapeDatBuilder.Build, matching the shared
theory's own approach, not by skipping or weakening the row.
WalkTraceReplayContext.Signature(WalkOracleFrame) now filters to the DI/DC/BLD/LS
kinds (LC/SC/EC/OC never had a WalkEvent analogue in RetailFrameWalk's own
four-kind vocabulary) instead of mapping them to a "?" placeholder, so the still-
fixture comparison stays apples-to-apples on both sides.

B4 — WalkTranscriptSignatureDiff (test-side only, no runner/tool): diffs two
transcripts (raw lines or parsed frames) at the full DI/DC/BLD/LS/LC/SC/EC/OC
level, reporting the first divergent event and position per frame. Proven over a
synthetic self-vs-self-minus-one-event pair (SignatureDiff_ReportsTheExactRemovedEvent).

Tests: T1 (flag off) is split into a unit-level zero-allocation/zero-output check
on WalkTranscriptDump itself (the walk's pre-existing allocation, e.g.
RetailFrameWalk.EmitDrawCells's per-call array, is untouched by this chunk and not
independently zero-alloc) and an integration-level Collect() check; both assert via
WalkOracleTrace.Parse returning zero frames rather than raw string equality, which
is robust to unrelated Console.WriteLine noise from other test classes running in
parallel (a real, observed hazard — WalkFrameDriverTests joins
CameraDiagnosticsCollection for the same reason CornerFloodReplayTests/
Issue181WallPressEquilibriumTests already do, issue #251). T2 proves the
emitter/parser round trip on a synthetic interior frame. T3's InstalledDat rows all
pass. T4: LaunchOptionsDocumentationTests green with the ACDREAM_DUMP_WALK_TRANSCRIPT
row (both directions).

Gates: hermetic lane 6,814/0 (was 6,795 baseline + new tests), three consecutive
clean runs; InstalledDat lane 245/3 known-failures (the two pre-existing #383
layout tests + TowerAscent) unchanged from baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 11:49:42 +02:00

331 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AcDream.App.Tests.Rendering;
using AcDream.App.Rendering.Walk;
using DatReaderWriter;
using DatReaderWriter.Options;
namespace AcDream.App.Tests.Rendering.Walk;
/// <summary>
/// FW1's conformance gate, first slice: replay pose-stamped oracle
/// fixtures through the ported walk and require the identical event
/// sequence. The projection-light interior fixtures gate first
/// (docs/research/2026-08-30-fw-walk-oracle/README.md, posed round);
/// the outdoor/landscape fixtures join as the landscape world build
/// lands.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class WalkTraceConformanceTests
{
private sealed class Recorder : IWalkEventSink
{
public readonly List<WalkEvent> Events = new();
public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
}
private static DatCollection OpenDats()
{
string? datDir = CornerFloodReplayTests.ResolveDatDir();
if (datDir is null)
{
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
}
return new DatCollection(datDir!, DatAccessType.Read);
}
[Fact]
public void Street_outdoor_first_frame_diff()
{
// The first landscape-involving conformance case: diff-first (the
// assert prints both signatures on mismatch for adjudication).
IReadOnlyList<WalkOracleFrame> frames =
WalkOracleTrace.Load("posed/holtburg-street-outdoor");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing)
Assert.NotNull(frame.Pose);
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
};
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, null, world.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
}
[Fact]
public void Doorway_still_first_frame_diff()
{
// Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape
// through two exit views — tests the flood depth in isolation from
// the building look-in machinery.
IReadOnlyList<WalkOracleFrame> frames =
WalkOracleTrace.Load("posed/holtburg-doorway-still");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
};
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
// Render::deg_mul is DYNAMIC (auto-tuned by frame load), and this
// capture ran right after the heavy terrace-edge capture with the
// multiplier depressed: at mul ≤ 0 the degrade thresholds sit at or
// below each level's ideal, so 001e (eff 29.3), 0026 (27.1), and
// 002f (43.4) all select the portless level 1 — retail's zero
// look-in floods. Every other fixture pins ≈ +0.99 (thresholds at
// max). The recon session's live dump read 0.99 under the same cdb
// load. Re-dump deg_mul per capture at the next retail session.
var walk = new RetailFrameWalk { DegradeMultiplier = 0f };
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
}
[Fact]
public void Oh_doorway_still_first_frame_diff()
{
// S3 chunk 1 (§11.2 B3): the OH kit pose's own doorway-still capture
// — DI f4180108... no, a9b4013f, DC(ov=2, n=3) per §6b. Mirrors
// Doorway_still_first_frame_diff's own structure (this pose needs
// the interior camera cell, unlike the plain outdoor/theory rows).
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
"docs/research/2026-09-01-overhaul/oh-capture", "holtburg-doorway-still.walk");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
};
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
}
private const string FwOracleRoot = "docs/research/2026-08-30-fw-walk-oracle";
/// <summary>S3 chunk 1 (§11.2 B3): the OH capture directory — its OWN
/// pose-stamped kit-pose captures, a DIFFERENT root than the FW0 still
/// fixtures above.</summary>
private const string OhCaptureRoot = "docs/research/2026-09-01-overhaul/oh-capture";
[Theory]
[InlineData(FwOracleRoot, "posed/terrace-center")]
[InlineData(FwOracleRoot, "posed/terrace-edge")]
[InlineData(FwOracleRoot, "posed/cathedral-arrival")]
// S3 chunk 1 (§11.2 B3): the OH kit poses, as NEW rows — the OH
// cathedral-arrival root is f4180108, NOT FW0's f4180106 (a new pose,
// not a replacement of the FW0 row above).
[InlineData(OhCaptureRoot, "terrace-edge.walk")]
[InlineData(OhCaptureRoot, "cathedral-arrival.walk")]
public void Still_fixture_first_frame_reproduces_exactly(string root, string fixture)
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(root, fixture);
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
WalkOracleFrame frame = frames[1];
Assert.NotNull(frame.Pose);
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
};
WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
? Assert.Contains(frame.Pose.CellId, world.Cells)
: null;
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
string expected = WalkTraceReplayContext.Signature(frame);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
Assert.True(
expected == actual,
$"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
}
[Fact]
public void Foundry_entry_reproduces_every_frame_before_the_f67_order_segment()
{
// F67F79 (the fixture's terminal standing segment) diverge in ONE
// aspect: the flood SET through building a9b40036 is exact, but
// retail orders its DCs 116,118,11d where the replay orders
// 11d,116,118. The microscope isolates the flip to the ROOT plane
// of 0036's drawing BSP (N=(0,0,1) D=2.8): the replay's
// building-local eye z is 2.33 (d=+0.47 → NEG-subtree first), while
// retail behaves as d<0 (POS-first) — a structural ~0.5 m frame
// question (positionPush(2) / part-scale semantics), only
// adjudicable by a live cdb dump of FrameCurrent->viewer.viewpoint
// during 0036's build_draw_portals_only (script:
// tools/walk-oracle/fw1-f67-viewpoint-probe.cdb). Frames 166 must
// reproduce exactly.
MovingFixtureReplay("posed/foundry-entry", stopBeforeFrame: 67);
}
[Theory]
[InlineData("posed/holtburg-walkout")]
[InlineData("posed/holtburg-transitions")]
[InlineData("posed/holtburg-walkabout")]
public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
=> MovingFixtureReplay(fixture);
private void MovingFixtureReplay(string fixture, int stopBeforeFrame = int.MaxValue)
{
// Marker timing: the pose stamped at frame N+1 is the camera state
// frame N drew with (fixture README) — pair events(N) with
// pose(N+1). One walk + one landscape carry state across frames,
// reproducing retail's cross-frame caches (the stale cell-order
// quirk included).
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
Assert.True(frames.Count >= 3);
using DatCollection dats = OpenDats();
WalkOraclePose anchor = frames[1].Pose!;
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin);
var walk = new RetailFrameWalk();
for (int n = 1; n < frames.Count - 1; n++)
{
WalkOracleFrame frame = frames[n];
if (frame.Number >= stopBeforeFrame) break;
string expected = WalkTraceReplayContext.Signature(frame);
// The marker dumps the PREVIOUS frame's camera, so frame N's
// true camera state lies between pose(N) and pose(N+1) — a
// capture artifact, not a port ambiguity. A frame passes when
// the replay matches under either adjacent pose.
string? firstActual = null;
bool matched = false;
foreach (WalkOraclePose pose in new[] { frames[n + 1].Pose!, frame.Pose! })
{
Assert.NotNull(pose);
WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin);
var ctx = new WalkTraceReplayContext(pose, world.Cells)
{
Buildings = world.Buildings,
};
WalkCell? camera = null;
if ((pose.CellId & 0xFFFFu) >= 0x100)
{
Assert.True(
world.Cells.TryGetValue(pose.CellId, out camera),
$"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded");
}
var recorder = new Recorder();
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder);
string actual = WalkTraceReplayContext.Signature(recorder.Events);
firstActual ??= actual;
if (actual == expected)
{
matched = true;
break;
}
}
Assert.True(
matched,
$"frame {frame.Number} diverged under both adjacent poses ({fixture})\n"
+ $"EXPECTED: {expected}\nACTUAL: {firstActual}");
}
}
[Fact]
public void Foundry_deep_reproduces_every_complete_frame_exactly()
{
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/foundry-deep");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
Dictionary<uint, WalkCell> cells =
WalkWorldDatAdapter.BuildInteriorCells(dats, 0xA9B40000u);
var landscape = new WalkLandscape { MidWidth = 1, Blocks = new WalkLandBlock?[1] };
foreach (WalkOracleFrame frame in frames)
{
Assert.NotNull(frame.Pose);
WalkCell camera = Assert.Contains(frame.Pose!.CellId, cells);
var ctx = new WalkTraceReplayContext(frame.Pose, cells);
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
Assert.Equal(
WalkTraceReplayContext.Signature(frame),
WalkTraceReplayContext.Signature(recorder.Events));
}
}
[Fact]
public void Oh_foundry_deep_reproduces_every_complete_frame_exactly()
{
// S3 chunk 1 (§11.2 B3): the OH kit pose's own foundry-deep capture
// — DI a9b40176, DC(ov=1, n=2), 12 town buildings drawn through the
// surviving exit chain (§6b). UNLIKE the FW0 sibling above, this
// capture's own retail transcript shows real BLD content at that
// depth, so the stub 1x1 landscape (which has no blocks/buildings to
// walk at all) undershoots it — first divergence, run without the
// fix below: "DI:a9b40176|DC:ov=1:a9b40176,a9b40177|LS" (nothing
// after LS) vs retail's "…|LS|BLD:a9b40031|BLD:a9b…" (12 real
// buildings). The full landscape/building assembler
// (WalkLandscapeDatBuilder.Build — the SAME one the shared theory
// and Oh_doorway_still_first_frame_diff use) reproduces them; the
// camera is stationary across this still pose, so one build serves
// every frame in the loop, matching the FW0 sibling's "build once"
// shape.
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
"docs/research/2026-09-01-overhaul/oh-capture", "foundry-deep.walk");
Assert.NotEmpty(frames);
using DatCollection dats = OpenDats();
Assert.NotNull(frames[0].Pose);
WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(
dats, frames[0].Pose!.CellId, frames[0].Pose!.Origin);
foreach (WalkOracleFrame frame in frames)
{
Assert.NotNull(frame.Pose);
WalkCell camera = Assert.Contains(frame.Pose!.CellId, world.Cells);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
};
var walk = new RetailFrameWalk();
var recorder = new Recorder();
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
Assert.Equal(
WalkTraceReplayContext.Signature(frame),
WalkTraceReplayContext.Signature(recorder.Events));
}
}
}