acdream/tests/AcDream.App.Tests/Physics/Issue270ProductionWiringTests.cs

115 lines
5 KiB
C#

using System.Reflection;
using AcDream.App.Net;
using AcDream.App.Physics;
using AcDream.App.Tests.Architecture;
using AcDream.Core.Physics;
using AcDream.Runtime.Gameplay;
namespace AcDream.App.Tests.Physics;
public sealed class Issue270ProductionWiringTests
{
[Fact]
public void MovementStats_UseOneEdgeTrackerAndResetItWithTheSession()
{
// C3c-F1 (2026-08-02): the #270 invariant is unchanged — exactly one
// stamina-exhaustion edge tracker, exhaustion dispatched once on the
// edge, tracker reset with the session — but the wiring moved from
// the factory's deleted ApplyMovementStats body into
// LiveMovementStatsApplier, which routes through the Runtime
// movement owner's typed seam instead of touching the controller.
_ = Assert.Single(
typeof(LiveMovementStatsApplier).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType == typeof(StaminaExhaustionEdgeTracker));
IReadOnlyList<CompiledCall> applierCalls =
CompiledCallGraph.ReadDeclared(typeof(LiveMovementStatsApplier));
Assert.Single(
applierCalls,
call =>
call.Target.DeclaringType == typeof(StaminaExhaustionEdgeTracker)
&& call.Target.Name == nameof(StaminaExhaustionEdgeTracker.Observe));
Assert.Single(
applierCalls,
call =>
call.Target.DeclaringType
== typeof(RuntimeLocalPlayerMovementState)
&& call.Target.Name == nameof(
RuntimeLocalPlayerMovementState.ReportExhaustion));
Assert.Single(
applierCalls,
call =>
call.Target.DeclaringType == typeof(StaminaExhaustionEdgeTracker)
&& call.Target.Name == nameof(StaminaExhaustionEdgeTracker.Reset));
IReadOnlyList<CompiledCall> factoryCalls =
CompiledCallGraph.ReadDeclared(typeof(LiveSessionRuntimeFactory));
Assert.Single(
factoryCalls,
call =>
call.Target.DeclaringType == typeof(LiveMovementStatsApplier)
&& call.Target.Name == nameof(LiveMovementStatsApplier.Reset));
// The factory keeps zero direct controller mutations: both stat
// callbacks route through the applier's seam.
Assert.Equal(
2,
factoryCalls.Count(call =>
call.Target.DeclaringType == typeof(LiveMovementStatsApplier)
&& call.Target.Name == nameof(LiveMovementStatsApplier.Apply)));
Assert.DoesNotContain(
factoryCalls,
call => call.Target.DeclaringType == typeof(MotionInterpreter)
&& call.Target.Name == nameof(MotionInterpreter.ReportExhaustion));
}
[Fact]
public void RemoteSpawnSettle_IsRetriedAndCoversBothCreationRoutes()
{
const BindingFlags flags = BindingFlags.Instance
| BindingFlags.Static
| BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.DeclaredOnly;
MethodInfo[] callers = typeof(LiveEntityNetworkUpdateController)
.GetMethods(flags)
.Where(method => method.GetMethodBody() is not null)
.Where(method => CompiledCallGraph.Read(method).Any(call =>
call.Target.DeclaringType
== typeof(LiveEntityNetworkUpdateController)
&& call.Target.Name == "SeedRemoteSpawnPlacement"))
.ToArray();
Assert.Equal(2, callers.Length);
Assert.Equal(
["DispatchRemoteInboundMotion", "OnPosition"],
callers.Select(method => method.Name).Order().ToArray());
MethodInfo retryingInboundRoute = Assert.Single(
callers,
method => method.Name == "DispatchRemoteInboundMotion");
IReadOnlyList<CompiledCall> retryCalls =
CompiledCallGraph.Read(retryingInboundRoute);
int contact = CompiledCallGraph.IndexOf(
retryCalls,
typeof(PhysicsBody),
"get_InContact");
int settle = CompiledCallGraph.IndexOf(
retryCalls,
typeof(LiveEntityNetworkUpdateController),
"SeedRemoteSpawnPlacement");
Assert.True(contact >= 0 && settle > contact);
// C3c-F5: the settle helper moved to Core (SpawnPlacementSettler) so
// the local player's Runtime first-entry activation shares the same
// tested compressed-first-gravity-frame sweep.
MethodInfo seed = typeof(LiveEntityNetworkUpdateController).GetMethod(
"SeedRemoteSpawnPlacement",
flags)
?? throw new MissingMethodException(
typeof(LiveEntityNetworkUpdateController).FullName,
"SeedRemoteSpawnPlacement");
Assert.Contains(
CompiledCallGraph.Read(seed),
call => call.Target.DeclaringType == typeof(SpawnPlacementSettler)
&& call.Target.Name == nameof(SpawnPlacementSettler.TrySettle));
}
}