Hygiene B6b: convert leaky-world tests + HudSystem cosmetics

- 10 EditMode files: every bare trailing world.Dispose() now inside using(world){} (single-world) or a [TearDown]+List<World> (the multi-world reject-matrix tests in BoonApplyTests/RouteSelectSystemTests) — an assertion failure can no longer leak the World and mask the true first failure. No test logic changed.
- HudSystem: collapse the blank-line run before the class close; trim the extracted route-map clause from the READY-panel comment.

466/466 EditMode tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-08 21:06:33 -07:00
parent 6379f5d897
commit e27a495530
11 changed files with 128 additions and 62 deletions
@@ -4,6 +4,7 @@ using ProjectM.Simulation;
using Unity.Core;
using Unity.Entities;
using Unity.NetCode;
using System.Collections.Generic;
namespace ProjectM.Tests
{
@@ -18,6 +19,14 @@ namespace ProjectM.Tests
{
const uint Seed = 999u;
const int GateLayer = 2;
readonly List<World> _worlds = new();
[TearDown]
public void Cleanup()
{
foreach (var w in _worlds) if (w.IsCreated) w.Dispose();
_worlds.Clear();
}
static (World world, SimulationSystemGroup group, Entity dir) MakeGateWorld(byte lifecycle = RunLifecycle.RouteSelect)
{
@@ -69,6 +78,8 @@ namespace ProjectM.Tests
public void ValidPick_Latches_WithTrueServerEpoch()
{
var (world, group, dir) = MakeGateWorld();
using (world)
{
var em = world.EntityManager;
MakePlayer(em, 1, RegionId.Expedition);
SendPick(em, 1, optionIndex: 1, forSeed: (int)Seed, forLayer: GateLayer);
@@ -80,13 +91,15 @@ namespace ProjectM.Tests
Assert.AreEqual(1, cmd.OptionIndex, "the picked option");
Assert.AreEqual(3, cmd.ForRunEpoch, "stamped from the TRUE server epoch, never the client echo");
Assert.AreEqual(0, PendingRequests(em), "request consumed");
world.Dispose();
}
}
[Test]
public void TwoSameTickPicks_FirstWins()
{
var (world, group, dir) = MakeGateWorld();
using (world)
{
var em = world.EntityManager;
MakePlayer(em, 1, RegionId.Expedition);
MakePlayer(em, 2, RegionId.Expedition);
@@ -99,7 +112,7 @@ namespace ProjectM.Tests
Assert.AreEqual(1, cmd.HasPick, "exactly one commit");
Assert.AreEqual(0, cmd.OptionIndex, "the FIRST accepted pick wins (in-place latch, DR-014)");
Assert.AreEqual(0, PendingRequests(em), "both requests consumed");
world.Dispose();
}
}
[Test]
@@ -107,51 +120,53 @@ namespace ProjectM.Tests
{
// Wrong run-identity token (a stale pick from the previous run).
var (w1, g1, d1) = MakeGateWorld();
_worlds.Add(w1);
MakePlayer(w1.EntityManager, 1, RegionId.Expedition);
SendPick(w1.EntityManager, 1, 0, forSeed: (int)Seed + 1, forLayer: GateLayer);
g1.Update();
Assert.AreEqual(0, w1.EntityManager.GetComponentData<RouteCommand>(d1).HasPick, "wrong seed rejected");
Assert.AreEqual(0, PendingRequests(w1.EntityManager));
w1.Dispose();
// Wrong layer (a pick from the previous gate of the SAME run).
var (w2, g2, d2) = MakeGateWorld();
_worlds.Add(w2);
MakePlayer(w2.EntityManager, 1, RegionId.Expedition);
SendPick(w2.EntityManager, 1, 0, (int)Seed, forLayer: GateLayer - 1);
g2.Update();
Assert.AreEqual(0, w2.EntityManager.GetComponentData<RouteCommand>(d2).HasPick, "stale layer rejected");
w2.Dispose();
// Option index out of the published range.
var (w3, g3, d3) = MakeGateWorld();
_worlds.Add(w3);
MakePlayer(w3.EntityManager, 1, RegionId.Expedition);
SendPick(w3.EntityManager, 1, optionIndex: 2, (int)Seed, GateLayer); // count is 2 -> max index 1
g3.Update();
Assert.AreEqual(0, w3.EntityManager.GetComponentData<RouteCommand>(d3).HasPick, "out-of-range rejected");
w3.Dispose();
// Base-region sender (N3): a home-bound joiner cannot commit the party's route.
var (w4, g4, d4) = MakeGateWorld();
_worlds.Add(w4);
MakePlayer(w4.EntityManager, 1, RegionId.Base);
SendPick(w4.EntityManager, 1, 0, (int)Seed, GateLayer);
g4.Update();
Assert.AreEqual(0, w4.EntityManager.GetComponentData<RouteCommand>(d4).HasPick, "base sender rejected (N3)");
w4.Dispose();
// Gate closed (mid-room): the pick is dropped, never queued.
var (w5, g5, d5) = MakeGateWorld(lifecycle: RunLifecycle.InRoom);
_worlds.Add(w5);
MakePlayer(w5.EntityManager, 1, RegionId.Expedition);
SendPick(w5.EntityManager, 1, 0, (int)Seed, GateLayer);
g5.Update();
Assert.AreEqual(0, w5.EntityManager.GetComponentData<RouteCommand>(d5).HasPick, "closed gate rejected");
Assert.AreEqual(0, PendingRequests(w5.EntityManager), "request still consumed");
w5.Dispose();
}
[Test]
public void AlreadyLatched_LaterPickIgnored()
{
var (world, group, dir) = MakeGateWorld();
using (world)
{
var em = world.EntityManager;
MakePlayer(em, 1, RegionId.Expedition);
em.SetComponentData(dir, new RouteCommand { HasPick = 1, OptionIndex = 0, ForRunEpoch = 3, ForLayer = GateLayer });
@@ -161,7 +176,7 @@ namespace ProjectM.Tests
var cmd = em.GetComponentData<RouteCommand>(dir);
Assert.AreEqual(0, cmd.OptionIndex, "an already-latched gate ignores later picks");
world.Dispose();
}
}
}
}