test(app): freeze GameWindow lifecycle boundaries

Pin the accepted startup, input, frame, resize, shutdown, and native-window order before Slice 8 moves those edges, while deleting only unread duplicate state and test-only GameWindow facades.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 09:27:55 +02:00
parent 20ebf217e2
commit 476c2e6de1
7 changed files with 380 additions and 56 deletions

View file

@ -0,0 +1,277 @@
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,
"startupStore.LoadDisplay()",
"QualitySettings.WithEnvOverrides(startupBase)",
"_displayFramePacing.InitializeStartup(startupDisplay.VSync)",
"VSync = startupPacing.UseVSync",
"Samples = startupQuality.MsaaSamples",
"Window.Create(options)",
"_displayFramePacing.BindSurface(",
"_window.Load += OnLoad;",
"_window.Update += OnUpdate;",
"_window.Render += OnRender;",
"_window.Render += _displayFramePacing.OnFrameRendered;",
"_window.Closing += OnClosing;",
"_window.FocusChanged += OnFocusChanged;",
"_window.Move += _displayFramePacing.OnWindowMoved;",
"_window.StateChanged += _displayFramePacing.OnWindowStateChanged;",
"_window.FramebufferResize += OnFramebufferResize;",
"_window.Run();");
string[] registrations =
[
"_window.Load += OnLoad;",
"_window.Update += OnUpdate;",
"_window.Render += OnRender;",
"_window.Render += _displayFramePacing.OnFrameRendered;",
"_window.Closing += OnClosing;",
"_window.FocusChanged += OnFocusChanged;",
"_window.Move += _displayFramePacing.OnWindowMoved;",
"_window.StateChanged += _displayFramePacing.OnWindowStateChanged;",
"_window.FramebufferResize += OnFramebufferResize;",
];
foreach (string registration in registrations)
Assert.Equal(1, CountOccurrences(body, registration));
}
[Fact]
public void SessionStart_FollowsFrameGraphPublication()
{
string body = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionLifecycleHost");
AssertAppearsInOrder(
body,
"_gl = GL.GetApi(_window!);",
"_input = _window!.CreateInput();",
"_kbSource = new AcDream.App.Input.SilkKeyboardSource(firstKb);",
"_mouseSource = new AcDream.App.Input.SilkMouseSource(",
"_inputDispatcher = new AcDream.UI.Abstractions.Input.InputDispatcher(",
"_movementInput.Bind(_inputDispatcher);",
"_cameraInput.Bind(_inputDispatcher);",
"_inputDispatcher.Fired += OnInputAction;",
"mouse.MouseMove +=",
"_cameraController = new CameraController(orbit, fly);",
"_dats = RuntimeDatCollectionFactory.OpenReadOnly(_datDir);",
"LoadAndApplyPersistedSettings();",
"_uiHost = new AcDream.App.UI.UiHost(",
"_uiHost.WireMouse(m)",
"_uiHost.WireKeyboard(kb)",
"_retailUiRuntime = AcDream.App.UI.RetailUiRuntime.Mount(",
"_liveEntities = new AcDream.App.World.LiveEntityRuntime(",
"_renderFrameOrchestrator =",
"_updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(",
"_liveSessionController.Start(");
Assert.Equal(1, CountOccurrences(body, "_liveSessionController.Start("));
int start = body.IndexOf("_liveSessionController.Start(", 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 InputAction_PreservesRetailAcceptedPriority()
{
string body = MethodBody(
"private void OnInputAction(",
"private void ToggleLiveCombatMode()");
AssertAppearsInOrder(
body,
"HandlePointerAction(action, activation)",
"action == AcDream.UI.Abstractions.Input.InputAction.ScrollUp",
"HandleScrollAction(action);",
"HandleCombatAction(action, activation)",
"activation != AcDream.UI.Abstractions.Input.ActivationType.Press",
"_retailUiRuntime?.HandleInputAction(action)",
"_selectionInteractions?.HandleInputAction(action)",
"HandlePressedMovementAction(action)",
"switch (action)");
}
[Fact]
public void FramebufferResize_PreservesViewportCameraAndDevtoolsOrder()
{
string body = MethodBody(
"private void OnFramebufferResize(",
"private void ApplyDisplayWindowState(");
AssertAppearsInOrder(
body,
"if (newSize.X <= 0 || newSize.Y <= 0) return;",
"_gl?.Viewport(",
"_viewportAspect.Update(",
"_cameraController?.SetAspect(",
"_devToolsFramePresenter?.ResetLayout(");
Assert.DoesNotContain("_uiHost", body, StringComparison.Ordinal);
}
[Fact]
public void UpdateRenderFocusAndCloseRemainNarrowHostEdges()
{
string source = GameWindowSource();
string update = Slice(
source,
"private void OnUpdate(double dt)",
"private void OnCameraModeChanged(");
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, "_updateFrameOrchestrator.Tick("));
Assert.Equal(1, CountOccurrences(render, "_renderFrameOrchestrator!.Render("));
AssertAppearsInOrder(
render,
"Vector2D<int> size = _window!.Size;",
"_renderFrameOrchestrator!.Render(",
"new AcDream.App.Rendering.RenderFrameInput(");
Assert.DoesNotContain("FramebufferSize", render, StringComparison.Ordinal);
AssertAppearsInOrder(focus, "if (!focused)", "_gameplayInputFrame?.EndMouseLook();");
AssertAppearsInOrder(close, "=> CompleteShutdown();", "_shutdown.CompleteOrThrow();");
}
[Fact]
public void Shutdown_PreservesDependencyStagesAndNativeWindowLast()
{
string source = GameWindowSource();
string manifest = Slice(
source,
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
string dispose = Slice(
source,
"public void Dispose()",
"private sealed class NullAnimLoader");
string[] stages =
[
"new ResourceShutdownStage(\"session lifetime\"",
"new ResourceShutdownStage(\"frame borrowers\"",
"new ResourceShutdownStage(\"session dependents\"",
"new ResourceShutdownStage(\"live entities\"",
"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(\"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(
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 == '}'));
}
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 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.");
}
}