using System.Reflection; using AcDream.App.Composition; using AcDream.App.Rendering; namespace AcDream.App.Tests.Composition; public sealed class FrameRootCompositionTests { [Fact] public void RuntimeBindingsReleaseInReverseAndRetryOnlyFailedEdges() { var calls = new List(); var bindings = new FrameRootRuntimeBindings(); bindings.Adopt("first", new RetryBinding("first", calls, 0)); bindings.Adopt("second", new RetryBinding("second", calls, 1)); Assert.Throws(bindings.Dispose); Assert.Equal(["second", "first"], calls); bindings.Dispose(); bindings.Dispose(); Assert.Equal(["second", "first", "second"], calls); Assert.Throws(() => bindings.Adopt("late", new RetryBinding("late", calls, 0))); } [Fact] public void ProductionPhasePublishesOnlyAfterBothRootsExist() { string source = File.ReadAllText(Path.Combine( FindRepoRoot(), "src", "AcDream.App", "Composition", "FrameRootComposition.cs")); AssertAppearsInOrder( source, "new RenderFrameResourceController(", "new WorldSceneRenderer(", "new RenderFrameOrchestrator(", "new RetailLiveFrameCoordinator(", "new UpdateFrameOrchestrator(", "d.FrameGraphs.PublishOwned(", "_publication.PublishFrameRoots(result);", "graphLease.Transfer();", "bindingsLease.Transfer();"); } [Fact] public void GameWindowRetainsOnlyThePhaseBoundaryAndFrameHandoffs() { string source = File.ReadAllText(Path.Combine( FindRepoRoot(), "src", "AcDream.App", "Rendering", "GameWindow.cs")); Assert.Contains("new FrameRootCompositionPhase(", source); Assert.DoesNotContain("new AcDream.App.Rendering.WorldSceneRenderer(", source); Assert.DoesNotContain("new AcDream.App.Update.UpdateFrameOrchestrator(", source); Assert.DoesNotContain("CaptureWorldLifecycleResourceSnapshot", source); Assert.DoesNotContain("_frameGraphs.Publish(", source); } [Fact] public void FramePhaseAndSnapshotSourceRetainNoWindowOwner() { Assert.DoesNotContain( typeof(FrameRootCompositionPhase).GetFields( BindingFlags.Instance | BindingFlags.NonPublic), field => field.FieldType == typeof(GameWindow)); string snapshots = File.ReadAllText(Path.Combine( FindRepoRoot(), "src", "AcDream.App", "Diagnostics", "WorldLifecycleResourceSnapshotSource.cs")); Assert.DoesNotContain("GameWindow", snapshots, StringComparison.Ordinal); Assert.Contains("_liveEntities.PendingTeardownCount", snapshots); Assert.Contains("GpuMemoryTracker.AllocatedBytes", snapshots); Assert.Contains("_frameProfiler.LastReport", snapshots); } private sealed class RetryBinding( string name, List calls, int failures) : IDisposable { private int _failures = failures; public void Dispose() { calls.Add(name); if (_failures-- > 0) throw new InvalidOperationException("retry"); } } private static void AssertAppearsInOrder(string source, params string[] values) { int cursor = 0; foreach (string value in values) { int found = source.IndexOf(value, cursor, StringComparison.Ordinal); Assert.True(found >= 0, $"Missing expected source fragment: {value}"); cursor = found + value.Length; } } 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."); } }