Files
esengine/tests/ECS/Core/EventSystem.test.ts

616 lines
21 KiB
TypeScript
Raw Normal View History

import { TypeSafeEventSystem, GlobalEventSystem } from '../../../src/ECS/Core/EventSystem';
import { ECSEventType } from '../../../src/ECS/CoreEvents';
// 测试事件数据类型
interface TestCustomEvent {
playerId: number;
message: string;
timestamp: number;
}
interface PlayerLevelUpEvent {
playerId: number;
oldLevel: number;
newLevel: number;
experience: number;
}
interface EntityCreatedEvent {
entityId: number;
entityName: string;
componentCount: number;
}
describe('EventSystem - 事件系统测试', () => {
let eventSystem: TypeSafeEventSystem;
beforeEach(() => {
eventSystem = new TypeSafeEventSystem();
});
describe('基本事件功能', () => {
test('应该能够注册事件监听器', () => {
let eventReceived = false;
eventSystem.on('test:event', () => {
eventReceived = true;
});
eventSystem.emit('test:event', {});
expect(eventReceived).toBe(true);
});
test('应该能够传递事件数据', () => {
let receivedData: TestCustomEvent | null = null;
eventSystem.on('custom:test', (data: TestCustomEvent) => {
receivedData = data;
});
const testData: TestCustomEvent = {
playerId: 123,
message: 'Hello World',
timestamp: Date.now()
};
eventSystem.emit('custom:test', testData);
expect(receivedData).toEqual(testData);
});
test('应该能够移除事件监听器', () => {
let callCount = 0;
const handler = () => {
callCount++;
};
const listenerId = eventSystem.on('removable:event', handler);
eventSystem.emit('removable:event', {});
expect(callCount).toBe(1);
eventSystem.off('removable:event', listenerId);
eventSystem.emit('removable:event', {});
expect(callCount).toBe(1); // 应该保持不变
});
test('应该能够一次性监听事件', async () => {
let callCount = 0;
eventSystem.once('once:event', () => {
callCount++;
});
await eventSystem.emit('once:event', {});
await eventSystem.emit('once:event', {});
await eventSystem.emit('once:event', {});
expect(callCount).toBe(1); // 只应该被调用一次
});
test('应该能够处理多个监听器', () => {
const results: string[] = [];
eventSystem.on('multi:event', () => {
results.push('handler1');
});
eventSystem.on('multi:event', () => {
results.push('handler2');
});
eventSystem.on('multi:event', () => {
results.push('handler3');
});
eventSystem.emit('multi:event', {});
expect(results).toEqual(['handler1', 'handler2', 'handler3']);
});
});
describe('预定义ECS事件', () => {
test('应该能够监听实体创建事件', () => {
let entityCreatedData: any = null;
eventSystem.on(ECSEventType.ENTITY_CREATED, (data: any) => {
entityCreatedData = data;
});
const testData = {
entityId: 123,
entityName: 'TestEntity',
componentCount: 3
};
eventSystem.emit(ECSEventType.ENTITY_CREATED, testData);
expect(entityCreatedData).toEqual(testData);
});
test('应该能够监听实体销毁事件', () => {
let entityDestroyedData: any = null;
eventSystem.on(ECSEventType.ENTITY_DESTROYED, (data: any) => {
entityDestroyedData = data;
});
const testData = {
entityId: 456,
entityName: 'DestroyedEntity',
componentCount: 2
};
eventSystem.emit(ECSEventType.ENTITY_DESTROYED, testData);
expect(entityDestroyedData).toEqual(testData);
});
test('应该能够监听组件添加事件', () => {
let componentAddedData: any = null;
eventSystem.on(ECSEventType.COMPONENT_ADDED, (data: any) => {
componentAddedData = data;
});
const testData = {
entityId: 789,
componentType: 'PositionComponent',
componentData: { x: 10, y: 20 }
};
eventSystem.emit(ECSEventType.COMPONENT_ADDED, testData);
expect(componentAddedData).toEqual(testData);
});
test('应该能够监听组件移除事件', () => {
let componentRemovedData: any = null;
eventSystem.on(ECSEventType.COMPONENT_REMOVED, (data: any) => {
componentRemovedData = data;
});
const testData = {
entityId: 101112,
componentType: 'VelocityComponent',
componentData: { vx: 5, vy: -3 }
};
eventSystem.emit(ECSEventType.COMPONENT_REMOVED, testData);
expect(componentRemovedData).toEqual(testData);
});
test('应该能够监听系统添加事件', () => {
let systemAddedData: any = null;
eventSystem.on(ECSEventType.SYSTEM_ADDED, (data: any) => {
systemAddedData = data;
});
const testData = {
systemName: 'MovementSystem',
systemType: 'EntitySystem',
updateOrder: 1
};
eventSystem.emit(ECSEventType.SYSTEM_ADDED, testData);
expect(systemAddedData).toEqual(testData);
});
test('应该能够监听系统移除事件', () => {
let systemRemovedData: any = null;
eventSystem.on(ECSEventType.SYSTEM_REMOVED, (data: any) => {
systemRemovedData = data;
});
const testData = {
systemName: 'RenderSystem',
systemType: 'EntitySystem',
updateOrder: 2
};
eventSystem.emit(ECSEventType.SYSTEM_REMOVED, testData);
expect(systemRemovedData).toEqual(testData);
});
});
describe('事件优先级和执行顺序', () => {
test('应该按优先级顺序执行监听器', () => {
const executionOrder: string[] = [];
// 添加不同优先级的监听器
eventSystem.on('priority:event', () => {
executionOrder.push('normal');
});
eventSystem.on('priority:event', () => {
executionOrder.push('high');
}, { priority: 10 });
eventSystem.on('priority:event', () => {
executionOrder.push('low');
}, { priority: -10 });
eventSystem.emit('priority:event', {});
// 应该按照 high -> normal -> low 的顺序执行
expect(executionOrder).toEqual(['high', 'normal', 'low']);
});
test('相同优先级的监听器应该按注册顺序执行', () => {
const executionOrder: string[] = [];
eventSystem.on('order:event', () => {
executionOrder.push('first');
});
eventSystem.on('order:event', () => {
executionOrder.push('second');
});
eventSystem.on('order:event', () => {
executionOrder.push('third');
});
eventSystem.emit('order:event', {});
expect(executionOrder).toEqual(['first', 'second', 'third']);
});
});
describe('异步事件处理', () => {
test('应该能够处理异步事件监听器', async () => {
let asyncResult = '';
eventSystem.on('async:event', async (data: { message: string }) => {
await new Promise(resolve => setTimeout(resolve, 10));
asyncResult = data.message;
}, { async: true });
await eventSystem.emit('async:event', { message: 'async test' });
expect(asyncResult).toBe('async test');
});
test('应该能够等待所有异步监听器完成', async () => {
const results: string[] = [];
eventSystem.on('multi-async:event', async () => {
await new Promise(resolve => setTimeout(resolve, 20));
results.push('handler1');
}, { async: true });
eventSystem.on('multi-async:event', async () => {
await new Promise(resolve => setTimeout(resolve, 10));
results.push('handler2');
}, { async: true });
eventSystem.on('multi-async:event', async () => {
await new Promise(resolve => setTimeout(resolve, 5));
results.push('handler3');
}, { async: true });
await eventSystem.emit('multi-async:event', {});
// 所有异步处理器都应该完成
expect(results.length).toBe(3);
expect(results).toContain('handler1');
expect(results).toContain('handler2');
expect(results).toContain('handler3');
});
test('异步事件处理中的错误应该被正确处理', async () => {
let successHandlerCalled = false;
eventSystem.on('error:event', async () => {
throw new Error('Test async error');
}, { async: true });
eventSystem.on('error:event', async () => {
successHandlerCalled = true;
}, { async: true });
// emit方法应该内部处理异步错误不向外抛出
await expect(eventSystem.emit('error:event', {})).resolves.toBeUndefined();
// 成功的处理器应该被调用
expect(successHandlerCalled).toBe(true);
});
});
describe('事件验证和类型安全', () => {
test('应该能够验证事件数据类型', () => {
let validationPassed = false;
eventSystem.on('typed:event', (data: PlayerLevelUpEvent) => {
// TypeScript应该确保类型安全
expect(typeof data.playerId).toBe('number');
expect(typeof data.oldLevel).toBe('number');
expect(typeof data.newLevel).toBe('number');
expect(typeof data.experience).toBe('number');
validationPassed = true;
});
const levelUpData: PlayerLevelUpEvent = {
playerId: 123,
oldLevel: 5,
newLevel: 6,
experience: 1500
};
eventSystem.emit('typed:event', levelUpData);
expect(validationPassed).toBe(true);
});
test('应该能够处理复杂的事件数据结构', () => {
interface ComplexEvent {
metadata: {
timestamp: number;
source: string;
};
payload: {
entities: Array<{
id: number;
components: string[];
}>;
systems: string[];
};
}
let receivedEvent: ComplexEvent | null = null;
eventSystem.on('complex:event', (data: ComplexEvent) => {
receivedEvent = data;
});
const complexData: ComplexEvent = {
metadata: {
timestamp: Date.now(),
source: 'test'
},
payload: {
entities: [
{ id: 1, components: ['Position', 'Velocity'] },
{ id: 2, components: ['Health', 'Render'] }
],
systems: ['Movement', 'Render', 'Combat']
}
};
eventSystem.emit('complex:event', complexData);
expect(receivedEvent).toEqual(complexData);
});
});
describe('性能和内存管理', () => {
test('大量事件监听器应该有良好的性能', () => {
const listenerCount = 50; // 减少数量以避免超过限制
let callCount = 0;
// 注册大量监听器
for (let i = 0; i < listenerCount; i++) {
eventSystem.on('perf:event', () => {
callCount++;
});
}
const startTime = performance.now();
eventSystem.emit('perf:event', {});
const endTime = performance.now();
expect(callCount).toBe(listenerCount);
const duration = endTime - startTime;
expect(duration).toBeLessThan(100); // 应该在100ms内完成
console.log(`${listenerCount}个监听器的事件触发耗时: ${duration.toFixed(2)}ms`);
});
test('频繁的事件触发应该有良好的性能', () => {
let eventCount = 0;
eventSystem.on('frequent:event', () => {
eventCount++;
});
const emitCount = 10000;
const startTime = performance.now();
for (let i = 0; i < emitCount; i++) {
eventSystem.emit('frequent:event', { index: i });
}
const endTime = performance.now();
expect(eventCount).toBe(emitCount);
const duration = endTime - startTime;
expect(duration).toBeLessThan(200); // 应该在200ms内完成
console.log(`${emitCount}次事件触发耗时: ${duration.toFixed(2)}ms`);
});
test('移除监听器应该释放内存', () => {
const listenerIds: string[] = [];
// 添加大量监听器
for (let i = 0; i < 100; i++) {
const handler = () => {};
const id = eventSystem.on('memory:event', handler);
listenerIds.push(id);
}
// 触发事件以确保监听器正常工作
eventSystem.emit('memory:event', {});
// 移除所有监听器
listenerIds.forEach(id => {
eventSystem.off('memory:event', id);
});
// 再次触发事件,应该没有监听器被调用
let callCount = 0;
eventSystem.on('memory:event', () => {
callCount++;
});
eventSystem.emit('memory:event', {});
expect(callCount).toBe(1); // 只有新添加的监听器被调用
});
test('应该能够清理所有事件监听器', () => {
let callCount = 0;
eventSystem.on('cleanup:event1', () => callCount++);
eventSystem.on('cleanup:event2', () => callCount++);
eventSystem.on('cleanup:event3', () => callCount++);
// 清理所有监听器
eventSystem.clear();
// 触发事件,应该没有监听器被调用
eventSystem.emit('cleanup:event1', {});
eventSystem.emit('cleanup:event2', {});
eventSystem.emit('cleanup:event3', {});
expect(callCount).toBe(0);
});
});
describe('错误处理', () => {
test('监听器中的错误不应该影响其他监听器', () => {
let successHandlerCalled = false;
eventSystem.on('error:event', () => {
throw new Error('Test error in handler');
});
eventSystem.on('error:event', () => {
successHandlerCalled = true;
});
// 触发事件不应该抛出异常
expect(() => {
eventSystem.emit('error:event', {});
}).not.toThrow();
// 成功的处理器应该被调用
expect(successHandlerCalled).toBe(true);
});
test('应该能够处理监听器注册和移除中的边界情况', () => {
const handler = () => {};
// 移除不存在的监听器应该安全
expect(() => {
const result = eventSystem.off('nonexistent:event', 'non-existent-id');
expect(result).toBe(false);
}).not.toThrow();
// 重复添加相同的监听器应该安全
const id1 = eventSystem.on('duplicate:event', handler);
const id2 = eventSystem.on('duplicate:event', handler);
let callCount = 0;
eventSystem.on('duplicate:event', () => {
callCount++;
});
eventSystem.emit('duplicate:event', {});
// 所有监听器都应该被调用
expect(callCount).toBe(1); // 新添加的监听器被调用
});
test('触发不存在的事件应该安全', () => {
expect(() => {
eventSystem.emit('nonexistent:event', {});
}).not.toThrow();
});
});
describe('全局事件系统', () => {
test('全局事件系统应该能够跨实例通信', () => {
let receivedData: any = null;
GlobalEventSystem.on('global:test', (data) => {
receivedData = data;
});
const testData = { message: 'global event test' };
GlobalEventSystem.emit('global:test', testData);
expect(receivedData).toEqual(testData);
});
test('全局事件系统应该是全局实例', () => {
// GlobalEventSystem 是全局实例不需要getInstance
expect(GlobalEventSystem).toBeDefined();
expect(GlobalEventSystem).toBeInstanceOf(TypeSafeEventSystem);
});
test('全局事件系统应该能够与局部事件系统独立工作', () => {
let localCallCount = 0;
let globalCallCount = 0;
eventSystem.on('isolated:event', () => {
localCallCount++;
});
GlobalEventSystem.on('isolated:event', () => {
globalCallCount++;
});
// 触发局部事件
eventSystem.emit('isolated:event', {});
expect(localCallCount).toBe(1);
expect(globalCallCount).toBe(0);
// 触发全局事件
GlobalEventSystem.emit('isolated:event', {});
expect(localCallCount).toBe(1);
expect(globalCallCount).toBe(1);
});
});
describe('事件统计和调试', () => {
test('应该能够获取事件系统统计信息', () => {
// 添加一些监听器
eventSystem.on('stats:event1', () => {});
eventSystem.on('stats:event1', () => {});
eventSystem.on('stats:event2', () => {});
// 触发一些事件
eventSystem.emit('stats:event1', {});
eventSystem.emit('stats:event2', {});
eventSystem.emit('stats:event1', {});
const stats = eventSystem.getStats() as Map<string, any>;
expect(stats).toBeInstanceOf(Map);
expect(stats.size).toBe(2);
});
test('应该能够获取特定事件的统计信息', async () => {
eventSystem.on('specific:event', () => {});
eventSystem.on('specific:event', () => {});
await eventSystem.emit('specific:event', {});
await eventSystem.emit('specific:event', {});
await eventSystem.emit('specific:event', {});
const eventStats = eventSystem.getStats('specific:event') as any;
expect(eventStats.listenerCount).toBe(2);
expect(eventStats.triggerCount).toBe(3);
});
});
});