using System.Reflection; using AcDream.App.Rendering; namespace AcDream.App.Tests.Rendering; public sealed class PrivatePresentationRendererTests { private static readonly RenderFrameInput Input = new(0.125d, 1600, 900); private static readonly WorldRenderFrameOutcome World = new(3, 12, true); [Theory] [InlineData(false, false)] [InlineData(true, false)] [InlineData(false, true)] [InlineData(true, true)] public void Render_PreservesPortalPaperdollGameplayDevToolsScreenshotOrder( bool portalVisible, bool screenshotCaptured) { List calls = []; var portal = new Portal(calls); var paperdoll = new Paperdoll(calls); var gameplay = new GameplayUi(calls); var devTools = new DevTools(calls); var screenshots = new Screenshots(calls, screenshotCaptured); var presentation = new PrivatePresentationRenderer( portal, new FoundationSource(portalVisible), paperdoll, gameplay, devTools, screenshots); PrivatePresentationFrameOutcome outcome = presentation.Render(Input, World); Assert.Equal( ["portal", "paperdoll", "gameplay-ui", "devtools-render", "screenshot"], calls); Assert.Equal(new PrivatePresentationFrameOutcome( portalVisible, screenshotCaptured), outcome); Assert.Equal((Input.ViewportWidth, Input.ViewportHeight), portal.Size); Assert.Equal((Input.ViewportWidth, Input.ViewportHeight), screenshots.Size); Assert.Equal(Input, gameplay.Input); Assert.Equal(Input, devTools.Input); } [Fact] public void Render_OptionalLayersMayBeAbsentButPortalOutcomeRemainsObserved() { List calls = []; var presentation = new PrivatePresentationRenderer( new Portal(calls), new FoundationSource(portalVisible: true), paperdoll: null, gameplayUi: null, devTools: null, screenshots: null); PrivatePresentationFrameOutcome outcome = presentation.Render(Input, World); Assert.Equal(["portal"], calls); Assert.True(outcome.PortalViewportDrawn); Assert.False(outcome.ScreenshotCaptured); } [Fact] public void Render_ReportsThePreparedPortalSnapshotWhenDrawMutatesTheSource() { List calls = []; var foundation = new MutableFoundationSource(portalVisible: true); var presentation = new PrivatePresentationRenderer( new MutatingPortal(calls, () => foundation.PortalVisible = false), foundation, paperdoll: null, gameplayUi: null, devTools: null, screenshots: null); PrivatePresentationFrameOutcome outcome = presentation.Render(Input, World); Assert.Equal(["portal"], calls); Assert.False(foundation.PortalVisible); Assert.True(outcome.PortalViewportDrawn); } [Fact] public void Constructor_RequiresTheCanonicalPortalOwner() { Assert.Throws(() => new PrivatePresentationRenderer(null!, new FoundationSource(false), null, null, null, null)); Assert.Throws(() => new PrivatePresentationRenderer(new Portal([]), null!, null, null, null, null)); } [Fact] public void ExtractedPresentationOwner_RetainsNoDirectWindowOrDelegate() { FieldInfo[] fields = typeof(PrivatePresentationRenderer).GetFields( BindingFlags.Instance | BindingFlags.NonPublic); Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow)); Assert.DoesNotContain( fields, field => typeof(Delegate).IsAssignableFrom(field.FieldType)); } [Fact] public void RetainedUiAdapter_UsesTheDocumentedRuntimeBindingBoundary() { FieldInfo[] adapterFields = typeof(RetainedGameplayUiFrame).GetFields( BindingFlags.Instance | BindingFlags.NonPublic); Assert.Equal( [ typeof(AcDream.App.UI.RetailUiRuntime), typeof(Silk.NET.Input.IInputContext), ], adapterFields.Select(field => field.FieldType).OrderBy(type => type.FullName)); Assert.DoesNotContain( adapterFields, field => field.FieldType == typeof(GameWindow) || typeof(Delegate).IsAssignableFrom(field.FieldType)); FieldInfo bindings = typeof(AcDream.App.UI.RetailUiRuntime).GetField( "_bindings", BindingFlags.Instance | BindingFlags.NonPublic)!; Assert.Equal(typeof(AcDream.App.UI.RetailUiRuntimeBindings), bindings.FieldType); Assert.Contains( typeof(AcDream.App.UI.RetailUiRuntimeBindings).GetProperties(), property => property.PropertyType.Name.EndsWith("Bindings", StringComparison.Ordinal)); } [Fact] public void KnownSharedOwnerPaths_ArePinnedWithoutClaimingRecursivePurity() { static Type[] FieldTypes(Type owner) => owner.GetFields( BindingFlags.Instance | BindingFlags.NonPublic) .Select(field => field.FieldType) .ToArray(); Assert.Contains( typeof(AcDream.App.UI.RetailUiRuntime), FieldTypes(typeof(RetainedGameplayUiFrame))); Assert.Contains( typeof(AcDream.App.UI.UiViewport), FieldTypes(typeof(RetailPaperdollFrameView))); Assert.Contains( typeof(AcDream.App.UI.UiElement), FieldTypes(typeof(PaperdollInventoryVisibility))); Assert.Contains( typeof(AcDream.UI.Abstractions.Panels.Debug.DebugVM), FieldTypes(typeof(DevToolsPanelSet))); Assert.Contains( typeof(AcDream.App.Settings.IRuntimeSettingsPreviewSource), FieldTypes(typeof(RuntimeWorldFrameSettingsPreview))); Assert.Contains( typeof(AcDream.App.Streaming.LocalPlayerTeleportController), FieldTypes(typeof(LocalPlayerPortalViewport))); Assert.Contains( typeof(AcDream.App.Streaming.ILocalPlayerTeleportInputLifetime), FieldTypes(typeof(AcDream.App.Streaming.LocalPlayerTeleportController))); Assert.DoesNotContain( FieldTypes(typeof(PrivateFrameScreenshot)), type => typeof(Delegate).IsAssignableFrom(type)); Assert.Null(typeof(AcDream.App.Diagnostics.FrameScreenshotController).GetField( "_getSize", BindingFlags.Instance | BindingFlags.NonPublic)); } private sealed class Portal(List calls) : IPrivatePortalViewport { public (int Width, int Height) Size { get; private set; } public void Draw(int width, int height) { Size = (width, height); calls.Add("portal"); } } private sealed class FoundationSource(bool portalVisible) : IRenderFrameFoundationSource { public RenderFrameFoundation Foundation { get; } = new( portalVisible, default, default); } private sealed class MutableFoundationSource(bool portalVisible) : IRenderFrameFoundationSource { public bool PortalVisible { get; set; } = portalVisible; public RenderFrameFoundation Foundation => new( PortalVisible, default, default); } private sealed class MutatingPortal(List calls, Action mutate) : IPrivatePortalViewport { public void Draw(int width, int height) { calls.Add("portal"); mutate(); } } private sealed class Paperdoll(List calls) : IPrivatePaperdollFrame { public void Render() => calls.Add("paperdoll"); } private sealed class GameplayUi(List calls) : IRetainedGameplayUiFrame { public RenderFrameInput Input { get; private set; } public void Render(double deltaSeconds, int width, int height) { Input = new RenderFrameInput(deltaSeconds, width, height); calls.Add("gameplay-ui"); } } private sealed class DevTools(List calls) : IDevToolsFrameLifecycle { public RenderFrameInput Input { get; private set; } public void BeginFrame(float deltaSeconds) => calls.Add("devtools-begin"); public void AbortFrame() => calls.Add("devtools-abort"); public void Render(double deltaSeconds, int viewportWidth, int viewportHeight) { Input = new RenderFrameInput(deltaSeconds, viewportWidth, viewportHeight); calls.Add("devtools-render"); } } private sealed class Screenshots(List calls, bool result) : IPrivateFrameScreenshot { public (int Width, int Height) Size { get; private set; } public bool CapturePending(int width, int height) { Size = (width, height); calls.Add("screenshot"); return result; } } }