using NUnit.Framework; using ProjectM.Simulation; using Unity.Collections; using Unity.Entities; namespace ProjectM.Tests { /// /// Regression guard for the Phase 1 inline-mod design: returns the /// def BY VALUE, so the inline slots must survive that copy. (A nested BlobArray of /// mods would corrupt its relative-offset pointer on this copy and read empty — the blocker the inline layout /// avoids.) Looks up the SECOND item by id and reads a non-zero mod value: an index-1 lookup + a non-zero read /// is exactly what would expose an offset corruption that a length-only check on item 0 could miss. /// public class ItemDatabaseBlobTests { [Test] public void TryGetItem_RoundTrips_Inline_Mods_For_Second_Item() { var builder = new BlobBuilder(Allocator.Temp); ref var root = ref builder.ConstructRoot(); var arr = builder.Allocate(ref root.Items, 2); arr[0] = new ItemDefBlob { ItemId = 100, EquipSlot = EquipSlotId.Weapon }; arr[1] = new ItemDefBlob { ItemId = 101, EquipSlot = EquipSlotId.Armor, Mod0 = new ItemModSpec { Target = (byte)StatTarget.MoveSpeed, Op = (byte)ModOp.PercentAdd, Value = 0.25f }, Mod1 = new ItemModSpec { Target = (byte)StatTarget.Damage, Op = (byte)ModOp.Flat, Value = 7f }, Mod2 = new ItemModSpec { Target = 255 }, Mod3 = new ItemModSpec { Target = 255 }, }; var blob = builder.CreateBlobAssetReference(Allocator.Persistent); builder.Dispose(); try { ref var db = ref blob.Value; Assert.IsTrue(db.TryGetItem(101, out var def), "Second item resolves by id."); Assert.AreEqual(EquipSlotId.Armor, def.EquipSlot); var m0 = def.GetMod(0); Assert.AreEqual((byte)StatTarget.MoveSpeed, m0.Target, "Inline Mod0 target survives the by-value copy."); Assert.AreEqual(0.25f, m0.Value, 1e-4f, "Inline Mod0 value survives the by-value copy (would read 0 under a nested-blob corruption)."); var m1 = def.GetMod(1); Assert.AreEqual((byte)StatTarget.Damage, m1.Target); Assert.AreEqual(7f, m1.Value, 1e-4f); Assert.AreEqual(255, def.GetMod(2).Target, "Unused inline slots stay 255."); } finally { blob.Dispose(); } } } }