diff --git a/GFramework.Core.Tests/ecs/EcsBasicTests.cs b/GFramework.Core.Tests/ecs/EcsBasicTests.cs new file mode 100644 index 0000000..ca7d030 --- /dev/null +++ b/GFramework.Core.Tests/ecs/EcsBasicTests.cs @@ -0,0 +1,223 @@ +using System.Reflection; +using Arch.Core; +using GFramework.Core.Abstractions.ecs; +using GFramework.Core.architecture; +using GFramework.Core.ecs; +using GFramework.Core.ecs.components; +using GFramework.Core.ecs.systems; +using GFramework.Core.ioc; +using GFramework.Core.logging; +using NUnit.Framework; + +namespace GFramework.Core.Tests.ecs; + +/// +/// ECS基础功能测试类,用于验证ECS系统的核心功能。 +/// 包括实体创建、组件设置、系统更新、实体销毁等基本操作。 +/// +[TestFixture] +public class EcsBasicTests +{ + /// + /// 测试初始化方法,在每个测试方法执行前运行。 + /// 负责初始化日志工厂、依赖注入容器和架构上下文。 + /// + [SetUp] + public void Setup() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + + _container = new MicrosoftDiContainer(); + var loggerField = typeof(MicrosoftDiContainer).GetField("_logger", + BindingFlags.NonPublic | BindingFlags.Instance); + loggerField?.SetValue(_container, + LoggerFactoryResolver.Provider.CreateLogger(nameof(EcsBasicTests))); + + _context = new ArchitectureContext(_container); + } + + /// + /// 测试清理方法,在每个测试方法执行后运行。 + /// 负责释放ECS世界资源并清空容器和上下文。 + /// + [TearDown] + public void TearDown() + { + _ecsWorld?.Dispose(); + _ecsWorld = null; + _container?.Clear(); + _context = null; + } + + private MicrosoftDiContainer? _container; + private ArchitectureContext? _context; + private EcsWorld? _ecsWorld; + + /// + /// 初始化ECS系统并注册指定类型的系统实例。 + /// + /// 需要注册的系统类型数组 + private void InitializeEcsWithSystems(params Type[] systemTypes) + { + _ecsWorld = new EcsWorld(); + _container!.Register(_ecsWorld); + _container.Register(_ecsWorld as IEcsWorld); + + var systems = new List(); + foreach (var systemType in systemTypes) + { + var system = (IEcsSystem)Activator.CreateInstance(systemType)!; + system.SetContext(_context!); + system.Init(); + systems.Add(system); + _container.RegisterPlurality(system); + } + + _container.Register(systems as IReadOnlyList); + } + + /// + /// 测试ECS初始化功能,验证是否能正确创建EcsWorld实例。 + /// + [Test] + public void Test_01_InitializeEcs_Should_Create_EcsWorld() + { + _context!.InitializeEcs(); + var ecsWorld = _context.GetEcsWorld(); + + Assert.That(ecsWorld, Is.Not.Null, "EcsWorld should be created"); + Assert.That(ecsWorld.EntityCount, Is.EqualTo(0), "Initial entity count should be 0"); + } + + /// + /// 测试实体创建功能,验证能否成功创建带有指定组件的实体。 + /// + [Test] + public void Test_02_CreateEntity_Should_Work() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1), "Entity count should be 1"); + Assert.That(_ecsWorld.IsAlive(entity), Is.True, "Entity should be alive"); + } + + /// + /// 测试组件设置功能,验证能否正确存储和获取组件数据。 + /// + [Test] + public void Test_03_SetComponent_Should_Store_Data() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position)); + var world = _ecsWorld.InternalWorld; + + world.Set(entity, new Position(10, 20)); + + Assert.That(world.Has(entity), Is.True, "Entity should have Position component"); + ref var pos = ref world.Get(entity); + Assert.That(pos.X, Is.EqualTo(10), "Position.X should be 10"); + Assert.That(pos.Y, Is.EqualTo(20), "Position.Y should be 20"); + } + + /// + /// 测试移动系统功能,验证系统能否正确更新实体位置。 + /// + [Test] + public void Test_04_MovementSystem_Should_Update_Position() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity)); + + var world = _ecsWorld.InternalWorld; + world.Set(entity, new Position(0, 0)); + world.Set(entity, new Velocity(10, 5)); + + var systems = _container!.Get>(); + Assert.That(systems, Is.Not.Null); + Assert.That(systems!.Count, Is.GreaterThan(0)); + + var movementSystem = systems.First(s => s is MovementSystem) as MovementSystem; + Assert.That(movementSystem, Is.Not.Null); + + movementSystem!.Update(1.0f); + + ref var pos = ref world.Get(entity); + Assert.That(pos.X, Is.EqualTo(10).Within(0.001f), "X position should be 10"); + Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f), "Y position should be 5"); + } + + /// + /// 测试实体销毁功能,验证能否正确销毁实体并更新实体计数。 + /// + [Test] + public void Test_05_DestroyEntity_Should_Work() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position)); + + _ecsWorld.DestroyEntity(entity); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0), "Entity count should be 0"); + Assert.That(_ecsWorld.IsAlive(entity), Is.False, "Entity should not be alive"); + } + + /// + /// 测试世界清理功能,验证能否清除所有实体。 + /// + [Test] + public void Test_06_ClearWorld_Should_Remove_All_Entities() + { + _ecsWorld = new EcsWorld(); + for (int i = 0; i < 10; i++) + { + _ecsWorld.CreateEntity(typeof(Position)); + } + + _ecsWorld.Clear(); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0), "Entity count should be 0 after clear"); + } + + /// + /// 测试多个实体的批量更新功能,验证系统能否正确处理多个实体的更新。 + /// + [Test] + public void Test_07_Multiple_Entities_Should_Update_Correctly() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var world = _ecsWorld!.InternalWorld; + var entities = new Entity[10]; + + for (var i = 0; i < 10; i++) + { + entities[i] = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + world.Set(entities[i], new Position(0, 0)); + world.Set(entities[i], new Velocity(i, i * 2)); + } + + var systems = _container!.Get>(); + var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem; + + movementSystem!.Update(1.0f); + + for (int i = 0; i < 10; i++) + { + ref var pos = ref world.Get(entities[i]); + Assert.That(pos.X, Is.EqualTo(i).Within(0.001f), $"Entity {i} X position should be {i}"); + Assert.That(pos.Y, Is.EqualTo(i * 2).Within(0.001f), $"Entity {i} Y position should be {i * 2}"); + } + } + + /// + /// 测试未初始化情况下获取ECS世界的异常处理。 + /// + [Test] + public void Test_08_GetEcsWorld_Without_Initialize_Should_Throw() + { + Assert.Throws(() => { _context!.GetEcsWorld(); }, + "Getting EcsWorld without initialization should throw"); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs b/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs new file mode 100644 index 0000000..dde6c2d --- /dev/null +++ b/GFramework.Core.Tests/ecs/EcsIntegrationTests.cs @@ -0,0 +1,352 @@ +using System.Reflection; +using Arch.Core; +using GFramework.Core.Abstractions.ecs; +using GFramework.Core.architecture; +using GFramework.Core.ecs; +using GFramework.Core.ecs.components; +using GFramework.Core.ecs.systems; +using GFramework.Core.ioc; +using GFramework.Core.logging; +using NUnit.Framework; + +namespace GFramework.Core.Tests.ecs; + +/// +/// ECS集成测试类,用于验证ECS系统的整体功能和性能表现。 +/// 包括实体管理、组件操作、系统调度、优先级控制以及性能基准测试。 +/// +[TestFixture] +public class EcsIntegrationTests +{ + /// + /// 测试初始化方法,在每个测试方法执行前运行。 + /// 负责初始化日志工厂、依赖注入容器和架构上下文。 + /// + [SetUp] + public void Setup() + { + LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); + + _container = new MicrosoftDiContainer(); + var loggerField = typeof(MicrosoftDiContainer).GetField("_logger", + BindingFlags.NonPublic | BindingFlags.Instance); + loggerField?.SetValue(_container, + LoggerFactoryResolver.Provider.CreateLogger(nameof(EcsIntegrationTests))); + + _context = new ArchitectureContext(_container); + } + + /// + /// 测试清理方法,在每个测试方法执行后运行。 + /// 负责释放ECS世界资源并清空容器和上下文。 + /// + [TearDown] + public void TearDown() + { + _ecsWorld?.Dispose(); + _ecsWorld = null; + _container?.Clear(); + _context = null; + } + + private MicrosoftDiContainer? _container; + private ArchitectureContext? _context; + private EcsWorld? _ecsWorld; + + /// + /// 初始化ECS系统并注册指定类型的系统实例。 + /// + /// 需要注册的系统类型数组 + private void InitializeEcsWithSystems(params Type[] systemTypes) + { + _ecsWorld = new EcsWorld(); + _container!.Register(_ecsWorld); + _container.Register(_ecsWorld as IEcsWorld); + + var systems = new List(); + foreach (var systemType in systemTypes) + { + var system = (IEcsSystem)Activator.CreateInstance(systemType)!; + system.SetContext(_context!); + system.Init(); + systems.Add(system); + _container.RegisterPlurality(system); + } + + _container.Register(systems as IReadOnlyList); + } + + /// + /// 测试ECS初始化功能,验证是否能正确创建EcsWorld实例。 + /// + [Test] + public void InitializeEcs_Should_Create_EcsWorld() + { + _context!.InitializeEcs(); + var ecsWorld = _context.GetEcsWorld(); + + Assert.That(ecsWorld, Is.Not.Null); + Assert.That(ecsWorld.EntityCount, Is.EqualTo(0)); + } + + /// + /// 测试实体创建功能,验证创建实体后实体计数是否正确增加。 + /// + [Test] + public void CreateEntity_Should_Increase_EntityCount() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1)); + Assert.That(_ecsWorld.IsAlive(entity), Is.True); + } + + /// + /// 测试实体销毁功能,验证销毁实体后实体计数是否正确减少。 + /// + [Test] + public void DestroyEntity_Should_Decrease_EntityCount() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position)); + + _ecsWorld.DestroyEntity(entity); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0)); + Assert.That(_ecsWorld.IsAlive(entity), Is.False); + } + + /// + /// 测试组件设置功能,验证能否正确存储和获取组件数据。 + /// + [Test] + public void SetComponent_Should_Store_ComponentData() + { + _ecsWorld = new EcsWorld(); + var entity = _ecsWorld.CreateEntity(typeof(Position)); + + var world = _ecsWorld.InternalWorld; + world.Set(entity, new Position(10, 20)); + + Assert.That(world.Has(entity), Is.True); + ref var pos = ref world.Get(entity); + Assert.That(pos.X, Is.EqualTo(10)); + Assert.That(pos.Y, Is.EqualTo(20)); + } + + /// + /// 测试世界清理功能,验证能否清除所有实体。 + /// + [Test] + public void ClearWorld_Should_Remove_All_Entities() + { + _ecsWorld = new EcsWorld(); + for (int i = 0; i < 10; i++) + { + _ecsWorld.CreateEntity(typeof(Position)); + } + + _ecsWorld.Clear(); + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(0)); + } + + /// + /// 测试ECS系统注册功能,验证系统能否正确添加到运行器中。 + /// + [Test] + public void RegisterEcsSystem_Should_Add_System_To_Runner() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var systems = _container!.Get>(); + Assert.That(systems, Is.Not.Null); + Assert.That(systems!.Count, Is.EqualTo(1)); + Assert.That(systems[0], Is.InstanceOf()); + } + + /// + /// 测试移动系统功能,验证系统能否正确更新单个实体的位置。 + /// + [Test] + public void MovementSystem_Should_Update_Position() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var entity = _ecsWorld!.CreateEntity(typeof(Position), typeof(Velocity)); + + var world = _ecsWorld.InternalWorld; + world.Set(entity, new Position(0, 0)); + world.Set(entity, new Velocity(10, 5)); + + var systems = _container!.Get>(); + var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem; + + movementSystem!.Update(1.0f); + + ref var pos = ref world.Get(entity); + Assert.That(pos.X, Is.EqualTo(10).Within(0.001f)); + Assert.That(pos.Y, Is.EqualTo(5).Within(0.001f)); + } + + /// + /// 测试移动系统功能,验证系统能否正确批量更新多个实体的位置。 + /// + [Test] + public void MovementSystem_Should_Update_Multiple_Entities() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var world = _ecsWorld!.InternalWorld; + var entities = new Entity[100]; + + for (var i = 0; i < 100; i++) + { + entities[i] = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + world.Set(entities[i], new Position(0, 0)); + world.Set(entities[i], new Velocity(i, i * 2)); + } + + var systems = _container!.Get>(); + var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem; + + movementSystem!.Update(0.5f); + + for (var i = 0; i < 100; i++) + { + ref var pos = ref world.Get(entities[i]); + Assert.That(pos.X, Is.EqualTo(i * 0.5f).Within(0.001f)); + Assert.That(pos.Y, Is.EqualTo(i * 2 * 0.5f).Within(0.001f)); + } + } + + /// + /// 测试ECS系统运行器的优先级调度功能,验证系统是否按优先级顺序执行。 + /// + [Test] + public void EcsSystemRunner_Should_Respect_Priority() + { + InitializeEcsWithSystems(typeof(LowPrioritySystem), typeof(HighPrioritySystem)); + + var systems = _container!.Get>(); + Assert.That(systems, Is.Not.Null); + Assert.That(systems!.Count, Is.EqualTo(2)); + + var sortedSystems = systems.OrderBy(s => s.Priority).ToList(); + Assert.That(sortedSystems[0], Is.InstanceOf()); + Assert.That(sortedSystems[1], Is.InstanceOf()); + } + + /// + /// 测试未初始化情况下获取ECS世界的异常处理。 + /// + [Test] + public void GetEcsWorld_Without_Initialize_Should_Throw() + { + Assert.Throws(() => { _context!.GetEcsWorld(); }); + } + + /// + /// 性能基准测试:验证更新10000个实体的性能表现。 + /// + [Test] + public void Performance_Test_10000_Entities() + { + InitializeEcsWithSystems(typeof(MovementSystem)); + + var world = _ecsWorld!.InternalWorld; + + for (int i = 0; i < 10000; i++) + { + var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + world.Set(entity, new Position(0, 0)); + world.Set(entity, new Velocity(1, 1)); + } + + var systems = _container!.Get>(); + var movementSystem = systems!.First(s => s is MovementSystem) as MovementSystem; + + var startTime = DateTime.UtcNow; + movementSystem!.Update(0.016f); + var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(10000)); + Assert.That(elapsed, Is.LessThan(100), $"Updating 10000 entities took: {elapsed}ms"); + } + + /// + /// 性能基准测试:验证创建1000个实体的性能表现。 + /// + [Test] + public void Performance_Test_1000_Entities_Creation() + { + _ecsWorld = new EcsWorld(); + var world = _ecsWorld.InternalWorld; + + var startTime = DateTime.UtcNow; + for (int i = 0; i < 1000; i++) + { + var entity = _ecsWorld.CreateEntity(typeof(Position), typeof(Velocity)); + world.Set(entity, new Position(0, 0)); + world.Set(entity, new Velocity(1, 1)); + } + + var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; + + Assert.That(_ecsWorld.EntityCount, Is.EqualTo(1000)); + Assert.That(elapsed, Is.LessThan(50), $"Creating 1000 entities took: {elapsed}ms"); + } +} + +/// +/// 高优先级系统示例,用于测试系统调度优先级功能。 +/// +public class HighPrioritySystem : EcsSystemBase +{ + /// + /// 获取系统优先级,数值越小优先级越高。 + /// + public override int Priority => -100; + + /// + /// ECS初始化回调方法。 + /// + protected override void OnEcsInit() + { + } + + /// + /// 系统更新方法。 + /// + /// 帧间隔时间 + public override void Update(float deltaTime) + { + } +} + +/// +/// 低优先级系统示例,用于测试系统调度优先级功能。 +/// +public class LowPrioritySystem : EcsSystemBase +{ + /// + /// 获取系统优先级,数值越大优先级越低。 + /// + public override int Priority => 100; + + /// + /// ECS初始化回调方法。 + /// + protected override void OnEcsInit() + { + } + + /// + /// 系统更新方法。 + /// + /// 帧间隔时间 + public override void Update(float deltaTime) + { + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/mediator/MediatorComprehensiveTests.cs b/GFramework.Core.Tests/mediator/MediatorComprehensiveTests.cs index 771334a..bf09075 100644 --- a/GFramework.Core.Tests/mediator/MediatorComprehensiveTests.cs +++ b/GFramework.Core.Tests/mediator/MediatorComprehensiveTests.cs @@ -214,9 +214,9 @@ public class MediatorComprehensiveTests var results = new List(); // 流应该在100ms后被取消(TaskCanceledException 继承自 OperationCanceledException) - Assert.ThrowsAsync(async () => + Assert.ThrowsAsync(async () => { - await foreach (var item in stream.WithCancellation(cts.Token)) + await foreach (var item in stream) { results.Add(item); } @@ -531,7 +531,7 @@ public sealed class TestValidatedCommandHandler : IRequestHandler