Files
Project-M/Assets/_Project/Tests/EditMode/BoonOfferTests.cs
T
kronic e27a495530 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>
2026-07-08 21:06:33 -07:00

107 lines
4.8 KiB
C#

using NUnit.Framework;
using ProjectM.Server;
using ProjectM.Simulation;
using Unity.Collections;
using Unity.Core;
using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Tests
{
/// <summary>
/// Pins the boon pool math (<see cref="BoonMath.PickBoons"/>: deterministic, 3 distinct, class-filtered,
/// weight-0 excluded) and <see cref="BoonOfferSystem"/> (one deal per RoomEpoch; expedition players only;
/// owner-seeded per player so co-op offers differ).
/// </summary>
public class BoonOfferTests
{
[Test]
public void PickBoons_Deterministic_Distinct_ClassFiltered()
{
var blob = BoonCatalogData.BuildDefault(Allocator.Temp);
ref var pool = ref blob.Value;
for (byte classId = 0; classId <= 1; classId++)
{
for (uint seed = 1; seed < 200; seed += 7)
{
int n = BoonMath.PickBoons(seed, classId, ref pool, out byte a0, out byte a1, out byte a2);
Assert.AreEqual(3, n, "the default pool always fills 3 options");
Assert.AreNotEqual(a0, a1, "distinct");
Assert.AreNotEqual(a1, a2, "distinct");
Assert.AreNotEqual(a0, a2, "distinct");
// Deterministic re-draw.
BoonMath.PickBoons(seed, classId, ref pool, out byte b0, out byte b1, out byte b2);
Assert.AreEqual(a0, b0);
Assert.AreEqual(a1, b1);
Assert.AreEqual(a2, b2);
// Every option is class-legal.
byte bit = BoonMath.MaskFor(classId);
foreach (var id in new[] { a0, a1, a2 })
{
int idx = BoonMath.FindDef(ref pool, id);
Assert.GreaterOrEqual(idx, 0, "offered id exists");
Assert.AreNotEqual(0, pool.Defs[idx].ClassMask & bit,
$"boon {id} offered to class {classId} must pass its ClassMask");
}
}
}
blob.Dispose();
}
[Test]
public void OfferSystem_DealsOncePerRoom_ExpeditionOnly_PerPlayerSeeds()
{
var world = new World("BoonOfferTest");
using (world)
{
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
group.AddSystemToUpdateList(world.GetOrCreateSystem<BoonOfferSystem>());
group.SortSystems();
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
var em = world.EntityManager;
var dir = em.CreateEntity(typeof(RunInfo), typeof(RunRuntime));
em.SetComponentData(dir, new RunInfo { Lifecycle = RunLifecycle.RoomReward, CurrentRoom = 1 });
em.SetComponentData(dir, new RunRuntime { RunSeed = 4242u, RoomEpoch = 2 });
var catalog = em.CreateEntity(typeof(BoonCatalog));
em.SetComponentData(catalog, new BoonCatalog { Value = BoonCatalogData.BuildDefault() });
Entity MakePlayer(int netId, byte region, byte classId)
{
var e = em.CreateEntity(typeof(PlayerTag), typeof(BoonOffer), typeof(GhostOwner),
typeof(RegionTag), typeof(PlayerClass));
em.SetComponentData(e, new GhostOwner { NetworkId = netId });
em.SetComponentData(e, new RegionTag { Region = region });
em.SetComponentData(e, new PlayerClass { ClassId = classId });
return e;
}
var out1 = MakePlayer(1, RegionId.Expedition, 0);
var out2 = MakePlayer(2, RegionId.Expedition, 1);
var home = MakePlayer(3, RegionId.Base, 0);
group.Update(); // one-shot state attach
group.Update(); // deal
var offer1 = em.GetComponentData<BoonOffer>(out1);
var offer2 = em.GetComponentData<BoonOffer>(out2);
Assert.AreEqual(1, offer1.Pending, "expedition player 1 dealt");
Assert.AreEqual(1, offer2.Pending, "expedition player 2 dealt");
Assert.AreEqual(0, em.GetComponentData<BoonOffer>(home).Pending, "home player dealt NOTHING");
bool differ = offer1.Option0 != offer2.Option0 || offer1.Option1 != offer2.Option1
|| offer1.Option2 != offer2.Option2;
Assert.IsTrue(differ, "per-player seeds -> co-op offers differ (seed folds NetworkId)");
// Same epoch -> no re-deal (clear one offer and confirm it stays cleared).
em.SetComponentData(out1, default(BoonOffer));
group.Update();
Assert.AreEqual(0, em.GetComponentData<BoonOffer>(out1).Pending, "one deal per RoomEpoch (latch)");
}
}
}
}