using System.Collections; using System.Reflection; using AcDream.App.Physics; using AcDream.App.Rendering; using AcDream.App.World; namespace AcDream.App.Tests.World; public sealed class GameWindowLiveEntityCompositionTests { private const BindingFlags PrivateImplementation = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic; [Theory] [InlineData("RegisterLiveEntityCollision")] [InlineData("MotionTableDefaultPose")] [InlineData("LeaveWorldLiveEntityRuntimeComponents")] [InlineData("WithdrawLiveEntityWorldProjection")] [InlineData("RebindAnimatedEntityForAppearance")] [InlineData("OnLiveEntitySpawned")] [InlineData("OnLiveEntitySpawnedLocked")] [InlineData("RehydrateServerEntitiesForLandblock")] [InlineData("TryInitializeLiveCenter")] [InlineData("CompleteLiveEntityReady")] [InlineData("OnLiveAppearanceUpdated")] [InlineData("OnLiveEntityPickedUp")] [InlineData("OnLiveParentUpdated")] [InlineData("TryAcceptParentForRender")] [InlineData("OnLiveEntityDeleted")] [InlineData("OnLiveEntityPruned")] [InlineData("TearDownLiveEntityRuntimeComponents")] [InlineData("CreateLiveEntityRuntimeTeardownPlan")] [InlineData("RouteSameGenerationCreateObject")] [InlineData("OnLiveMotionUpdated")] [InlineData("OnLivePositionUpdated")] [InlineData("OnLiveVectorUpdated")] [InlineData("OnLiveStateUpdated")] [InlineData("EnsureRemoteMotionBindings")] [InlineData("ResolvePhysicsHost")] [InlineData("GetSetupCylinder")] [InlineData("RouteServerMoveTo")] [InlineData("StickToObjectFromWire")] [InlineData("ClearTargetForHiddenEntity")] [InlineData("ApplyServerControlledVelocityCycle")] [InlineData("RunRemoteTeleportHook")] [InlineData("SendImmediateLocalPositionEvent")] [InlineData("DispatchRemoteInboundMotion")] [InlineData("CreateRemoteMotion")] [InlineData("WillAdvanceRemoteMotion")] [InlineData("AdvanceLiveObjectRuntime")] [InlineData("AdvanceLiveObjectRuntimeCore")] [InlineData("ReconcileLiveObjectSpatialPresentation")] [InlineData("CaptureAnimationHooks")] public void GameWindow_DoesNotReacquireExtractedLiveEntityBodies(string methodName) { Assert.Null(typeof(GameWindow).GetMethod(methodName, PrivateImplementation)); } [Fact] public void GameWindow_DoesNotOwnAnAppearanceUpdateStateType() { Assert.Null(typeof(GameWindow).GetNestedType( "AppearanceUpdateState", BindingFlags.NonPublic)); } [Theory] [InlineData(typeof(LiveEntityCollisionBuilder))] [InlineData(typeof(LiveEntityDefaultPoseResolver))] [InlineData(typeof(LiveEntityProjectionWithdrawalController))] [InlineData(typeof(LocalPlayerShadowState))] [InlineData(typeof(LiveEntityAppearanceBinding))] [InlineData(typeof(LiveEntityHydrationController))] [InlineData(typeof(DatLiveEntityProjectionMaterializer))] [InlineData(typeof(LiveEntityRuntimeTeardownController))] [InlineData(typeof(LiveEntityNetworkUpdateController))] [InlineData(typeof(LiveEntityMotionRuntimeController))] [InlineData(typeof(LiveEntityInboundAuthorityGate))] [InlineData(typeof(DeferredLiveEntityMotionRuntimeBindings))] [InlineData(typeof(AcDream.App.Update.LiveObjectFrameController))] [InlineData(typeof(AcDream.App.Update.LiveEffectFrameController))] [InlineData(typeof(AcDream.App.Update.LiveSpatialPresentationReconciler))] [InlineData(typeof(AcDream.App.Streaming.StreamingFrameController))] [InlineData(typeof(LiveEntityAnimationPresenter))] [InlineData(typeof(LiveAnimationPresentationContext))] [InlineData(typeof(StaticLiveRootCommitter))] [InlineData(typeof(RetailLiveFrameCoordinator))] public void ExtractedHelpers_DoNotOwnGuidIndexesOrBackendState(Type helperType) { foreach (FieldInfo field in helperType.GetFields( BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)) { Assert.False(typeof(IDictionary).IsAssignableFrom(field.FieldType), $"{helperType.Name}.{field.Name} must resolve identity through LiveEntityRuntime."); string typeName = field.FieldType.FullName ?? field.FieldType.Name; Assert.DoesNotContain("Silk.NET", typeName, StringComparison.Ordinal); Assert.DoesNotContain("OpenGL", typeName, StringComparison.OrdinalIgnoreCase); Assert.NotEqual(typeof(GameWindow), field.FieldType); } } [Fact] public void ExtractedUpdateOwners_DoNotRetainAnonymousCallbacks() { Type[] owners = [ typeof(AcDream.App.Update.LiveObjectFrameController), typeof(AcDream.App.Update.LiveEffectFrameController), typeof(AcDream.App.Update.LiveSpatialPresentationReconciler), typeof(AcDream.App.Streaming.StreamingFrameController), typeof(LiveEntityAnimationScheduler), typeof(LiveEntityAnimationPresenter), typeof(LiveAnimationPresentationContext), typeof(StaticLiveRootCommitter), typeof(RetailLiveFrameCoordinator), ]; foreach (Type owner in owners) { Assert.DoesNotContain( owner.GetFields(BindingFlags.Instance | BindingFlags.NonPublic), field => typeof(Delegate).IsAssignableFrom(field.FieldType)); } Assert.False(typeof(ILiveAnimationPresentationContext).IsAssignableFrom( typeof(GameWindow))); } [Fact] public void RuntimeViewsAndProjectileController_RetainTypedSourcesNotWindowClosures() { FieldInfo animationRuntime = Assert.Single( typeof(LiveEntityAnimationRuntimeView) .GetFields(BindingFlags.Instance | BindingFlags.NonPublic), field => field.Name == "_runtime"); FieldInfo remoteRuntime = Assert.Single( typeof(LiveEntityRemoteMotionRuntimeView) .GetFields(BindingFlags.Instance | BindingFlags.NonPublic), field => field.Name == "_runtime"); Assert.Equal(typeof(ILiveEntityRuntimeSource), animationRuntime.FieldType); Assert.Equal(typeof(ILiveEntityRuntimeSource), remoteRuntime.FieldType); FieldInfo[] projectileFields = typeof(ProjectileController).GetFields( BindingFlags.Instance | BindingFlags.NonPublic); Assert.Equal( typeof(IProjectileSetupResolver), Assert.Single(projectileFields, field => field.Name == "_setupResolver").FieldType); Assert.Equal( typeof(IEntityRootPosePublisher), Assert.Single(projectileFields, field => field.Name == "_rootPoses").FieldType); Assert.Equal( typeof(LiveWorldOriginState), Assert.Single(projectileFields, field => field.Name == "_origin").FieldType); Assert.DoesNotContain( projectileFields, field => typeof(Delegate).IsAssignableFrom(field.FieldType) && !field.Name.Contains("DiagnosticSink", StringComparison.Ordinal)); string source = File.ReadAllText(Path.Combine( FindRepoRoot(), "src", "AcDream.App", "Rendering", "GameWindow.cs")); Assert.DoesNotContain( "new LiveEntityAnimationRuntimeView(() =>", source, StringComparison.Ordinal); Assert.DoesNotContain( "new LiveEntityRemoteMotionRuntimeView(() =>", source, StringComparison.Ordinal); Assert.Contains("new AcDream.App.Physics.DatProjectileSetupResolver", source); } [Fact] public void SessionReset_ClosesEveryStreamingReadinessOwner() { string source = File.ReadAllText(Path.Combine( FindRepoRoot(), "src", "AcDream.App", "Rendering", "GameWindow.cs")); Assert.Contains("_localPlayerMode.ResetSession();", source, StringComparison.Ordinal); Assert.Contains( "_liveEntityNetworkUpdates?.ResetSessionState();", source, StringComparison.Ordinal); Assert.Contains("_liveWorldOrigin.Reset();", source, StringComparison.Ordinal); } 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."); } }