acdream/tests/AcDream.App.Tests/Rendering/GameWindowSlice8BoundaryTests.cs

589 lines
26 KiB
C#

namespace AcDream.App.Tests.Rendering;
/// <summary>
/// Temporary source-shape freeze for the Slice 8 cutover. Each later checkpoint
/// replaces the corresponding assertion with functional tests on the extracted
/// owner; this file prevents a mechanical move from silently reordering host
/// behavior before that owner exists.
/// </summary>
public sealed class GameWindowSlice8BoundaryTests
{
[Fact]
public void Run_PreservesNativeAttributeAndCallbackOrder()
{
string body = MethodBody("public void Run()", "private void OnLoad()");
AssertAppearsInOrder(
body,
"RuntimeSettingsSnapshot startup = _runtimeSettings.Startup",
"_displayFramePacing.InitializeStartup(startup.Display.VSync)",
"VSync = startupPacing.UseVSync",
"Samples = startup.Quality.MsaaSamples",
"Window.Create(options)",
"_displayFramePacing.BindSurface(",
"_windowCallbacks = SilkWindowCallbackBinding.Create(",
"new WindowCallbackTargets(",
"OnLoad,",
"OnUpdate,",
"OnRender,",
"OnClosing,",
"OnFocusChanged,",
"OnFramebufferResize),",
"_displayFramePacing,",
"_hostQuiescence);",
"_windowCallbacks.Attach();",
"_window.Run();");
Assert.Equal(1, CountOccurrences(body, "SilkWindowCallbackBinding.Create("));
Assert.Equal(1, CountOccurrences(body, "_windowCallbacks.Attach();"));
Assert.DoesNotContain("_window.Load +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Update +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Render +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Closing +=", body, StringComparison.Ordinal);
}
[Fact]
public void SessionStart_FollowsFrameGraphPublication()
{
string body = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
AssertAppearsInOrder(
body,
"GameWindowPlatformResult<GL, IInputContext> platform = AcquirePlatform();",
"new HostInputCameraCompositionPhase(",
"this).Compose(platform);",
"new ContentEffectsAudioCompositionPhase(",
"this).Compose(platform, hostInputCamera);",
"new SettingsDevToolsCompositionPhase(",
"this).Compose(platform, hostInputCamera, contentEffectsAudio);",
"InteractionRetainedUiResult interactionUi =",
"new InteractionRetainedUiCompositionPhase(",
"this).Compose(",
"LivePresentationResult livePresentation =",
"new LivePresentationCompositionPhase(",
"this).Compose(",
"var renderFrameOrchestrator =",
"var updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(",
"_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);",
"_liveSessionHost = CreateLiveSessionHost();",
"_liveCombatModeCommands.Bind(combatCommand);",
"_runtimeDiagnosticCommands.Bind(runtimeDiagnostics);",
"_gameplayInputActions = AcDream.App.Input.GameplayInputActionRouter.Create(",
"_gameplayInputActions.Attach();",
"_liveSessionHost.Start(_options)");
Assert.Equal(1, CountOccurrences(body, "_liveSessionHost.Start(_options)"));
string phaseOne = Slice(
File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"HostInputCameraComposition.cs")),
"private HostInputCameraResult ComposeCore(",
"private void Fault(");
Assert.Contains("if (firstKeyboard is not null)", phaseOne, StringComparison.Ordinal);
Assert.Contains("if (firstMouse is not null)", phaseOne, StringComparison.Ordinal);
Assert.Contains(
"if (keyboard is not null && mouse is not null)",
phaseOne,
StringComparison.Ordinal);
Assert.DoesNotContain(
"if (firstKeyboard is not null && firstMouse is not null)",
phaseOne,
StringComparison.Ordinal);
int start = body.IndexOf("_liveSessionHost.Start(_options)", StringComparison.Ordinal);
string postStart = body[start..];
Assert.Contains("switch (liveStart.Status)", postStart, StringComparison.Ordinal);
Assert.Empty(System.Text.RegularExpressions.Regex.Matches(
postStart,
@"(?m)^\s+_[A-Za-z]\w*\s*(?:\?\?=|=)"));
Assert.DoesNotContain("new ", postStart, StringComparison.Ordinal);
Assert.DoesNotContain(".Mount(", postStart, StringComparison.Ordinal);
Assert.DoesNotContain(".Bind(", postStart, StringComparison.Ordinal);
Assert.DoesNotContain(".Attach(", postStart, StringComparison.Ordinal);
Assert.DoesNotContain(".Compose(", postStart, StringComparison.Ordinal);
Assert.DoesNotContain("PrepareResources(", postStart, StringComparison.Ordinal);
}
[Fact]
public void WorldEnvironment_IsOwnedAndGameWindowOnlyComposesItsTypedEdges()
{
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string sessionFactory = MethodBody(
"private AcDream.App.Net.LiveSessionEventRouter CreateLiveSessionEventRouter(",
"private AcDream.App.Net.LiveInventorySessionBindings");
string worldPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"WorldRenderComposition.cs"));
Assert.Contains(
"private readonly AcDream.App.World.WorldEnvironmentController _worldEnvironment =",
source,
StringComparison.Ordinal);
Assert.Contains(
"new WorldRenderDependencies(\n _worldEnvironment,",
load.Replace("\r\n", "\n", StringComparison.Ordinal),
StringComparison.Ordinal);
string worldCompose = Slice(
worldPhase,
"public WorldRenderResult Compose(",
"private (BitmapFont? Font, TextRenderer? Text) ComposeOptionalHudResources(");
AssertAppearsInOrder(
worldCompose,
"WorldRegionData region = _factory.LoadRegion(content.Dats);",
"_factory.InitializeEnvironment(_dependencies.Environment, region.Region);");
Assert.Contains("environment.Initialize(region);", worldPhase, StringComparison.Ordinal);
AssertAppearsInOrder(
sessionFactory,
"new AcDream.App.Net.LiveEnvironmentSessionSink(",
"_worldEnvironment.ApplyAdminEnvirons,",
"_worldEnvironment.SynchronizeFromServer)");
Assert.DoesNotContain("_loadedSkyDesc", source, StringComparison.Ordinal);
Assert.DoesNotContain("_loadedSkyDayIndex", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void RefreshSkyForCurrentDay()", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void OnEnvironChanged(", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void CycleTimeOfDay()", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void CycleWeather()", source, StringComparison.Ordinal);
string settingsPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SettingsDevToolsComposition.cs"));
AssertAppearsInOrder(
settingsPhase,
"debugVm.CycleTimeOfDay = _dependencies.DiagnosticCommands.CycleTimeOfDay;",
"debugVm.CycleWeather = _dependencies.DiagnosticCommands.CycleWeather;",
"debugVm.ToggleCollisionWires =");
AssertAppearsInOrder(
load,
"new AcDream.App.Diagnostics.RuntimeDiagnosticCommandController(",
"_runtimeDiagnosticCommands.Bind(runtimeDiagnostics);");
}
[Fact]
public void InputAction_IsOneTypedOwnerHandoff()
{
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string shutdown = MethodBody(
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
Assert.DoesNotContain("private void OnInputAction(", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void SetInputCombatScope(", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void ToggleLiveCombatMode()", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void OnUiDragReleasedOutside(", source, StringComparison.Ordinal);
Assert.DoesNotContain("DragReleasedOutsideUi +=", source, StringComparison.Ordinal);
Assert.Equal(1, CountOccurrences(
load,
"AcDream.App.Input.GameplayInputActionRouter.Create("));
Assert.Equal(1, CountOccurrences(load, "_gameplayInputActions.Attach();"));
Assert.Equal(1, CountOccurrences(
LivePresentationSource(),
"RetainedUiGameplayBinding.Create("));
Assert.Equal(1, CountOccurrences(
LivePresentationSource(),
"retainedGameplayLease.Resource.Attach();"));
AssertAppearsInOrder(
shutdown,
"_liveCombatModeCommands.Deactivate",
"_runtimeDiagnosticCommands.Deactivate",
"_retainedUiGameplayBinding?.Deactivate()",
"_gameplayInputActions?.Deactivate()",
"new ResourceShutdownStage(\"session lifetime\"",
"binding.Dispose();",
"_retainedUiGameplayBinding = null;",
"actions.Dispose();",
"_gameplayInputActions = null;");
}
[Fact]
public void FramebufferResize_IsOneTypedOwnerHandoff()
{
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string body = MethodBody(
"private void OnFramebufferResize(",
"private (uint Claim, bool Unhydratable)? _spawnClaimRangeMemo;");
Assert.Contains("=> _framebufferResize.Resize(newSize);", body, StringComparison.Ordinal);
Assert.DoesNotContain("Viewport(", body, StringComparison.Ordinal);
Assert.DoesNotContain("SetAspect(", body, StringComparison.Ordinal);
Assert.DoesNotContain("ResetLayout(", body, StringComparison.Ordinal);
string phaseOne = Slice(
File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"HostInputCameraComposition.cs")),
"private HostInputCameraResult ComposeCore(",
"private void Fault(");
AssertAppearsInOrder(
phaseOne,
"_dependencies.FramebufferResize.BindViewport(",
"_publication.PublishCameraController(camera)",
"_dependencies.FramebufferResize.BindCamera(",
"_dependencies.FramebufferResize.Resize(");
Assert.DoesNotContain(
"_viewportAspect.Update(_window",
load,
StringComparison.Ordinal);
}
[Fact]
public void RuntimeSettings_IsOneTwoPhaseOwnerWithoutWindowStateMirrors()
{
string source = GameWindowSource();
string run = MethodBody("public void Run()", "private void OnLoad()");
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string shutdown = MethodBody(
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
Assert.Contains(
"private readonly RuntimeSettingsController _runtimeSettings;",
source,
StringComparison.Ordinal);
Assert.Contains(
"_runtimeSettings = new RuntimeSettingsController(",
source,
StringComparison.Ordinal);
Assert.DoesNotContain("new SettingsStore(", source, StringComparison.Ordinal);
Assert.DoesNotContain("new AcDream.UI.Abstractions.Panels.Settings.SettingsStore(", source, StringComparison.Ordinal);
Assert.DoesNotContain(".LoadDisplay()", source, StringComparison.Ordinal);
Assert.DoesNotContain(".LoadAudio()", source, StringComparison.Ordinal);
Assert.DoesNotContain(".LoadGameplay()", source, StringComparison.Ordinal);
Assert.DoesNotContain(".LoadChat()", source, StringComparison.Ordinal);
Assert.DoesNotContain(".LoadCharacter(", source, StringComparison.Ordinal);
Assert.DoesNotContain("_persistedDisplay", source, StringComparison.Ordinal);
Assert.DoesNotContain("_persistedAudio", source, StringComparison.Ordinal);
Assert.DoesNotContain("_persistedGameplay", source, StringComparison.Ordinal);
Assert.DoesNotContain("_persistedChat", source, StringComparison.Ordinal);
Assert.DoesNotContain("_persistedCharacter", source, StringComparison.Ordinal);
Assert.DoesNotContain("_activeToonKey", source, StringComparison.Ordinal);
Assert.DoesNotContain("_settingsStore", source, StringComparison.Ordinal);
Assert.DoesNotContain("_settingsVm", source, StringComparison.Ordinal);
Assert.DoesNotContain("LoadAndApplyPersistedSettings", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void ApplyDisplayWindowState", source, StringComparison.Ordinal);
Assert.DoesNotContain("public void ReapplyQualityPreset", source, StringComparison.Ordinal);
AssertAppearsInOrder(
run,
"RuntimeSettingsSnapshot startup = _runtimeSettings.Startup",
"_displayFramePacing.InitializeStartup(startup.Display.VSync)",
"Samples = startup.Quality.MsaaSamples",
"Window.Create(options)");
AssertAppearsInOrder(
load,
"new SettingsDevToolsCompositionPhase(",
"this).Compose(platform, hostInputCamera, contentEffectsAudio);",
"new WorldRenderCompositionPhase(",
"_runtimeSettings.BindRuntimeTargets(",
"_liveSessionHost.Start(_options)");
string settingsPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SettingsDevToolsComposition.cs"));
AssertAppearsInOrder(
settingsPhase,
"_dependencies.Settings.ApplyStartup(_dependencies.StartupTarget);",
"_dependencies.DevTools is { } optional");
string worldPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"WorldRenderComposition.cs"));
AssertAppearsInOrder(
worldPhase,
"TerrainAtlas.Build(gl, dats, bindless)",
"settings.ResolvedQuality.AnisotropicLevel",
"_factory.CreateTerrain(");
AssertAppearsInOrder(
shutdown,
"_runtimeSettings.UnbindViewModel()",
"_runtimeSettings.UnbindRuntimeTargets",
"_retailUiLease.Dispose()",
"_streamer?.Dispose()",
"_wbDrawDispatcher?.Dispose()",
"_terrain?.Dispose()");
}
[Fact]
public void UpdateRenderFocusAndCloseRemainNarrowHostEdges()
{
string source = GameWindowSource();
string update = Slice(
source,
"private void OnUpdate(double dt)",
"private void OnRender(double deltaSeconds)");
string render = Slice(
source,
"private void OnRender(double deltaSeconds)",
"private AcDream.App.Diagnostics.WorldLifecycleResourceSnapshot");
string focus = Slice(
source,
"private void OnFocusChanged(bool focused)",
"public void Dispose()");
string close = Slice(
source,
"private void OnClosing()",
"private ResourceShutdownTransaction CreateShutdownTransaction()");
Assert.Equal(1, CountOccurrences(update, "_frameGraphs.Tick("));
Assert.Equal(1, CountOccurrences(render, "_frameGraphs.Render("));
AssertAppearsInOrder(
render,
"Vector2D<int> size = _window!.Size;",
"_frameGraphs.Render(",
"new AcDream.App.Rendering.RenderFrameInput(");
Assert.DoesNotContain("FramebufferSize", render, StringComparison.Ordinal);
Assert.Contains(
"=> _cameraPointerInput?.HandleFocusChanged(focused);",
focus,
StringComparison.Ordinal);
AssertAppearsInOrder(
close,
"_hostQuiescence.StopAccepting();",
"CompleteShutdown();",
"_shutdown.CompleteOrThrow();");
}
[Fact]
public void Shutdown_PreservesDependencyStagesAndNativeWindowLast()
{
string source = GameWindowSource();
string manifest = Slice(
source,
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
int disposeStart = source.IndexOf("public void Dispose()", StringComparison.Ordinal);
Assert.True(disposeStart >= 0);
string dispose = source[disposeStart..];
string[] stages =
[
"new ResourceShutdownStage(\"input callback deactivation\"",
"new ResourceShutdownStage(\"session lifetime\"",
"new ResourceShutdownStage(\"input callback detach\"",
"new ResourceShutdownStage(\"frame borrowers\"",
"new ResourceShutdownStage(\"session dependents\"",
"new ResourceShutdownStage(\"live entities\"",
"new ResourceShutdownStage(\"effect dispatch edges\"",
"new ResourceShutdownStage(\"live entity dependents\"",
"new ResourceShutdownStage(\"submitted GPU work\"",
"new ResourceShutdownStage(\"render frontends\"",
"new ResourceShutdownStage(\"shared texture owners\"",
"new ResourceShutdownStage(\"mesh adapter\"",
"new ResourceShutdownStage(\"remaining render owners\"",
"new ResourceShutdownStage(\"dedicated render resources\"",
"new ResourceShutdownStage(\"failed render construction cleanup\"",
"new ResourceShutdownStage(\"frame flight owner\"",
"new ResourceShutdownStage(\"content mappings\"",
"new ResourceShutdownStage(\"input\"",
"new ResourceShutdownStage(\"OpenGL context\"",
];
AssertAppearsInOrder(manifest, stages);
Assert.Equal(stages.Length, CountOccurrences(manifest, "new ResourceShutdownStage("));
foreach (string stage in stages)
Assert.Equal(1, CountOccurrences(manifest, stage));
AssertAppearsInOrder(
manifest,
"binding.Dispose();",
"if (!binding.IsDisposalComplete)",
"_windowCallbacks = null;");
AssertAppearsInOrder(
manifest,
"new(\"combat command slot\", _liveCombatModeCommands.Deactivate)",
"new(\"diagnostic command slot\", _runtimeDiagnosticCommands.Deactivate)",
"new(\"retained gameplay\", () => _retainedUiGameplayBinding?.Deactivate())",
"new(\"gameplay actions\", () => _gameplayInputActions?.Deactivate())",
"new(\"camera pointer\", () => _cameraPointerInput?.Deactivate())",
"new ResourceShutdownStage(\"session lifetime\"",
"controller.Dispose();",
"new ResourceShutdownStage(\"input callback detach\"",
"binding.Dispose();",
"actions.Dispose();",
"pointer.Dispose();",
"new ResourceShutdownStage(\"session dependents\"",
"pointer.ReleaseMouseLookAfterSessionRetirement();",
"pointer.UnbindGameplayFrame(gameplayFrame);",
"_cameraPointerInput = null;");
AssertAppearsInOrder(
dispose,
"CompleteShutdown();",
"_window?.Dispose();",
"_window = null;");
Assert.Equal(1, CountOccurrences(dispose, "CompleteShutdown();"));
Assert.Equal(1, CountOccurrences(dispose, "_window?.Dispose();"));
Assert.Equal(1, CountOccurrences(dispose, "_window = null;"));
int nativeReleased = dispose.IndexOf("_window = null;", StringComparison.Ordinal)
+ "_window = null;".Length;
string afterNativeRelease = dispose[nativeReleased..];
int fallbackDocumentation = afterNativeRelease.IndexOf(
"/// <summary>",
StringComparison.Ordinal);
if (fallbackDocumentation >= 0)
afterNativeRelease = afterNativeRelease[..fallbackDocumentation];
Assert.All(
afterNativeRelease,
character => Assert.True(char.IsWhiteSpace(character) || character == '}'));
}
[Fact]
public void ResourceRootsAndFramePairHaveExplicitAcquireTransferAndReleaseBoundaries()
{
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string shutdown = Slice(
source,
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
string terrainAtlas = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"TerrainAtlas.cs"));
string worldPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"WorldRenderComposition.cs"));
string livePhase = LivePresentationSource();
AssertAppearsInOrder(
livePhase,
"d.PortalTunnelFallback.AcquirePrepared(",
"static tunnel => tunnel.PrepareResources());",
"d.RenderResourceLifetime.AcquireSkyShader(",
"() => new SkyRenderer(");
AssertAppearsInOrder(
load,
"new WorldRenderCompositionPhase(",
"new LivePresentationCompositionPhase(",
"_portalTunnelFallback.Transfer(",
"_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);");
AssertAppearsInOrder(
worldPhase,
"lifetime.AcquireTerrainAtlas(",
"TerrainAtlas.Build(gl, dats, bindless)",
"TerrainModernRenderer CreateTerrain(",
"TerrainModernRenderer terrain = AcquireAndPublish(");
AssertAppearsInOrder(
shutdown,
"_frameGraphs.Withdraw();",
"_retailUiLease.Dispose();",
"_localPlayerTeleport?.Dispose();",
"_portalTunnelFallback.ReleaseFallback();",
"_skyRenderer?.Dispose()",
"_terrain?.Dispose();",
"_renderResourceLifetime.ReleaseSkyShader",
"_renderResourceLifetime.ReleaseTerrainAtlas",
"_glConstructionCleanup.Dispose",
"_gl?.Dispose();");
Assert.DoesNotContain(
"RetailUiRuntime.Mount(",
load,
StringComparison.Ordinal);
Assert.DoesNotContain("gl.GenTexture()", terrainAtlas, StringComparison.Ordinal);
Assert.DoesNotContain(
"portalTunnel.PrepareResources();",
load,
StringComparison.Ordinal);
Assert.DoesNotContain("_portalTunnel =", source, StringComparison.Ordinal);
Assert.DoesNotContain("_renderFrameOrchestrator", source, StringComparison.Ordinal);
Assert.DoesNotContain("_updateFrameOrchestrator", source, StringComparison.Ordinal);
AssertAppearsInOrder(
source,
"_window.Run();",
"_glConstructionCleanup.RetainFrom(failure);",
"_glConstructionCleanup.Dispose");
}
private static string MethodBody(string start, string end) =>
Slice(GameWindowSource(), start, end);
private static string Slice(string source, string start, string end)
{
int first = source.IndexOf(start, StringComparison.Ordinal);
int last = source.IndexOf(end, first + 1, StringComparison.Ordinal);
Assert.True(first >= 0, $"Missing source boundary: {start}");
Assert.True(last > first, $"Missing source boundary: {end}");
return source[first..last];
}
private static int CountOccurrences(string source, string value)
{
int count = 0;
int cursor = 0;
while ((cursor = source.IndexOf(value, cursor, StringComparison.Ordinal)) >= 0)
{
count++;
cursor += value.Length;
}
return count;
}
private static void AssertAppearsInOrder(string source, params string[] fragments)
{
int cursor = -1;
foreach (string fragment in fragments)
{
int next = source.IndexOf(fragment, cursor + 1, StringComparison.Ordinal);
Assert.True(next >= 0, $"Missing expected source fragment: {fragment}");
Assert.True(next > cursor, $"Out-of-order source fragment: {fragment}");
cursor = next;
}
}
private static string GameWindowSource() => File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
private static string LivePresentationSource() => File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"LivePresentationComposition.cs"));
private static string FindRepoRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
return directory.FullName;
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
}
}