test: replace input and physics source freezes

This commit is contained in:
Erik 2026-08-18 15:31:57 +02:00
parent 0ad2ee1cdf
commit caa5eb8b2b
6 changed files with 313 additions and 238 deletions

View file

@ -1,10 +1,18 @@
using System.Reflection;
using AcDream.App.Input;
using AcDream.App.Tests.Architecture;
using AcDream.App.World;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
namespace AcDream.App.Tests.Input;
/// <summary>
/// C3c-F2 (2026-08-02): source pin for the production player-mode auto-entry
/// precondition. <c>PlayerModeAutoEntry</c> is a ONE-SHOT that disarms before
/// invoking its callback, and the production callback completes the world
/// reveal, so an attempt made before the Runtime first-entry conductor has
/// C3c-F2 (2026-08-02): compiled architecture check for the production
/// player-mode auto-entry precondition. <c>PlayerModeAutoEntry</c> is a
/// ONE-SHOT that disarms before invoking its callback, and the production
/// callback completes the world reveal, so an attempt made before the Runtime
/// first-entry conductor has
/// committed permanently seals the reveal with the player never in world —
/// the second link of the connected-gate login wedge
/// (logs/connected-world-gate-20260802-130455: one "not committed yet" line,
@ -14,45 +22,61 @@ namespace AcDream.App.Tests.Input;
/// The guard's own one-shot/latch behavior is covered behaviorally by
/// AcDream.Core.Tests.Input.AutoEnterPlayerModeTests; only the production
/// context's dependency graph (a ~15-dependency PlayerModeController) has no
/// focused harness, hence the source pin.
/// focused harness, hence the metadata check.
/// </summary>
public sealed class C3cF2AutoEntryWiringTests
{
[Fact]
public void ProductionAutoEntryRequiresTheRuntimePublishedController()
{
string source = ReadSource("Input", "PlayerModeAutoEntry.cs");
MethodInfo readiness = typeof(LivePlayerModeAutoEntryContext)
.GetProperty(
"IsPlayerControllerReady",
BindingFlags.Instance | BindingFlags.Public)!
.GetMethod!;
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(readiness);
Assert.DoesNotContain(
"public bool IsPlayerControllerReady => true;",
source,
StringComparison.Ordinal);
Assert.Contains(
"IsRuntimePublished: true",
source,
StringComparison.Ordinal);
calls,
call => call.Target.DeclaringType == typeof(PlayerMovementController)
&& call.Target.Name == "get_IsRuntimePublished");
Assert.Contains(
"record.PhysicsHost is EntityPhysicsHost",
source,
StringComparison.Ordinal);
calls,
call => call.Target.DeclaringType == typeof(LiveEntityRuntime)
&& call.Target.Name == nameof(LiveEntityRuntime.TryGetRecord));
Assert.Contains(
calls,
call => call.Target.DeclaringType == typeof(LiveEntityRecord)
&& call.Target.Name == "get_PhysicsHost");
Assert.Contains(
typeof(EntityPhysicsHost),
CompiledCallGraph.ReadTypeReferences(readiness));
}
[Fact]
public void PlayerPresentationAttach_DrainsMatchedAnimationsBeforeStartupMotionSuffix()
{
string source = ReadSource("Input", "PlayerModeController.cs");
int attach = source.IndexOf(
"controller.Motion.DefaultSink =",
StringComparison.Ordinal);
int animationDrain = source.IndexOf(
"sequencer.Manager.HandleEnterWorld();",
attach,
StringComparison.Ordinal);
int unmatchedMotionDrain = source.IndexOf(
"controller.Motion.HandleExitWorld();",
animationDrain,
StringComparison.Ordinal);
MethodInfo enter = typeof(PlayerModeController).GetMethod(
"BuildControllerAndCamera",
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new MissingMethodException(
typeof(PlayerModeController).FullName,
"BuildControllerAndCamera");
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(enter);
int attach = CompiledCallGraph.IndexOf(
calls,
typeof(MotionInterpreter),
"set_DefaultSink");
int animationDrain = CompiledCallGraph.IndexOf(
calls,
typeof(MotionTableManager),
nameof(MotionTableManager.HandleEnterWorld),
attach + 1);
int unmatchedMotionDrain = CompiledCallGraph.IndexOf(
calls,
typeof(MotionInterpreter),
nameof(MotionInterpreter.HandleExitWorld),
animationDrain + 1);
Assert.True(attach >= 0, "The player animation sink was not attached.");
Assert.True(
@ -62,24 +86,4 @@ public sealed class C3cF2AutoEntryWiringTests
unmatchedMotionDrain > animationDrain,
"Only the unmatched pre-attach interpreter suffix may drain last.");
}
private static string ReadSource(params string[] relativePath)
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
{
return File.ReadAllText(Path.Combine(
directory.FullName,
"src",
"AcDream.App",
Path.Combine(relativePath)));
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
}
}