616 lines
21 KiB
TypeScript
616 lines
21 KiB
TypeScript
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);
|
||
});
|
||
});
|
||
}); |