acdream/tests/AcDream.App.Tests/Streaming/WorldRevealCoordinatorTests.cs
Erik bb16f74fd4 perf(streaming): quiesce retired generations and budget teardown
Publish the retail blocking-for-cells edge before deferred recenter work, freeze old-world presentation/simulation/audio, and advance full-window retirement from exact metered entity and owner cursors. This removes synchronous portal teardown without allowing retained owners to remain observable.
2026-07-24 18:29:52 +02:00

160 lines
5.8 KiB
C#

using AcDream.App.Streaming;
using AcDream.Core.Selection;
namespace AcDream.App.Tests.Streaming;
public sealed class WorldRevealCoordinatorTests
{
private sealed class State
{
public bool RenderReady { get; set; }
public bool CollisionReady { get; set; }
public bool CompositesReady { get; set; }
public int InvalidateCount { get; private set; }
public int PrepareCount { get; private set; }
public WorldRevealCoordinator Build(List<string>? logs = null) => new(
isRenderNeighborhoodReady: (_, _) => RenderReady,
isSpawnCellReady: _ => CollisionReady,
isTerrainNeighborhoodReady: (_, _) => CollisionReady,
areCompositeTexturesReady: () => CompositesReady,
prepareCompositeTextures: (_, _) => PrepareCount++,
invalidateCompositeTextures: () =>
{
InvalidateCount++;
CompositesReady = false;
},
isSpawnClaimUnhydratable: _ => false,
log: logs is null ? null : new Action<string>(logs.Add));
}
[Fact]
public void Begin_InvalidatesPriorDestinationAndStartsOneGeneration()
{
var state = new State { CompositesReady = true };
WorldRevealCoordinator coordinator = state.Build();
long generation = coordinator.Begin(WorldRevealKind.Login);
Assert.Equal(1, generation);
Assert.Equal(1, state.InvalidateCount);
Assert.False(state.CompositesReady);
Assert.Equal(WorldRevealKind.Login, coordinator.Snapshot.Kind);
}
[Fact]
public void PrepareAndEvaluate_UsesOneCanonicalSnapshotForLifecycle()
{
const uint outdoorCell = 0x11340021u;
var state = new State
{
RenderReady = true,
CollisionReady = true,
};
WorldRevealCoordinator coordinator = state.Build();
coordinator.Begin(WorldRevealKind.Login);
WorldRevealReadinessSnapshot first = coordinator.PrepareAndEvaluate(outdoorCell);
state.CompositesReady = true;
WorldRevealReadinessSnapshot ready = coordinator.PrepareAndEvaluate(outdoorCell);
Assert.False(first.IsReady);
Assert.True(ready.IsReady);
Assert.Equal(2, state.PrepareCount);
Assert.Equal(ready, coordinator.Snapshot.Readiness);
}
[Fact]
public void PortalLifecycle_CountsMaterializationAndCompletesVisibleGeneration()
{
const uint outdoorCell = 0x3032001Cu;
var logs = new List<string>();
var state = new State
{
RenderReady = true,
CollisionReady = true,
CompositesReady = true,
};
WorldRevealCoordinator coordinator = state.Build(logs);
coordinator.Begin(WorldRevealKind.Portal);
state.CompositesReady = true;
Assert.True(coordinator.Evaluate(outdoorCell).IsReady);
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized();
coordinator.ObserveWorldViewportVisible();
coordinator.Complete();
Assert.Equal(1, coordinator.PortalMaterializationCount);
Assert.True(coordinator.Snapshot.Materialized);
Assert.True(coordinator.Snapshot.WorldViewportObserved);
Assert.True(coordinator.Snapshot.Completed);
Assert.Equal(0, coordinator.Snapshot.InvariantFailureCount);
Assert.Single(logs, line => line.Contains("event=materialized", StringComparison.Ordinal));
}
[Fact]
public void Cancel_PreventsLateDestinationWorkFromMutatingLifecycle()
{
var state = new State
{
RenderReady = true,
CollisionReady = true,
CompositesReady = true,
};
WorldRevealCoordinator coordinator = state.Build();
coordinator.Begin(WorldRevealKind.Portal);
coordinator.Cancel();
coordinator.PrepareAndEvaluate(0x11340021u);
coordinator.ObserveMaterialized();
coordinator.ObserveWorldViewportVisible();
coordinator.Complete();
Assert.True(coordinator.Snapshot.Cancelled);
Assert.Equal(default, coordinator.Snapshot.Readiness);
Assert.Equal(0, coordinator.PortalMaterializationCount);
}
[Fact]
public void RevealLifetime_QuiescesContinuouslyAndOnlyActiveGenerationReopens()
{
var availability = new WorldGenerationAvailabilityState();
var world = new GpuWorldState(availability: availability);
var audio = new RecordingAudioQuiescence();
var quiescence = new WorldGenerationQuiescence(
availability,
new SelectionState(),
world,
audio);
var coordinator = new WorldRevealCoordinator(
isRenderNeighborhoodReady: (_, _) => true,
isSpawnCellReady: _ => true,
isTerrainNeighborhoodReady: (_, _) => true,
areCompositeTexturesReady: () => true,
prepareCompositeTextures: (_, _) => { },
invalidateCompositeTextures: () => { },
isSpawnClaimUnhydratable: _ => false,
quiescence: quiescence);
Assert.Equal(1, coordinator.Begin(WorldRevealKind.Login));
Assert.Equal(2, coordinator.Begin(WorldRevealKind.Portal));
Assert.False(availability.IsWorldAvailable);
Assert.Equal(2, availability.QuiescedGeneration);
Assert.Equal(1, audio.SuspendCalls);
coordinator.Complete();
Assert.True(availability.IsWorldAvailable);
Assert.Equal(1, audio.ResumeCalls);
}
private sealed class RecordingAudioQuiescence : AcDream.App.Audio.IWorldAudioQuiescence
{
public int SuspendCalls { get; private set; }
public int ResumeCalls { get; private set; }
public void SuspendWorldAudio() => SuspendCalls++;
public void ResumeWorldAudio() => ResumeCalls++;
}
}