import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem'; import { Entity } from '../../../src/ECS/Entity'; import { Component } from '../../../src/ECS/Component'; import { Scene } from '../../../src/ECS/Scene'; import { Matcher } from '../../../src/ECS/Utils/Matcher'; import { GlobalEventBus } from '../../../src/ECS/Core/EventBus'; import { TypeSafeEventSystem } from '../../../src/ECS/Core/EventSystem'; // 测试组件 class TestComponent extends Component { public value: number = 0; constructor(...args: unknown[]) { super(); const [value = 0] = args as [number?]; this.value = value; } } // 测试事件 interface TestEvent { id: number; message: string; } // 具体的实体系统实现 class ConcreteEntitySystem extends EntitySystem { public processCallCount = 0; public eventHandlerCallCount = 0; constructor() { super(Matcher.all(TestComponent)); } protected override process(entities: readonly Entity[]): void { this.processCallCount++; } // 测试用的手动事件监听器 public addManualEventListener(): void { this.addEventListener('manual_event', (event: any) => { this.eventHandlerCallCount++; }); } // 测试用的移除事件监听器 public removeManualEventListener(): void { const handler = (event: any) => { this.eventHandlerCallCount++; }; const listenerRef = this.addEventListener('manual_event', handler); if (listenerRef) { this.removeEventListener('manual_event', listenerRef); } } // 公开测试方法 public testAddEventListener(eventType: string, handler: (event: any) => void): string | null { return this.addEventListener(eventType, handler); } public testRemoveEventListener(eventType: string, listenerRef: string): void { this.removeEventListener(eventType, listenerRef); } } // 没有装饰器的系统(用于对比测试) class PlainEntitySystem extends EntitySystem { public processCallCount = 0; public onDestroyCallCount = 0; constructor() { super(Matcher.all(TestComponent)); } protected override process(entities: readonly Entity[]): void { this.processCallCount++; } protected override onDestroy(): void { this.onDestroyCallCount++; } } describe('EntitySystem', () => { let scene: Scene; let system: ConcreteEntitySystem; let entity: Entity; beforeEach(() => { scene = new Scene(); system = new ConcreteEntitySystem(); entity = scene.createEntity('test_entity'); entity.addComponent(new TestComponent(10)); scene.addEntity(entity); scene.addSystem(system); }); afterEach(() => { // 清理场景 scene.removeSystem(system); }); describe('手动事件监听器管理', () => { it('应该能够手动添加事件监听器', () => { system.addManualEventListener(); // 发射手动事件 scene.eventSystem.emitSync('manual_event', { test: true }); // 验证手动事件监听器被调用 expect(system.eventHandlerCallCount).toBe(1); }); it('应该能够手动移除事件监听器', () => { const handler = jest.fn(); // 添加监听器 const listenerRef = system.testAddEventListener('manual_remove_event', handler); // 发射事件验证监听器工作 scene.eventSystem.emitSync('manual_remove_event', {}); expect(handler).toHaveBeenCalledTimes(1); // 移除监听器 if (listenerRef) { system.testRemoveEventListener('manual_remove_event', listenerRef); } // 再次发射事件 scene.eventSystem.emitSync('manual_remove_event', {}); // 验证监听器已被移除 expect(handler).toHaveBeenCalledTimes(1); }); it('当系统被销毁时,应该清理手动添加的事件监听器', () => { const handler = jest.fn(); // 添加手动监听器 system.testAddEventListener('destroy_test_event', handler); // 验证监听器工作 scene.eventSystem.emitSync('destroy_test_event', {}); expect(handler).toHaveBeenCalledTimes(1); // 销毁系统 scene.removeSystem(system); // 再次发射事件 scene.eventSystem.emitSync('destroy_test_event', {}); // 验证监听器已被清理 expect(handler).toHaveBeenCalledTimes(1); }); }); describe('生命周期管理', () => { it('应该按正确的顺序执行销毁流程', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 销毁系统 scene.removeSystem(plainSystem); // 验证用户的 onDestroy 被调用 expect(plainSystem.onDestroyCallCount).toBe(1); }); it('框架的 destroy 方法应该清理所有资源并调用用户回调', () => { const testEvent: TestEvent = { id: 3, message: 'destroy test' }; const handler = jest.fn(); // 添加手动监听器 system.testAddEventListener('destroy_order_test', handler); // 验证手动监听器工作 scene.eventSystem.emitSync('destroy_order_test', {}); expect(handler).toHaveBeenCalledTimes(1); // 直接调用框架的销毁方法 system.destroy(); // 重置计数器并验证监听器被清理 handler.mockClear(); scene.eventSystem.emitSync('destroy_order_test', {}); expect(handler).not.toHaveBeenCalled(); }); it('dispose 方法应该调用 onDestroy 回调', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 直接调用 dispose plainSystem.dispose(); // 验证 onDestroy 被调用 expect(plainSystem.onDestroyCallCount).toBe(1); }); it('多次调用 dispose 或 destroy 不应该重复调用 onDestroy', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 调用 dispose plainSystem.dispose(); expect(plainSystem.onDestroyCallCount).toBe(1); // 再次调用 dispose plainSystem.dispose(); expect(plainSystem.onDestroyCallCount).toBe(1); // 调用 destroy plainSystem.destroy(); expect(plainSystem.onDestroyCallCount).toBe(1); }); it('先调用 destroy 再调用 dispose 不应该重复调用 onDestroy', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 先调用 destroy plainSystem.destroy(); expect(plainSystem.onDestroyCallCount).toBe(1); // 再调用 dispose plainSystem.dispose(); expect(plainSystem.onDestroyCallCount).toBe(1); }); }); describe('错误处理', () => { it('当事件系统不可用时,应该安全地处理监听器注册', () => { // 创建没有事件系统的场景 const sceneWithoutEvents = new Scene(); (sceneWithoutEvents as any).eventSystem = null; const systemWithoutEvents = new ConcreteEntitySystem(); // 应该不会抛出错误 expect(() => { sceneWithoutEvents.addSystem(systemWithoutEvents); }).not.toThrow(); }); it('当移除不存在的监听器时,应该安全处理', () => { const nonExistentListenerRef = 'non_existent_listener_ref'; // 应该不会抛出错误 expect(() => { system.testRemoveEventListener('non_existent_event', nonExistentListenerRef); }).not.toThrow(); }); }); describe('日志器命名', () => { it('应该使用类名作为日志器名称', () => { const loggerName = (system as any).getLoggerName(); expect(loggerName).toBe('ConcreteEntitySystem'); }); }); describe('nothing 匹配器与系统', () => { class NothingSystem extends EntitySystem { public onBeginCallCount = 0; public processCallCount = 0; public onEndCallCount = 0; constructor() { super(Matcher.nothing()); } protected override onBegin(): void { this.onBeginCallCount++; } protected override process(entities: readonly Entity[]): void { this.processCallCount++; // nothing 匹配器不应该有任何实体 expect(entities).toHaveLength(0); } protected override onEnd(): void { this.onEndCallCount++; } } it('使用 nothing 匹配器的系统应该正常运行生命周期方法', () => { const nothingSystem = new NothingSystem(); scene.addSystem(nothingSystem); // 手动触发更新 nothingSystem.update(); nothingSystem.lateUpdate(); // 生命周期方法应该被调用 expect(nothingSystem.onBeginCallCount).toBe(1); expect(nothingSystem.processCallCount).toBe(1); expect(nothingSystem.onEndCallCount).toBe(1); // 清理 scene.removeSystem(nothingSystem); }); it('nothing 匹配器系统的 entities 应该为空', () => { const nothingSystem = new NothingSystem(); scene.addSystem(nothingSystem); // 即使场景中有实体,entities 也应该为空 expect(nothingSystem.entities).toHaveLength(0); scene.removeSystem(nothingSystem); }); }); describe('系统更新顺序', () => { it('setUpdateOrder 应该能够设置更新顺序', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); expect(plainSystem.updateOrder).toBe(0); plainSystem.setUpdateOrder(10); expect(plainSystem.updateOrder).toBe(10); plainSystem.updateOrder = 20; expect(plainSystem.updateOrder).toBe(20); scene.removeSystem(plainSystem); }); it('设置相同的 updateOrder 不应该触发重排序', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); plainSystem.setUpdateOrder(10); // 设置相同的值不应该有问题 plainSystem.setUpdateOrder(10); expect(plainSystem.updateOrder).toBe(10); scene.removeSystem(plainSystem); }); }); describe('系统启用/禁用', () => { it('禁用系统时不应该调用 process', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); plainSystem.enabled = false; plainSystem.update(); expect(plainSystem.processCallCount).toBe(0); scene.removeSystem(plainSystem); }); it('重新启用系统后应该正常调用 process', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); plainSystem.enabled = false; plainSystem.update(); expect(plainSystem.processCallCount).toBe(0); plainSystem.enabled = true; plainSystem.update(); expect(plainSystem.processCallCount).toBe(1); scene.removeSystem(plainSystem); }); }); describe('实体跟踪回调', () => { class TrackingSystem extends EntitySystem { public addedEntities: Entity[] = []; public removedEntities: Entity[] = []; constructor() { super(Matcher.all(TestComponent)); } protected override process(entities: readonly Entity[]): void { // 处理实体 } protected override onAdded(entity: Entity): void { this.addedEntities.push(entity); } protected override onRemoved(entity: Entity): void { this.removedEntities.push(entity); } } it('当实体添加组件后应该触发 onAdded', () => { const trackingSystem = new TrackingSystem(); scene.addSystem(trackingSystem); // 创建新实体并添加组件 const newEntity = scene.createEntity('new_entity'); newEntity.addComponent(new TestComponent(5)); scene.addEntity(newEntity); // 触发更新以检测变化 trackingSystem.update(); // 应该检测到新实体 expect(trackingSystem.addedEntities.length).toBeGreaterThan(0); scene.removeSystem(trackingSystem); }); it('当实体移除组件后应该触发 onRemoved', () => { const trackingSystem = new TrackingSystem(); scene.addSystem(trackingSystem); // 先触发更新来跟踪现有实体 trackingSystem.update(); // 移除组件 const comp = entity.getComponent(TestComponent); if (comp) { entity.removeComponent(comp); } // 再次更新 trackingSystem.update(); // 应该检测到实体被移除 expect(trackingSystem.removedEntities.length).toBeGreaterThan(0); scene.removeSystem(trackingSystem); }); }); describe('reset 方法', () => { it('reset 后系统应该可以重新初始化', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 调用 reset plainSystem.reset(); // 验证系统被重置 expect(plainSystem.scene).toBeNull(); }); it('已销毁的系统调用 reset 不应该执行任何操作', () => { const plainSystem = new PlainEntitySystem(); scene.addSystem(plainSystem); // 先销毁系统 plainSystem.destroy(); // reset 不应该有任何效果 plainSystem.reset(); // onDestroy 只应被调用一次 expect(plainSystem.onDestroyCallCount).toBe(1); }); }); describe('辅助方法', () => { class HelperSystem extends EntitySystem { constructor() { super(Matcher.all(TestComponent)); } protected override process(entities: readonly Entity[]): void {} // 暴露 protected 方法供测试 public testRequireComponent Component>( entity: Entity, componentType: T ): InstanceType { return this.requireComponent(entity, componentType) as InstanceType; } public testForEach( entities: readonly Entity[], processor: (entity: Entity, index: number) => void ): void { this.forEach(entities, processor); } public testFilterEntities( entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean ): Entity[] { return this.filterEntities(entities, predicate); } public testFindEntity( entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean ): Entity | undefined { return this.findEntity(entities, predicate); } public testSomeEntity( entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean ): boolean { return this.someEntity(entities, predicate); } public testEveryEntity( entities: readonly Entity[], predicate: (entity: Entity, index: number) => boolean ): boolean { return this.everyEntity(entities, predicate); } } let helperSystem: HelperSystem; beforeEach(() => { helperSystem = new HelperSystem(); scene.addSystem(helperSystem); }); afterEach(() => { scene.removeSystem(helperSystem); }); it('requireComponent 应该返回存在的组件', () => { const component = helperSystem.testRequireComponent(entity, TestComponent); expect(component).toBeDefined(); expect(component.value).toBe(10); }); it('requireComponent 应该在组件不存在时抛出错误', () => { class NonExistentComponent extends Component {} expect(() => { helperSystem.testRequireComponent(entity, NonExistentComponent); }).toThrow(); }); it('forEach 应该遍历所有实体', () => { const entities = [entity]; const visited: Entity[] = []; helperSystem.testForEach(entities, (e) => { visited.push(e); }); expect(visited).toHaveLength(1); expect(visited[0]).toBe(entity); }); it('filterEntities 应该正确过滤实体', () => { const entity2 = scene.createEntity('entity2'); entity2.addComponent(new TestComponent(20)); scene.addEntity(entity2); const entities = [entity, entity2]; const filtered = helperSystem.testFilterEntities(entities, (e) => { const comp = e.getComponent(TestComponent); return comp !== null && comp.value > 15; }); expect(filtered).toHaveLength(1); expect(filtered[0]).toBe(entity2); }); it('findEntity 应该返回第一个匹配的实体', () => { const entities = [entity]; const found = helperSystem.testFindEntity(entities, (e) => { const comp = e.getComponent(TestComponent); return comp !== null && comp.value === 10; }); expect(found).toBe(entity); }); it('findEntity 应该在未找到时返回 undefined', () => { const entities = [entity]; const found = helperSystem.testFindEntity(entities, () => false); expect(found).toBeUndefined(); }); it('someEntity 应该正确判断是否存在匹配实体', () => { const entities = [entity]; const hasMatch = helperSystem.testSomeEntity(entities, (e) => { const comp = e.getComponent(TestComponent); return comp !== null && comp.value === 10; }); expect(hasMatch).toBe(true); const noMatch = helperSystem.testSomeEntity(entities, () => false); expect(noMatch).toBe(false); }); it('everyEntity 应该正确判断是否所有实体都匹配', () => { const entities = [entity]; const allMatch = helperSystem.testEveryEntity(entities, (e) => { return e.getComponent(TestComponent) !== null; }); expect(allMatch).toBe(true); const notAllMatch = helperSystem.testEveryEntity(entities, () => false); expect(notAllMatch).toBe(false); }); }); });