acdream/tests/AcDream.App.Tests/Diagnostics/WorldLifecycleAutomationControllerTests.cs

802 lines
30 KiB
C#

using System.Text.Json;
using AcDream.App.Diagnostics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Packs;
using AcDream.App.Rendering.Residency;
using AcDream.App.Rendering.Scene;
using AcDream.App.Streaming;
using AcDream.App.UI.Testing;
using AcDream.Runtime;
using AcDream.Runtime.World;
using SixLabors.ImageSharp;
namespace AcDream.App.Tests.Diagnostics;
public sealed class WorldLifecycleAutomationControllerTests
{
private static readonly RenderFrameInput FrameInput = new(1d / 60d, 1280, 720);
private static readonly RenderFrameOutcome FrameOutcome = new(
new WorldRenderFrameOutcome(4, 17, NormalWorldDrawn: true),
new PrivatePresentationFrameOutcome(
PortalViewportDrawn: false,
ScreenshotCaptured: true));
[Fact]
public void ScreenshotCapture_FlipsGlRowsAndWritesACompletePng()
{
string directory = NewDirectory();
// GL order: bottom row red/green, top row blue/white.
byte[] rgba =
[
255, 0, 0, 255, 0, 255, 0, 255,
0, 0, 255, 255, 255, 255, 255, 255,
];
var logs = new List<string>();
var controller = new FrameScreenshotController(
(_, _) => rgba,
directory,
logs.Add);
try
{
Assert.True(controller.TryRequest("login_stable", out string error), error);
Assert.False(controller.IsComplete("login_stable"));
Assert.True(controller.CapturePending(2, 2));
Assert.True(controller.IsComplete("login_stable"));
string path = Path.Combine(directory, "login_stable.png");
using Image image = Image.Load(path);
Assert.Equal(2, image.Width);
Assert.Equal(2, image.Height);
Assert.Contains(logs, line => line.Contains("screenshot-complete", StringComparison.Ordinal));
byte[] flipped = FrameScreenshotController.FlipRows(rgba, 2, 2);
Assert.Equal(rgba.AsSpan(8, 8).ToArray(), flipped.AsSpan(0, 8).ToArray());
Assert.Equal(rgba.AsSpan(0, 8).ToArray(), flipped.AsSpan(8, 8).ToArray());
}
finally
{
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void ScreenshotCapture_ReportsNoWorkAndFailedCapture()
{
string directory = NewDirectory();
var controller = new FrameScreenshotController(
(_, _) => [],
directory);
try
{
Assert.False(controller.CapturePending(0, 0));
Assert.True(controller.TryRequest("bad_frame", out string error), error);
Assert.False(controller.CapturePending(0, 0));
Assert.False(controller.IsComplete("bad_frame"));
}
finally
{
if (Directory.Exists(directory))
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void ScreenshotCapture_WritesRenderPackChoiceAndAtmosphereMetadata()
{
string directory = NewDirectory();
var diagnostics = new RenderPackDiagnosticsSnapshot(
RenderPackActivationState.Active,
"acdream.atmospheric",
"1.0.0",
"high",
"medium",
FailureReason: null,
ActivationGeneration: 7,
RetainedGpuBytes: 64,
TransientGpuBytes: 32,
ImageCount: 5,
BufferCount: 2,
DrawCalls: 12,
DispatchCalls: 4,
ShadowCasterCount: 22,
CascadeDrawCount: 44,
CpuClassificationCalls: 1,
SunElevationDegrees: 14.5,
ActiveDayGroup: 2,
Weather: "Clear",
WeatherIntensity: 0.25,
Outdoor: true,
DirectionalShadowStrength: 0.8,
Passes: [])
{
SharedWorldTransformUsedInstances = 68_395,
};
var controller = new FrameScreenshotController(
(_, _) => [255, 255, 255, 255],
directory,
renderPackMetadata: () => diagnostics);
Assert.Contains(
"worldTransforms=68395used",
RenderPackDiagnosticsFormatter.Format(diagnostics),
StringComparison.Ordinal);
try
{
Assert.True(controller.TryRequest("enhanced", out string error), error);
Assert.True(controller.CapturePending(1, 1));
using JsonDocument metadata = JsonDocument.Parse(
File.ReadAllText(Path.Combine(directory, "enhanced.metadata.json")));
JsonElement root = metadata.RootElement;
Assert.Equal(1, root.GetProperty("SchemaVersion").GetInt32());
JsonElement pack = root.GetProperty("RenderPack");
Assert.Equal("acdream.atmospheric", pack.GetProperty("PackId").GetString());
Assert.Equal("medium", pack.GetProperty("EffectiveQuality").GetString());
Assert.Equal(14.5, pack.GetProperty("SunElevationDegrees").GetDouble());
Assert.True(pack.GetProperty("Outdoor").GetBoolean());
Assert.Equal(
68_395u,
pack.GetProperty("SharedWorldTransformUsedInstances").GetUInt32());
}
finally
{
Directory.Delete(directory, recursive: true);
}
}
[Theory]
[InlineData("")]
[InlineData("../escape")]
[InlineData("has spaces")]
[InlineData("name.png")]
public void ArtifactNames_RejectPathsAndAmbiguousCharacters(string name)
{
Assert.False(AutomationArtifactName.TryValidate(name, out string error));
Assert.NotEmpty(error);
}
/// <summary>
/// glReadPixels reads GL_READ_FRAMEBUFFER. A capture that does not name
/// framebuffer 0 captures whichever offscreen target the previous renderer
/// left bound - the paperdoll/appraisal FBOs clear to RGBA(0,0,0,0), so the
/// artifact is a fully transparent PNG that reads as a blank-world failure
/// while the backbuffer on screen was correct.
/// </summary>
[Fact]
public void DefaultFramebufferRead_BindsFramebufferZeroAndRestoresTheCallersBinding()
{
var surface = new RecordingFramebufferSurface(
boundOnEntry: 7u,
drawBoundOnEntry: 3u,
fill: 0x42);
byte[] pixels = FrameScreenshotController.ReadDefaultFramebuffer(surface, 2, 3);
Assert.Equal(
[
"bind-read 0",
"bind-draw 0",
"samples",
"read 2x3",
"bind-read 7",
"bind-draw 3",
],
surface.Calls);
Assert.Equal(0u, surface.ReadFramebufferBindingDuringRead);
Assert.Equal(2 * 3 * 4, pixels.Length);
Assert.All(pixels, value => Assert.Equal(0x42, value));
Assert.Equal(7u, surface.ReadFramebufferBinding);
Assert.Equal(3u, surface.DrawFramebufferBinding);
}
[Fact]
public void DefaultFramebufferRead_RestoresTheCallersBindingWhenTheReadThrows()
{
var surface = new RecordingFramebufferSurface(
boundOnEntry: 9u,
drawBoundOnEntry: 0u,
fill: 0)
{
ReadFailure = new InvalidOperationException("GL_OUT_OF_MEMORY"),
};
Assert.Throws<InvalidOperationException>(
() => FrameScreenshotController.ReadDefaultFramebuffer(surface, 1, 1));
Assert.Equal(
["bind-read 0", "bind-draw 0", "samples", "read 1x1", "bind-read 9", "bind-draw 0"],
surface.Calls);
Assert.Equal(9u, surface.ReadFramebufferBinding);
}
/// <summary>
/// glReadPixels against a multisampled read framebuffer is undefined per the
/// GL spec, and the window is created with the quality preset's MSAA sample
/// count — so every pixel gate and every blank-world verdict in Campaign V
/// was reading through an unspecified operation. The capture must resolve
/// the default framebuffer into a single-sampled target first and read that.
/// </summary>
[Fact]
public void MultisampledDefaultFramebufferRead_ResolvesBeforeReading()
{
var surface = new RecordingFramebufferSurface(
boundOnEntry: 7u,
drawBoundOnEntry: 4u,
fill: 0x11)
{
Samples = 4,
};
byte[] pixels = FrameScreenshotController.ReadDefaultFramebuffer(surface, 2, 3);
Assert.Equal(
[
"bind-read 0",
"bind-draw 0",
"samples",
"create-resolve 2x3",
"bind-draw 64",
"blit 2x3",
"bind-read 64",
"read 2x3",
"delete-resolve 64",
"bind-read 7",
"bind-draw 4",
],
surface.Calls);
// The blit source must still be the multisampled default framebuffer,
// and the read source must be the resolved single-sampled one.
Assert.Equal(0u, surface.ReadFramebufferBindingDuringBlit);
Assert.Equal(64u, surface.DrawFramebufferBindingDuringBlit);
Assert.Equal(64u, surface.ReadFramebufferBindingDuringRead);
Assert.Equal(2 * 3 * 4, pixels.Length);
Assert.All(pixels, value => Assert.Equal(0x11, value));
}
[Fact]
public void MultisampledDefaultFramebufferRead_DeletesTheResolveTargetWhenTheReadThrows()
{
var surface = new RecordingFramebufferSurface(
boundOnEntry: 7u,
drawBoundOnEntry: 4u,
fill: 0)
{
Samples = 4,
ReadFailure = new InvalidOperationException("GL_OUT_OF_MEMORY"),
};
Assert.Throws<InvalidOperationException>(
() => FrameScreenshotController.ReadDefaultFramebuffer(surface, 1, 1));
Assert.Contains("delete-resolve 64", surface.Calls);
Assert.Equal(0, surface.LiveResolveTargets);
Assert.Equal(7u, surface.ReadFramebufferBinding);
Assert.Equal(4u, surface.DrawFramebufferBinding);
}
private sealed class RecordingFramebufferSurface(
uint boundOnEntry,
uint drawBoundOnEntry,
byte fill)
: FrameScreenshotController.IDefaultFramebufferSurface
{
private uint _readBinding = boundOnEntry;
private uint _drawBinding = drawBoundOnEntry;
private uint _nextResolveName = 64u;
public List<string> Calls { get; } = [];
public Exception? ReadFailure { get; init; }
public int Samples { get; init; } = 1;
public int LiveResolveTargets { get; private set; }
public uint? ReadFramebufferBindingDuringRead { get; private set; }
public uint? ReadFramebufferBindingDuringBlit { get; private set; }
public uint? DrawFramebufferBindingDuringBlit { get; private set; }
public uint ReadFramebufferBinding => _readBinding;
public uint DrawFramebufferBinding => _drawBinding;
public int DefaultFramebufferSamples
{
get
{
Calls.Add("samples");
return Samples;
}
}
public void BindReadFramebuffer(uint framebuffer)
{
_readBinding = framebuffer;
Calls.Add($"bind-read {framebuffer}");
}
public void BindDrawFramebuffer(uint framebuffer)
{
_drawBinding = framebuffer;
Calls.Add($"bind-draw {framebuffer}");
}
public uint CreateResolveTarget(int width, int height)
{
Calls.Add($"create-resolve {width}x{height}");
LiveResolveTargets++;
return _nextResolveName++;
}
public void DeleteResolveTarget(uint framebuffer)
{
Calls.Add($"delete-resolve {framebuffer}");
LiveResolveTargets--;
}
public void BlitColorNearest(int width, int height)
{
Calls.Add($"blit {width}x{height}");
ReadFramebufferBindingDuringBlit = _readBinding;
DrawFramebufferBindingDuringBlit = _drawBinding;
}
public void ReadRgba(int width, int height, byte[] destination)
{
Calls.Add($"read {width}x{height}");
ReadFramebufferBindingDuringRead = _readBinding;
if (ReadFailure is not null)
throw ReadFailure;
Array.Fill(destination, fill);
}
}
[Fact]
public void Checkpoint_WritesCanonicalRevealAndResourceSnapshot()
{
string directory = NewDirectory();
var reveal = new RuntimePortalSnapshot(
Generation: 7,
Kind: RuntimePortalKind.Portal,
Readiness: new RuntimeDestinationReadiness(
Generation: 7,
0x8A020164u,
IsIndoor: true,
IsUnhydratable: false,
RequiredRenderRadius: 0,
IsRenderNeighborhoodReady: true,
AreCompositeTexturesReady: true,
IsCollisionReady: true),
Materialized: true,
Completed: true,
Cancelled: false,
WorldViewportObserved: true,
WorldSimulationAvailable: true,
InvariantFailureCount: 0,
WaitCueShown: false,
PortalMaterializationCount: 3);
var resources = EmptyResources() with
{
LoadedLandblocks = 1,
WorldEntities = 42,
RenderSceneOracle = new CurrentRenderSceneOracleSnapshot(
Enabled: true,
CompletedFrameSequence: 19,
AbortedFrames: 1,
ProjectionCount: 73,
OutdoorStaticCount: 41,
CellStaticCount: 20,
DynamicCount: 12,
CellBucketCount: 4,
Digest: new RenderSceneHash128(
Low: 0x0123456789ABCDEF,
High: 0xFEDCBA9876543210),
CompletedPViewFrameSequence: 19,
AbortedPViewFrames: 0,
PViewCandidateCount: 61,
PViewDigest: new RenderSceneHash128(11, 12),
DispatcherFrameSequence: 19,
AbortedDispatcherFrames: 0,
DispatcherDrawCount: 3,
DispatcherEntityCount: 52,
DispatcherMeshRefCount: 80,
DispatcherInstanceCount: 96,
DispatcherOpaqueGroupCount: 11,
DispatcherTransparentGroupCount: 4,
DispatcherDigest: new RenderSceneHash128(13, 14),
CompletedSelectionFrameSequence: 19,
AbortedSelectionFrames: 0,
SelectionPartCount: 7,
SelectionDigest: new RenderSceneHash128(15, 16)),
RenderSceneShadow =
RenderSceneShadowComparisonSnapshot.Disabled with
{
Enabled = true,
ComparisonCount = 19,
SuccessfulComparisonCount = 19,
ComparedOracleFrameSequence = 19,
MatchedProjectionCount = 73,
},
TrackedGpuBytes = 1234,
Residency = new ResidencySnapshot(
[
new ResidencyDomainSnapshot(
ResidencyDomain.Animations,
EntryCount: 2,
OwnerCount: 0,
Charges: new ResidencyCharges(
DecodedBytes: 1024),
BudgetBytes: 4096,
Hits: 8,
Misses: 3,
Evictions: 1),
],
new ResidencyCharges(DecodedBytes: 1024)),
};
var screenshots = new FrameScreenshotController(
(_, _) => [0, 0, 0, 255],
Path.Combine(directory, "screenshots"));
int clientCloseRequests = 0;
var controller = new WorldLifecycleAutomationController(
() => reveal,
() => new RuntimeWorldEnvironmentOwnershipSnapshot(
IsInitialized: true,
DayGroupDefinitionCount: 4,
ActiveDayGroupCount: 1),
() => new RuntimeWorldTransitOwnershipSnapshot(
BufferedTeleportDestinationCount: 0,
PendingTeleportStartCount: 0,
ActiveTeleportCount: 0,
AcceptedTeleportDestinationCount: 0,
ActiveRevealCount: 0,
PendingDestinationReadinessCount: 0,
HostProjectionCount: 0,
PendingHostAcknowledgementCount: 0),
() => 3,
_ => resources,
screenshots,
directory,
requestClientClose: () => clientCloseRequests++);
try
{
Assert.True(controller.IsWorldReady);
Assert.True(controller.IsWorldViewportVisible);
Assert.Equal(3, controller.PortalMaterializationCount);
Assert.True(
controller.TryRequestClientClose(out string closeError),
closeError);
Assert.Equal(1, clientCloseRequests);
Assert.True(controller.TryRequestCheckpoint(
"dungeon",
out IRetailUiAutomationCheckpoint? request,
out string error), error);
Assert.NotNull(request);
Assert.Equal(RetailUiAutomationCheckpointStatus.Pending, request.Status);
controller.Process(FrameInput, FrameOutcome);
Assert.Equal(RetailUiAutomationCheckpointStatus.Succeeded, request.Status);
string line = Assert.Single(File.ReadAllLines(
Path.Combine(directory, "world-lifecycle.checkpoints.jsonl")));
using JsonDocument json = JsonDocument.Parse(line);
Assert.Equal("dungeon", json.RootElement.GetProperty("name").GetString());
Assert.Equal(7, json.RootElement.GetProperty("reveal").GetProperty("generation").GetInt64());
Assert.Equal(
4,
json.RootElement.GetProperty("environmentOwnership")
.GetProperty("dayGroupDefinitionCount").GetInt32());
Assert.Equal(
0,
json.RootElement.GetProperty("transitOwnership")
.GetProperty("pendingHostAcknowledgementCount")
.GetInt32());
Assert.Equal(4, json.RootElement.GetProperty("render").GetProperty("world")
.GetProperty("visibleLandblocks").GetInt32());
Assert.True(json.RootElement.GetProperty("render").GetProperty("presentation")
.GetProperty("screenshotCaptured").GetBoolean());
Assert.Equal(42, json.RootElement.GetProperty("resources").GetProperty("worldEntities").GetInt32());
JsonElement renderSceneOracle = json.RootElement
.GetProperty("resources")
.GetProperty("renderSceneOracle");
Assert.True(renderSceneOracle.GetProperty("enabled").GetBoolean());
Assert.Equal(
19,
renderSceneOracle.GetProperty("completedFrameSequence").GetInt64());
Assert.Equal(
73,
renderSceneOracle.GetProperty("projectionCount").GetInt32());
Assert.Equal(
0x0123456789ABCDEFuL,
renderSceneOracle.GetProperty("digest").GetProperty("low")
.GetUInt64());
Assert.Equal(
0xFEDCBA9876543210uL,
renderSceneOracle.GetProperty("digest").GetProperty("high")
.GetUInt64());
JsonElement renderSceneShadow = json.RootElement
.GetProperty("resources")
.GetProperty("renderSceneShadow");
Assert.True(renderSceneShadow.GetProperty("enabled").GetBoolean());
Assert.Equal(
19,
renderSceneShadow.GetProperty("comparisonCount").GetInt64());
Assert.Equal(
73,
renderSceneShadow.GetProperty("matchedProjectionCount")
.GetInt32());
Assert.Equal(
0,
renderSceneShadow.GetProperty("mismatchCount").GetInt64());
JsonElement residency = json.RootElement
.GetProperty("resources")
.GetProperty("residency");
Assert.Equal(
4096,
residency.GetProperty("totalBudgetBytes").GetInt64());
Assert.Equal(8, residency.GetProperty("totalHits").GetInt64());
Assert.Equal(
1024,
residency.GetProperty("totalCharges")
.GetProperty("decodedBytes")
.GetInt64());
Assert.True(File.Exists(Path.Combine(directory, "checkpoint-dungeon.json")));
}
finally
{
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void CheckpointRequests_DrainInFifoOrderOnce()
{
string directory = NewDirectory();
int captures = 0;
var controller = CreateController(
directory,
_ =>
{
captures++;
return EmptyResources();
});
try
{
Assert.True(controller.TryRequestCheckpoint(
"first", out IRetailUiAutomationCheckpoint? first, out _));
Assert.True(controller.TryRequestCheckpoint(
"second", out IRetailUiAutomationCheckpoint? second, out _));
controller.Process(FrameInput, FrameOutcome);
controller.Process(FrameInput, FrameOutcome);
Assert.NotNull(first);
Assert.NotNull(second);
Assert.Equal(1, first.Sequence);
Assert.Equal(2, second.Sequence);
Assert.Equal(RetailUiAutomationCheckpointStatus.Succeeded, first.Status);
Assert.Equal(RetailUiAutomationCheckpointStatus.Succeeded, second.Status);
Assert.Equal(2, captures);
string[] lines = File.ReadAllLines(
Path.Combine(directory, "world-lifecycle.checkpoints.jsonl"));
Assert.Equal(2, lines.Length);
Assert.Contains("\"name\":\"first\"", lines[0], StringComparison.Ordinal);
Assert.Contains("\"name\":\"second\"", lines[1], StringComparison.Ordinal);
}
finally
{
controller.Dispose();
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void CheckpointWriteFailure_IsReportedOnTokenWithoutThrowingFromFrame()
{
string directory = NewDirectory();
string blockedPath = Path.Combine(directory, "not-a-directory");
File.WriteAllText(blockedPath, "occupied");
var controller = CreateController(blockedPath, _ => EmptyResources());
try
{
Assert.True(controller.TryRequestCheckpoint(
"failed", out IRetailUiAutomationCheckpoint? request, out _));
controller.Process(FrameInput, FrameOutcome);
Assert.NotNull(request);
Assert.Equal(RetailUiAutomationCheckpointStatus.Failed, request.Status);
Assert.Contains("checkpoint 'failed' sequence 1 failed", request.Error);
}
finally
{
controller.Dispose();
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void CheckpointCancellationAndShutdown_AreExplicitAndNeverWritten()
{
string directory = NewDirectory();
var controller = CreateController(directory, _ => EmptyResources());
try
{
Assert.True(controller.TryRequestCheckpoint(
"caller", out IRetailUiAutomationCheckpoint? caller, out _));
Assert.True(controller.TryRequestCheckpoint(
"shutdown", out IRetailUiAutomationCheckpoint? shutdown, out _));
Assert.NotNull(caller);
Assert.NotNull(shutdown);
controller.CancelCheckpoint(caller);
controller.Dispose();
controller.Process(FrameInput, FrameOutcome);
Assert.Equal(RetailUiAutomationCheckpointStatus.Cancelled, caller.Status);
Assert.Contains("cancelled before render-frame capture", caller.Error);
Assert.Equal(RetailUiAutomationCheckpointStatus.Cancelled, shutdown.Status);
Assert.Contains("cancelled during world lifecycle automation shutdown", shutdown.Error);
Assert.False(controller.TryRequestCheckpoint(
"late", out IRetailUiAutomationCheckpoint? late, out string error));
Assert.Null(late);
Assert.Contains("shutting down", error);
Assert.False(File.Exists(Path.Combine(
directory,
"world-lifecycle.checkpoints.jsonl")));
}
finally
{
controller.Dispose();
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void RenderPackPerformanceReset_IsNoOpOnlyAfterFailedToRetail()
{
string directory = NewDirectory();
bool failedToRetail = true;
int resetCalls = 0;
var controller = new WorldLifecycleAutomationController(
() => default,
() => default,
() => default,
() => 0,
_ => EmptyResources(),
new FrameScreenshotController((_, _) => [], directory),
directory,
getRenderPackPerformanceSampleCount: () => 0,
resetRenderPackPerformance: () =>
{
resetCalls++;
return (true, string.Empty);
},
getRenderPackFailedToRetail: () => failedToRetail);
try
{
Assert.True(controller.RenderPackFailedToRetail);
Assert.True(controller.TryResetRenderPackPerformance(out string fallbackError));
Assert.Empty(fallbackError);
Assert.Equal(0, resetCalls);
failedToRetail = false;
Assert.True(controller.TryResetRenderPackPerformance(out string activeError));
Assert.Empty(activeError);
Assert.Equal(1, resetCalls);
}
finally
{
controller.Dispose();
Directory.Delete(directory, recursive: true);
}
}
[Fact]
public void TransitionAutomationDelegatesPreserveExactDisableReenableAndResizeOwnership()
{
string directory = NewDirectory();
var status = new RetailUiAutomationRenderPackStatus(
RetailUiAutomationRenderPackState.Active,
"acdream.atmospheric",
"medium",
ActivationGeneration: 1,
FailureReason: null);
var framebuffer = (Width: 1280, Height: 720);
var calls = new List<string>();
var controller = new WorldLifecycleAutomationController(
() => default,
() => default,
() => default,
() => 0,
_ => EmptyResources(),
new FrameScreenshotController((_, _) => [], directory),
directory,
getRenderPackStatus: () => status,
selectRenderPack: preset =>
{
calls.Add($"select:{preset}");
return (true, string.Empty);
},
disableRenderPack: () =>
{
calls.Add("disable-exact");
return (true, string.Empty);
},
reenableRenderPack: () =>
{
calls.Add("reenable-exact");
return (true, string.Empty);
},
getFramebufferSize: () => framebuffer,
resizeFramebuffer: (width, height) =>
{
calls.Add($"resize:{width}x{height}");
framebuffer = (width, height);
return (true, string.Empty);
});
try
{
Assert.Equal(status, controller.RenderPackStatus);
Assert.True(controller.TrySelectRenderPack("high", out string selectError));
Assert.Empty(selectError);
Assert.True(controller.TryDisableRenderPack(out string disableError));
Assert.Empty(disableError);
Assert.True(controller.TryReenableRenderPack(out string reenableError));
Assert.Empty(reenableError);
Assert.True(controller.TryResizeFramebuffer(1024, 768, out string resizeError));
Assert.Empty(resizeError);
Assert.Equal(1024, controller.FramebufferWidth);
Assert.Equal(768, controller.FramebufferHeight);
Assert.Equal(
["select:high", "disable-exact", "reenable-exact", "resize:1024x768"],
calls);
}
finally
{
controller.Dispose();
Directory.Delete(directory, recursive: true);
}
}
private static WorldLifecycleAutomationController CreateController(
string directory,
Func<RenderFrameOutcome, WorldLifecycleResourceSnapshot> capture) =>
new(
() => default,
() => default,
() => default,
() => 0,
capture,
new FrameScreenshotController((_, _) => [], directory),
directory);
private static WorldLifecycleResourceSnapshot EmptyResources() => new(
0, 0, 0, 0, 0, 0, 0, CurrentRenderSceneOracleSnapshot.Disabled,
RenderSceneShadowComparisonSnapshot.Disabled,
RenderFrameProductComparisonSnapshot.Disabled,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0L, 0, 0L, 0L, 0L, 0L, 0, 0, 0, 0, 0, 0, 0, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
0, 0, 0, 0, 0, 0, 0, default,
default, default, 0d, 0d, null);
private static string NewDirectory()
{
string path = Path.Combine(Path.GetTempPath(), "acdream-world-gate-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(path);
return path;
}
}