acdream/tests/AcDream.App.Tests
Erik a7529a975a test(app): serialize the classes sharing camera/render process globals (#252)
A full Release App run failed once at Issue181WallPressEquilibriumTests
.Diagnostic_WallPressedCamera_EyeWanderAndViewerCellStability. It passed in
isolation and did not recur across five further whole-suite runs, and the diff
under test touched only the world texture-creation stack -- nothing in camera,
visibility or physics. A cross-class parallelism race was the only plausible
mechanism, not a regression.

Ten App test classes share three process-global mutable statics, and xUnit runs
distinct test classes in parallel by default:

  - CameraDiagnostics: AlignToSlope, CollideCamera, TranslationStiffness,
    RotationStiffness, UseRetailChaseCamera. These are not merely written, they
    are written AWAY from their defaults -- RetailChaseCameraTests sets
    AlignToSlope and CollideCamera to false, and three classes set
    UseRetailChaseCamera to false -- while RetailChaseCamera.Update,
    CameraController.Active, CameraFrameController, WorldRenderFrameBuilder and
    MouseLookController read them.
  - RenderingDiagnostics.ProbeFlapEnabled, written by CornerFloodReplayTests
    and Issue181WallPressEquilibriumTests.
  - System.Console.Out, redirected by those same two classes to capture probe
    output.

Every one of these classes already saved and restored in try/finally. That is
correct within a class and remains necessary, but it was never sufficient. A
finally bounds a mutation in TIME along its own thread; it cannot stop another
class from reading the static inside that window. Worse, two overlapping
save/restore pairs can interleave so the second restore writes back the FIRST
one's temporary value, leaving the global permanently wrong for the rest of the
run. The Console.Out case is the sharpest instance: an interleaved restore can
install a DISPOSED StringWriter as the process-wide Console.Out, which then
throws in unrelated tests. Serializing the sharers is what makes each class's
existing finally sufficient.

The fix is a marker CollectionDefinition applied to the ten sharing classes,
following the WorldEnvironmentControllerCollection precedent. No collection
fixture: several members are [Theory] cases that need different knob values per
case, so a fixture cannot own the save/restore without rewriting every member's
internals, and it would not help the read side at all. Because every member
references the same compile-time const for the collection name, the grouping
cannot silently drift via a typo.

Membership is deliberately narrow. It covers the eight writers plus two classes
that drive production code which READS a knob another member moves off its
default (HouseExitWalkReplayTests and CameraFrameControllerTests both run
RetailChaseCamera.Update and assert on the resulting eye). Classes that merely
construct a CameraController without a retail chase camera are NOT members --
their reads fall through the null branch and are insensitive.

No production code changed; no assertion was weakened, and no retry, sleep or
tolerance was added.

Verification. Base commit f6275f45 measured empirically at 3,763 passed / 3
skipped. Post-fix: 136 whole-suite Release runs. Every failure observed was in
the pre-existing zero-allocation family tracked as #250 (an Expected 0 / Actual
N bytes assertion), and none was in any collection member. A matched 55-run
baseline at f6275f45 reproduced that same family, confirming it predates this
change. Serialization cost is inside run-to-run noise: the suite is ~3 s of a
~4.5 s wall-clock dotnet test, and the ten serialized classes are a small
fraction of it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 18:46:43 +02:00
..
Audio fix(audio): keep unavailable world slots quiescent 2026-07-24 19:49:25 +02:00
Combat refactor(runtime): close simulation ownership 2026-07-26 15:53:31 +02:00
Composition feat(render): Campaign V slice V6m commit 1 - portal space draws on Vulkan 2026-07-28 18:25:49 +02:00
Diagnostics fix(diag): resolve the multisampled backbuffer before reading it 2026-07-28 00:54:59 +02:00
Input test(app): serialize the classes sharing camera/render process globals (#252) 2026-07-28 18:46:43 +02:00
Interaction fix(interaction): restore retail loot placement and world-drop projection 2026-07-27 00:03:15 +02:00
Net refactor(runtime): unify generation reset for direct hosts 2026-07-27 00:43:26 +02:00
Physics fix(interaction): restore retail loot placement and world-drop projection 2026-07-27 00:03:15 +02:00
Platform feat(linux): gate graphical backends and capabilities 2026-07-27 12:26:58 +02:00
Plugins feat(D.2b): IUiRegistry plugin UI surface + buffered drain into UiHost 2026-06-14 17:46:37 +02:00
Rendering test(app): serialize the classes sharing camera/render process globals (#252) 2026-07-28 18:46:43 +02:00
Runtime feat(headless): complete deterministic bot command parity 2026-07-27 08:23:36 +02:00
Settings fix(rendering): survive display topology handoff 2026-07-25 13:15:24 +02:00
Spells feat(headless): share immutable gameplay content 2026-07-27 09:00:48 +02:00
Streaming refactor(runtime): unify generation reset for direct hosts 2026-07-27 00:43:26 +02:00
Studio Revert "Campaign V slice V4a" - it lost world multisampling 2026-07-27 18:29:28 +02:00
UI Revert "Campaign V slice V4a" - it lost world multisampling 2026-07-27 18:29:28 +02:00
World fix(interaction): restore retail loot placement and world-drop projection 2026-07-27 00:03:15 +02:00
AcDream.App.Tests.csproj refactor(runtime): own canonical action state 2026-07-26 10:44:09 +02:00
BoundedTestDatCollection.cs perf(content): remove exception-driven setup probes 2026-07-24 15:12:21 +02:00
FramePhaseTestDoubles.cs refactor(runtime): move session lifetime and ordered transport 2026-07-25 19:39:24 +02:00
FrameProfilerReportTests.cs perf(diag): complete trustworthy Slice A capture tooling 2026-07-24 12:46:51 +02:00
FrameStatsBufferTests.cs fix(pipeline): MP0 - profiler toggle-path hygiene + Max() seed (review follow-up) 2026-07-05 19:26:28 +02:00
GameRuntimeTestFactory.cs refactor(runtime): cut graphical host over to canonical root 2026-07-26 19:06:09 +02:00
GlobalUsings.cs Revert "Campaign V slice V4a" - it lost world multisampling 2026-07-27 18:29:28 +02:00
LiveEntityRuntimeFixture.cs refactor(runtime): own projectile simulation 2026-07-26 14:17:42 +02:00
RuntimeDatAccessArchitectureTests.cs feat(headless): share immutable process content 2026-07-27 08:37:24 +02:00
RuntimeDatCollectionFactoryTests.cs feat(headless): share immutable process content 2026-07-27 08:37:24 +02:00
RuntimeOptionsRetailUiTests.cs test(runtime): add deterministic world gate artifacts 2026-07-20 22:37:16 +02:00
RuntimeOptionsTests.cs feat(render): Campaign V slice V5 - Vulkan bring-up, dark 2026-07-28 07:11:50 +02:00
RuntimeWorldTransitTestDriver.cs refactor(runtime): own teleport destination correlation 2026-07-26 17:52:34 +02:00