test: replace composition source freezes
This commit is contained in:
parent
caa5eb8b2b
commit
80c7b44457
8 changed files with 471 additions and 406 deletions
|
|
@ -1,6 +1,12 @@
|
|||
using System.Reflection;
|
||||
using AcDream.App.Composition;
|
||||
using AcDream.App.Diagnostics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Tests.Architecture;
|
||||
using AcDream.App.Update;
|
||||
using AcDream.App.World;
|
||||
|
||||
namespace AcDream.App.Tests.Composition;
|
||||
|
||||
|
|
@ -28,48 +34,77 @@ public sealed class FrameRootCompositionTests
|
|||
[Fact]
|
||||
public void ProductionPhasePublishesOnlyAfterBothRootsExist()
|
||||
{
|
||||
string source = File.ReadAllText(Path.Combine(
|
||||
FindRepoRoot(),
|
||||
"src",
|
||||
"AcDream.App",
|
||||
"Composition",
|
||||
"FrameRootComposition.cs"));
|
||||
MethodInfo compose = typeof(FrameRootCompositionPhase).GetMethod(
|
||||
"ComposeCore",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new MissingMethodException(
|
||||
typeof(FrameRootCompositionPhase).FullName,
|
||||
"ComposeCore");
|
||||
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(compose);
|
||||
|
||||
AssertAppearsInOrder(
|
||||
source,
|
||||
"new RenderFrameResourceController(",
|
||||
"new RenderSceneShadowComparisonController(",
|
||||
"new WorldSceneRenderer(",
|
||||
"new WorldLifecycleAutomationController(",
|
||||
"\"world lifecycle automation owner\"",
|
||||
"\"world lifecycle automation binding\"",
|
||||
"new SerialRenderFramePostDiagnosticsPhase(",
|
||||
"new RenderFrameOrchestrator(",
|
||||
"postDiagnostics,",
|
||||
"new RetailLiveFrameCoordinator(",
|
||||
"new UpdateFrameOrchestrator(",
|
||||
"d.FrameGraphs.PublishOwned(",
|
||||
"_publication.PublishFrameRoots(result);",
|
||||
"graphLease.Transfer();",
|
||||
"bindingsLease.Transfer();");
|
||||
int resources = CallIndex(calls, typeof(RenderFrameResourceController), ".ctor");
|
||||
int comparison = CallIndex(
|
||||
calls,
|
||||
typeof(RenderSceneShadowComparisonController),
|
||||
".ctor",
|
||||
resources + 1);
|
||||
int scene = CallIndex(calls, typeof(WorldSceneRenderer), ".ctor", comparison + 1);
|
||||
int automation = CallIndex(
|
||||
calls,
|
||||
typeof(WorldLifecycleAutomationController),
|
||||
".ctor",
|
||||
scene + 1);
|
||||
int diagnostics = CallIndex(
|
||||
calls,
|
||||
typeof(SerialRenderFramePostDiagnosticsPhase),
|
||||
".ctor",
|
||||
automation + 1);
|
||||
int render = CallIndex(calls, typeof(RenderFrameOrchestrator), ".ctor", diagnostics + 1);
|
||||
int live = CallIndex(calls, typeof(RetailLiveFrameCoordinator), ".ctor", render + 1);
|
||||
int update = CallIndex(calls, typeof(UpdateFrameOrchestrator), ".ctor", live + 1);
|
||||
int graph = CallIndex(calls, typeof(GameFrameGraphSlot), "PublishOwned", update + 1);
|
||||
int publish = CallIndex(
|
||||
calls,
|
||||
typeof(IGameWindowFrameRootPublication),
|
||||
"PublishFrameRoots",
|
||||
graph + 1);
|
||||
int firstTransfer = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.First(pair => pair.index > publish
|
||||
&& pair.call.Target.Name == "Transfer")
|
||||
.index;
|
||||
int secondTransfer = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.First(pair => pair.index > firstTransfer
|
||||
&& pair.call.Target.Name == "Transfer")
|
||||
.index;
|
||||
|
||||
Assert.True(secondTransfer > firstTransfer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GameWindowRetainsOnlyThePhaseBoundaryAndFrameHandoffs()
|
||||
{
|
||||
string source = File.ReadAllText(Path.Combine(
|
||||
FindRepoRoot(),
|
||||
"src",
|
||||
"AcDream.App",
|
||||
"Rendering",
|
||||
"GameWindow.cs"));
|
||||
IReadOnlyList<CompiledCall> calls =
|
||||
CompiledCallGraph.ReadDeclared(typeof(GameWindow));
|
||||
|
||||
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("_worldLifecycleAutomation", source);
|
||||
Assert.DoesNotContain("_frameGraphs.Publish(", source);
|
||||
Assert.Single(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(FrameRootCompositionPhase)
|
||||
&& call.Target.IsConstructor);
|
||||
Assert.DoesNotContain(
|
||||
calls,
|
||||
call => call.Target.IsConstructor
|
||||
&& call.Target.DeclaringType is { } type
|
||||
&& (type == typeof(WorldSceneRenderer)
|
||||
|| type == typeof(UpdateFrameOrchestrator)
|
||||
|| type == typeof(WorldLifecycleAutomationController)));
|
||||
Assert.DoesNotContain(
|
||||
calls,
|
||||
call => call.Target.DeclaringType
|
||||
== typeof(WorldLifecycleResourceSnapshotSource)
|
||||
&& call.Target.Name == nameof(
|
||||
WorldLifecycleResourceSnapshotSource.Capture));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -80,19 +115,37 @@ public sealed class FrameRootCompositionTests
|
|||
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);
|
||||
Assert.Contains("Capture(RenderFrameOutcome outcome)", snapshots);
|
||||
Assert.Contains("outcome.World.VisibleLandblocks", snapshots);
|
||||
Assert.Contains("outcome.World.TotalLandblocks", snapshots);
|
||||
Assert.DoesNotContain(
|
||||
typeof(WorldLifecycleResourceSnapshotSource).GetFields(
|
||||
BindingFlags.Instance | BindingFlags.NonPublic),
|
||||
field => field.FieldType == typeof(GameWindow));
|
||||
|
||||
MethodInfo capture = typeof(WorldLifecycleResourceSnapshotSource)
|
||||
.GetMethod(nameof(WorldLifecycleResourceSnapshotSource.Capture))!;
|
||||
Assert.Equal(
|
||||
typeof(RenderFrameOutcome),
|
||||
Assert.Single(capture.GetParameters()).ParameterType);
|
||||
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(capture);
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(LiveEntityRuntime)
|
||||
&& call.Target.Name == "get_PendingTeardownCount");
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(GpuMemoryTracker)
|
||||
&& call.Target.Name == "get_AllocatedBytes");
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(FrameProfiler)
|
||||
&& call.Target.Name == "get_LastReport");
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(WorldRenderFrameOutcome)
|
||||
&& call.Target.Name == "get_VisibleLandblocks");
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(WorldRenderFrameOutcome)
|
||||
&& call.Target.Name == "get_TotalLandblocks");
|
||||
}
|
||||
|
||||
private sealed class RetryBinding(
|
||||
|
|
@ -110,27 +163,20 @@ public sealed class FrameRootCompositionTests
|
|||
}
|
||||
}
|
||||
|
||||
private static void AssertAppearsInOrder(string source, params string[] values)
|
||||
private static int CallIndex(
|
||||
IReadOnlyList<CompiledCall> calls,
|
||||
Type declaringType,
|
||||
string methodName,
|
||||
int startIndex = 0)
|
||||
{
|
||||
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.");
|
||||
int index = CompiledCallGraph.IndexOf(
|
||||
calls,
|
||||
declaringType,
|
||||
methodName,
|
||||
startIndex);
|
||||
Assert.True(
|
||||
index >= startIndex,
|
||||
$"Missing compiled edge {declaringType.FullName}.{methodName}.");
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue