Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates. Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean. Co-authored-by: OpenAI Codex <codex@openai.com>
112 lines
3.2 KiB
C#
112 lines
3.2 KiB
C#
using AcDream.App.World;
|
|
|
|
namespace AcDream.App.Tests.World;
|
|
|
|
public sealed class RetailInboundEventDispatcherTests
|
|
{
|
|
private sealed class Counter
|
|
{
|
|
public int Value;
|
|
}
|
|
|
|
[Fact]
|
|
public void NestedEvents_DrainAfterCompleteOuterTail_InArrivalOrder()
|
|
{
|
|
var dispatcher = new RetailInboundEventDispatcher();
|
|
var calls = new List<string>();
|
|
|
|
dispatcher.Run(() =>
|
|
{
|
|
calls.Add("position-head");
|
|
dispatcher.Run(() => calls.Add("state"));
|
|
dispatcher.Run(() => calls.Add("vector"));
|
|
calls.Add("position-tail");
|
|
});
|
|
|
|
Assert.Equal(
|
|
["position-head", "position-tail", "state", "vector"],
|
|
calls);
|
|
Assert.False(dispatcher.IsDraining);
|
|
Assert.Equal(0, dispatcher.PendingCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void FrameOperation_DefersInboundMutationUntilQuantumTail()
|
|
{
|
|
var dispatcher = new RetailInboundEventDispatcher();
|
|
var calls = new List<string>();
|
|
|
|
dispatcher.Run(() =>
|
|
{
|
|
calls.Add("physics");
|
|
dispatcher.Run(() => calls.Add("position"));
|
|
calls.Add("shadow-and-hooks");
|
|
});
|
|
|
|
Assert.Equal(["physics", "shadow-and-hooks", "position"], calls);
|
|
}
|
|
|
|
[Fact]
|
|
public void NestedStateOperations_DrainAfterOuterTail_InArrivalOrder()
|
|
{
|
|
var dispatcher = new RetailInboundEventDispatcher();
|
|
var calls = new List<string>();
|
|
|
|
dispatcher.Run(
|
|
dispatcher,
|
|
calls,
|
|
static (active, output) =>
|
|
{
|
|
output.Add("position-head");
|
|
active.Run(
|
|
output,
|
|
"state",
|
|
static (target, value) => target.Add(value));
|
|
output.Add("position-tail");
|
|
});
|
|
|
|
Assert.Equal(["position-head", "position-tail", "state"], calls);
|
|
Assert.False(dispatcher.IsDraining);
|
|
Assert.Equal(0, dispatcher.PendingCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void Failure_DiscardsQueuedTailAndLeavesDispatcherReusable()
|
|
{
|
|
var dispatcher = new RetailInboundEventDispatcher();
|
|
bool queuedRan = false;
|
|
|
|
Assert.Throws<InvalidOperationException>(() => dispatcher.Run(() =>
|
|
{
|
|
dispatcher.Run(() => queuedRan = true);
|
|
throw new InvalidOperationException("packet failed");
|
|
}));
|
|
|
|
Assert.False(queuedRan);
|
|
Assert.False(dispatcher.IsDraining);
|
|
Assert.Equal(0, dispatcher.PendingCount);
|
|
|
|
dispatcher.Run(() => queuedRan = true);
|
|
Assert.True(queuedRan);
|
|
}
|
|
|
|
[Fact]
|
|
public void StateFastPath_DoesNotAllocateAfterWarmup()
|
|
{
|
|
var dispatcher = new RetailInboundEventDispatcher();
|
|
var counter = new Counter();
|
|
Action<Counter, int> increment =
|
|
static (target, amount) => target.Value += amount;
|
|
dispatcher.Run(counter, 1, increment);
|
|
|
|
long before = GC.GetAllocatedBytesForCurrentThread();
|
|
for (int i = 0; i < 1_000; i++)
|
|
{
|
|
dispatcher.Run(counter, 1, increment);
|
|
}
|
|
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
|
|
|
Assert.Equal(0, allocated);
|
|
Assert.Equal(1_001, counter.Value);
|
|
}
|
|
}
|