#nullable enable using System.Diagnostics; using Demo2.Domain; using Demo2.ECS; using Demo2.Runtime; using NUnit.Framework; using Unity.Collections; using Unity.Entities; using UnityEngine; namespace Demo2.Tests { public sealed class Demo2EcsTests { [Test] public void SameSeedAndCommands_RemainIdenticalForTenThousandTicks() { using var left = new Demo2EcsWorld(20260817, Demo2BuiltInContent.BuildRules()); using var right = new Demo2EcsWorld(20260817, Demo2BuiltInContent.BuildRules()); left.SetPhase(Demo2Phase.Production); right.SetPhase(Demo2Phase.Production); for (var tick = 0; tick < 10_000; tick++) { left.Tick(); right.Tick(); } var leftState = left.EntityManager.GetComponentData(left.Singleton); var rightState = right.EntityManager.GetComponentData(right.Singleton); Assert.That(leftState.Tick, Is.EqualTo(10_000)); Assert.That(leftState.StateHash, Is.EqualTo(rightState.StateHash)); } [Test] public void SnapshotRestore_ContinuesWithTheSameHash() { using var source = new Demo2EcsWorld(42, Demo2BuiltInContent.BuildRules()); source.SetPhase(Demo2Phase.Production); for (var tick = 0; tick < 500; tick++) source.Tick(); var snapshot = source.CaptureSnapshot(new[] { "production.dense-ammo" }); using var restored = new Demo2EcsWorld(999, Demo2BuiltInContent.BuildRules()); restored.RestoreSnapshot(snapshot); for (var tick = 0; tick < 200; tick++) { source.Tick(); restored.Tick(); } var sourceState = source.EntityManager.GetComponentData(source.Singleton); var restoredState = restored.EntityManager.GetComponentData(restored.Singleton); Assert.That(restoredState.StateHash, Is.EqualTo(sourceState.StateHash)); } [Test] public void BuildCommands_AreIdempotentAndRemovalRefundsBeforeEntityDestruction() { using var service = new Demo2GameService(); service.StartNewGame(17); var command = new Demo2BuildCommand { Sequence = 1, IdempotencyToken = "test-place", PlayerId = "test", Kind = Demo2MachineKind.Conveyor, X = 1, Y = 1 }; var accepted = service.SubmitBuild(command); var duplicate = service.SubmitBuild(command); var removed = service.BuildLocal(Demo2MachineKind.Conveyor, 1, 1, remove: true); Assert.That(accepted.Accepted, Is.True); Assert.That(duplicate.Accepted && duplicate.Duplicate, Is.True); Assert.That(removed.Accepted, Is.True); Assert.That(removed.Refund, Is.EqualTo(5)); } [Test] public void MultipleTurrets_LoadWithoutSingletonExceptions() { using var world = new Demo2EcsWorld(31); var manager = world.EntityManager; var state = manager.GetComponentData(world.Singleton); var turret = Demo2EcsFactory.CreateMachine(manager, ++state.NextStableId, Demo2MachineKind.Turret, 22, 10, 0, 0); manager.AddComponentData(turret, new Demo2TurretAmmo()); Demo2EcsFactory.CreateMaterial(manager, ++state.NextStableId, Demo2MaterialKind.Ammo, 0, 19, 0, 9); manager.SetComponentData(world.Singleton, state); world.SetPhase(Demo2Phase.Production); world.Tick(); var query = manager.CreateEntityQuery(typeof(Demo2TurretAmmo)); using var ammo = query.ToComponentDataArray(Allocator.Temp); long total = 0; for (var i = 0; i < ammo.Length; i++) total += ammo[i].Value; Assert.That(total, Is.EqualTo(9)); } [Test] [Category("Demo2Performance")] public void StressTick_HandlesTenThousandItemsAndOneThousandMachines() { using var world = new Demo2EcsWorld(77); var manager = world.EntityManager; var state = manager.GetComponentData(world.Singleton); for (var i = 0; i < 1_000; i++) Demo2EcsFactory.CreateMachine(manager, ++state.NextStableId, Demo2MachineKind.Conveyor, i % Demo2Protocol.GridWidth, (i / Demo2Protocol.GridWidth) % 10, 0, 0); for (var i = 0; i < 10_000; i++) Demo2EcsFactory.CreateMaterial(manager, ++state.NextStableId, Demo2MaterialKind.IronOre, i % Demo2Protocol.GridWidth, 19, 0, 1); manager.SetComponentData(world.Singleton, state); world.SetPhase(Demo2Phase.Production); var before = System.GC.GetAllocatedBytesForCurrentThread(); var stopwatch = Stopwatch.StartNew(); world.Tick(); stopwatch.Stop(); var allocated = System.GC.GetAllocatedBytesForCurrentThread() - before; using var entities = manager.GetAllEntities(Allocator.Temp); UnityEngine.Debug.Log($"[Demo2 Benchmark] entities={entities.Length} tickMs={stopwatch.Elapsed.TotalMilliseconds:0.00} managedBytes={allocated}"); Assert.That(entities.Length, Is.GreaterThanOrEqualTo(11_000)); Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(10_000)); Assert.That(allocated, Is.LessThanOrEqualTo(4096)); } } }