516aacee18
ItemDatabaseBlobTests (inline-mod round-trip on the 2nd item — the nested-BlobArray regression guard); EquipSystemTests (equip sets ability+mod+moves item; unequip reverses + restores DefaultAbility; equip-over swaps; full-bag swap rejected with no loss; non-equippable/absent/unresolvable no-op; strip leaves foreign SourceIds untouched). 236/236 EditMode pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
2.6 KiB
C#
56 lines
2.6 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Simulation;
|
|
using Unity.Collections;
|
|
using Unity.Entities;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// Regression guard for the Phase 1 inline-mod design: <see cref="ItemDatabaseBlob.TryGetItem"/> returns the
|
|
/// def BY VALUE, so the inline <see cref="ItemModSpec"/> 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.
|
|
/// </summary>
|
|
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<ItemDatabaseBlob>();
|
|
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<ItemDatabaseBlob>(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(); }
|
|
}
|
|
}
|
|
}
|