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

133 lines
6 KiB
C#

using System.Reflection;
using System.Reflection.Emit;
using AcDream.App.Tests.Architecture;
using AcDream.App.Physics;
using AcDream.Core.World;
using AcDream.Runtime.Session;
namespace AcDream.App.Tests.Physics;
public sealed class LiveEntityNetworkBranchRoutingTests
{
[Fact]
public void Vector_ProjectileStopsCanonicalAndOrdinaryRoutes()
{
var calls = new List<string>();
LiveEntityVectorRoute route = LiveEntityVectorRouter.Route(
() => { calls.Add("projectile"); return true; },
() => { calls.Add("canonical"); return true; },
() => calls.Add("ordinary"));
Assert.Equal(LiveEntityVectorRoute.Projectile, route);
Assert.Equal(["projectile"], calls);
}
[Fact]
public void Vector_CanonicalBodyRunsOnlyAfterProjectileDeclines()
{
var calls = new List<string>();
LiveEntityVectorRoute route = LiveEntityVectorRouter.Route(
() => { calls.Add("projectile"); return false; },
() => { calls.Add("canonical"); return true; },
() => calls.Add("ordinary"));
Assert.Equal(LiveEntityVectorRoute.CanonicalBody, route);
Assert.Equal(["projectile", "canonical"], calls);
}
[Fact]
public void Vector_OrdinaryRemoteIsTheLastFallback()
{
var calls = new List<string>();
LiveEntityVectorRoute route = LiveEntityVectorRouter.Route(
() => { calls.Add("projectile"); return false; },
() => { calls.Add("canonical"); return false; },
() => calls.Add("ordinary"));
Assert.Equal(LiveEntityVectorRoute.OrdinaryRemote, route);
Assert.Equal(["projectile", "canonical", "ordinary"], calls);
}
// C4 route 2 (2026-08-03): LocalForcePositionTransaction and its
// ForcePosition_* coverage here are RETIRED, not adapted — the class is
// deleted outright (docs/research/2026-08-03-c4-route-2-contract.md
// §"The contract" item 2). Its three jobs (ownership validation, the
// blip/commit, and the exactly-once ack including the displaced-
// authority case its trailing isCurrent() covered) are now properties of
// the Runtime-owned RuntimeAcceptedPositionDriveController and are tested
// there: tests/AcDream.Runtime.Tests/Session/RuntimeAcceptedPositionDriveControllerTests.cs.
/// <summary>
/// R8 review fix (2026-08-03): assembly checks for the generic-tail
/// double-write guard in <c>LiveEntityNetworkUpdateController.OnPosition</c>.
/// A full behavioral fixture is impractical here for the SAME reason
/// <c>C3cF1ProductionWiringTests</c> gives — the controller's dependency
/// set is composition-only (67+ collaborators wired only by
/// <c>SessionPlayerComposition</c>) — so this follows that file's exact
/// established pattern: assert the compiled call/return structure rather
/// than construct the class. The Runtime-level behavioral
/// coverage for the seam itself lives in
/// RuntimeAcceptedPositionDriveControllerTests; these pins are what stop
/// this App-layer call site from silently reintroducing the retired
/// duplicate-write authority (the deleted LocalForcePositionTransaction
/// pair + the generic render-tail writing the SAME accepted Position a
/// second time — 670f307c's divergence class).
/// </summary>
public sealed class LiveEntityNetworkUpdateControllerForcePositionWiringTests
{
[Fact]
public void GenericTailWriteIsNeverDuplicatedForTheLocalForcePositionPath()
{
// The generic render-tail's WorldEntity write is the ONE
// remaining writer of an accepted Position — it must serve
// remotes only, never a second local-player write alongside the
// Runtime-committed one.
MethodInfo genericTail = typeof(LiveEntityNetworkUpdateController)
.GetMethod(
"TryApplyGenericRemoteRenderPose",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new MissingMethodException(
typeof(LiveEntityNetworkUpdateController).FullName,
"TryApplyGenericRemoteRenderPose");
Assert.Single(
CompiledCallGraph.Read(genericTail),
call => call.Target.DeclaringType == typeof(WorldEntity)
&& call.Target.Name == nameof(WorldEntity.SetPosition));
}
[Fact]
public void CommittedOrDeferredCellReturnsBeforeReachingTheGenericTail()
{
// The Committed/DeferredCell branch must still return
// immediately after its two preserved side effects — a missing
// `return` here would fall through into the generic tail below
// and resurrect the double-write.
MethodInfo onPosition = typeof(LiveEntityNetworkUpdateController)
.GetMethod(
"OnPosition",
BindingFlags.Instance | BindingFlags.Public)
?? throw new MissingMethodException(
typeof(LiveEntityNetworkUpdateController).FullName,
"OnPosition");
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(onPosition);
CompiledCall drive = Assert.Single(calls, call =>
call.Target.DeclaringType
== typeof(RuntimeAcceptedPositionDriveController)
&& call.Target.Name == "TryExecuteAcceptedLocalPosition");
CompiledCall observe = calls.First(call =>
call.Offset > drive.Offset
&& call.Target.Name == "ObserveAcceptedLocalPosition");
IReadOnlyList<CompiledInstruction> instructions =
CompiledCallGraph.ReadInstructions(onPosition);
int observeInstruction = instructions
.Select((instruction, index) => (instruction, index))
.Single(pair => pair.instruction.Offset == observe.Offset)
.index;
Assert.Equal(OpCodes.Ret, instructions[observeInstruction + 1].OpCode);
}
}
}