Move Region/environment, mandatory modern rendering, terrain, WB, texture, and sampler construction behind the typed Phase-4 composition boundary. Give every fallible GL constructor prefix retryable ownership so partial startup failure cannot leak or replay resource deletion while preserving the accepted render path and DAT inputs. Co-authored-by: Codex <codex@openai.com>
582 lines
26 KiB
C#
582 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);",
|
|
"_uiHost = _retailUiLease.AcquireHost(",
|
|
"_uiHost.WireMouse(m)",
|
|
"_uiHost.WireKeyboard(kb)",
|
|
"_retailUiRuntime = _retailUiLease.Mount(",
|
|
"_liveEntities = new AcDream.App.World.LiveEntityRuntime(",
|
|
"_selectionInteractions ??= new AcDream.App.Interaction.SelectionInteractionController(",
|
|
"_retainedUiGameplayBinding =",
|
|
"AcDream.App.Input.RetainedUiGameplayBinding.Create(",
|
|
"_retainedUiGameplayBinding.Attach();",
|
|
"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(
|
|
load,
|
|
"AcDream.App.Input.RetainedUiGameplayBinding.Create("));
|
|
Assert.Equal(1, CountOccurrences(
|
|
load,
|
|
"_retainedUiGameplayBinding.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? PickWorldGuidAtCursor(");
|
|
|
|
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)");
|
|
string dispose = Slice(
|
|
source,
|
|
"public void Dispose()",
|
|
"private sealed class NullAnimLoader");
|
|
|
|
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"));
|
|
|
|
AssertAppearsInOrder(
|
|
load,
|
|
"new WorldRenderCompositionPhase(",
|
|
"_portalTunnelFallback.AcquirePrepared(",
|
|
"static portalTunnel => portalTunnel.PrepareResources());",
|
|
"_renderResourceLifetime.AcquireSkyShader(",
|
|
"_skyRenderer = new AcDream.App.Rendering.Sky.SkyRenderer(",
|
|
"_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 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.");
|
|
}
|
|
}
|