覆盖querysystem/eventbus/componentstorage测试

This commit is contained in:
YHH
2025-07-29 09:08:31 +08:00
parent 608f5030b2
commit 2f71785add
4 changed files with 1391 additions and 1 deletions

View File

@@ -0,0 +1,639 @@
import {
ComponentRegistry,
ComponentStorage,
ComponentStorageManager,
ComponentType
} from '../../../src/ECS/Core/ComponentStorage';
import { Component } from '../../../src/ECS/Component';
// 测试组件类
class TestComponent extends Component {
constructor(public value: number = 0) {
super();
}
}
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class VelocityComponent extends Component {
constructor(public vx: number = 0, public vy: number = 0) {
super();
}
}
class HealthComponent extends Component {
constructor(public health: number = 100, public maxHealth: number = 100) {
super();
}
}
describe('ComponentRegistry - 组件注册表测试', () => {
beforeEach(() => {
// 重置注册表状态
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
(ComponentRegistry as any).nextBitIndex = 0;
});
describe('组件注册功能', () => {
test('应该能够注册组件类型', () => {
const bitIndex = ComponentRegistry.register(TestComponent);
expect(bitIndex).toBe(0);
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true);
});
test('重复注册相同组件应该返回相同的位索引', () => {
const bitIndex1 = ComponentRegistry.register(TestComponent);
const bitIndex2 = ComponentRegistry.register(TestComponent);
expect(bitIndex1).toBe(bitIndex2);
expect(bitIndex1).toBe(0);
});
test('应该能够注册多个组件类型', () => {
const bitIndex1 = ComponentRegistry.register(TestComponent);
const bitIndex2 = ComponentRegistry.register(PositionComponent);
const bitIndex3 = ComponentRegistry.register(VelocityComponent);
expect(bitIndex1).toBe(0);
expect(bitIndex2).toBe(1);
expect(bitIndex3).toBe(2);
});
test('应该能够检查组件是否已注册', () => {
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(false);
ComponentRegistry.register(TestComponent);
expect(ComponentRegistry.isRegistered(TestComponent)).toBe(true);
});
test('超过最大组件数量应该抛出错误', () => {
// 设置较小的最大组件数量用于测试
(ComponentRegistry as any).maxComponents = 3;
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
ComponentRegistry.register(VelocityComponent);
expect(() => {
ComponentRegistry.register(HealthComponent);
}).toThrow('Maximum number of component types (3) exceeded');
});
});
describe('位掩码功能', () => {
test('应该能够获取组件的位掩码', () => {
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
const mask1 = ComponentRegistry.getBitMask(TestComponent);
const mask2 = ComponentRegistry.getBitMask(PositionComponent);
expect(mask1).toBe(BigInt(1)); // 2^0
expect(mask2).toBe(BigInt(2)); // 2^1
});
test('应该能够获取组件的位索引', () => {
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
const index1 = ComponentRegistry.getBitIndex(TestComponent);
const index2 = ComponentRegistry.getBitIndex(PositionComponent);
expect(index1).toBe(0);
expect(index2).toBe(1);
});
test('获取未注册组件的位掩码应该抛出错误', () => {
expect(() => {
ComponentRegistry.getBitMask(TestComponent);
}).toThrow('Component type TestComponent is not registered');
});
test('获取未注册组件的位索引应该抛出错误', () => {
expect(() => {
ComponentRegistry.getBitIndex(TestComponent);
}).toThrow('Component type TestComponent is not registered');
});
});
describe('注册表管理', () => {
test('应该能够获取所有已注册的组件类型', () => {
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
const allTypes = ComponentRegistry.getAllRegisteredTypes();
expect(allTypes.size).toBe(2);
expect(allTypes.has(TestComponent)).toBe(true);
expect(allTypes.has(PositionComponent)).toBe(true);
expect(allTypes.get(TestComponent)).toBe(0);
expect(allTypes.get(PositionComponent)).toBe(1);
});
test('返回的注册表副本不应该影响原始数据', () => {
ComponentRegistry.register(TestComponent);
const allTypes = ComponentRegistry.getAllRegisteredTypes();
allTypes.set(PositionComponent, 999);
expect(ComponentRegistry.isRegistered(PositionComponent)).toBe(false);
});
});
});
describe('ComponentStorage - 组件存储器测试', () => {
let storage: ComponentStorage<TestComponent>;
beforeEach(() => {
// 重置注册表
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
(ComponentRegistry as any).nextBitIndex = 0;
storage = new ComponentStorage(TestComponent);
});
describe('基本存储功能', () => {
test('应该能够创建组件存储器', () => {
expect(storage).toBeInstanceOf(ComponentStorage);
expect(storage.size).toBe(0);
expect(storage.type).toBe(TestComponent);
});
test('应该能够添加组件', () => {
const component = new TestComponent(100);
storage.addComponent(1, component);
expect(storage.size).toBe(1);
expect(storage.hasComponent(1)).toBe(true);
expect(storage.getComponent(1)).toBe(component);
});
test('重复添加组件到同一实体应该抛出错误', () => {
const component1 = new TestComponent(100);
const component2 = new TestComponent(200);
storage.addComponent(1, component1);
expect(() => {
storage.addComponent(1, component2);
}).toThrow('Entity 1 already has component TestComponent');
});
test('应该能够获取组件', () => {
const component = new TestComponent(100);
storage.addComponent(1, component);
const retrieved = storage.getComponent(1);
expect(retrieved).toBe(component);
expect(retrieved!.value).toBe(100);
});
test('获取不存在的组件应该返回null', () => {
const retrieved = storage.getComponent(999);
expect(retrieved).toBeNull();
});
test('应该能够检查实体是否有组件', () => {
expect(storage.hasComponent(1)).toBe(false);
storage.addComponent(1, new TestComponent(100));
expect(storage.hasComponent(1)).toBe(true);
});
test('应该能够移除组件', () => {
const component = new TestComponent(100);
storage.addComponent(1, component);
const removed = storage.removeComponent(1);
expect(removed).toBe(component);
expect(storage.size).toBe(0);
expect(storage.hasComponent(1)).toBe(false);
expect(storage.getComponent(1)).toBeNull();
});
test('移除不存在的组件应该返回null', () => {
const removed = storage.removeComponent(999);
expect(removed).toBeNull();
});
});
describe('遍历和批量操作', () => {
test('应该能够遍历所有组件', () => {
const component1 = new TestComponent(100);
const component2 = new TestComponent(200);
const component3 = new TestComponent(300);
storage.addComponent(1, component1);
storage.addComponent(2, component2);
storage.addComponent(3, component3);
const results: Array<{component: TestComponent, entityId: number, index: number}> = [];
storage.forEach((component, entityId, index) => {
results.push({ component, entityId, index });
});
expect(results.length).toBe(3);
expect(results.find(r => r.entityId === 1)?.component).toBe(component1);
expect(results.find(r => r.entityId === 2)?.component).toBe(component2);
expect(results.find(r => r.entityId === 3)?.component).toBe(component3);
});
test('应该能够获取密集数组', () => {
const component1 = new TestComponent(100);
const component2 = new TestComponent(200);
storage.addComponent(1, component1);
storage.addComponent(2, component2);
const { components, entityIds } = storage.getDenseArray();
expect(components.length).toBe(2);
expect(entityIds.length).toBe(2);
expect(components).toContain(component1);
expect(components).toContain(component2);
expect(entityIds).toContain(1);
expect(entityIds).toContain(2);
});
test('应该能够清空所有组件', () => {
storage.addComponent(1, new TestComponent(100));
storage.addComponent(2, new TestComponent(200));
expect(storage.size).toBe(2);
storage.clear();
expect(storage.size).toBe(0);
expect(storage.hasComponent(1)).toBe(false);
expect(storage.hasComponent(2)).toBe(false);
});
});
describe('内存管理和优化', () => {
test('应该能够重用空闲索引', () => {
const component1 = new TestComponent(100);
const component2 = new TestComponent(200);
const component3 = new TestComponent(300);
// 添加三个组件
storage.addComponent(1, component1);
storage.addComponent(2, component2);
storage.addComponent(3, component3);
// 移除中间的组件
storage.removeComponent(2);
// 添加新组件应该重用空闲索引
const component4 = new TestComponent(400);
storage.addComponent(4, component4);
expect(storage.size).toBe(3);
expect(storage.getComponent(4)).toBe(component4);
});
test('应该能够压缩存储', () => {
// 添加多个组件
storage.addComponent(1, new TestComponent(100));
storage.addComponent(2, new TestComponent(200));
storage.addComponent(3, new TestComponent(300));
storage.addComponent(4, new TestComponent(400));
// 移除部分组件创建空洞
storage.removeComponent(2);
storage.removeComponent(3);
let stats = storage.getStats();
expect(stats.freeSlots).toBe(2);
expect(stats.fragmentation).toBeGreaterThan(0);
// 压缩存储
storage.compact();
stats = storage.getStats();
expect(stats.freeSlots).toBe(0);
expect(stats.fragmentation).toBe(0);
expect(storage.size).toBe(2);
expect(storage.hasComponent(1)).toBe(true);
expect(storage.hasComponent(4)).toBe(true);
});
test('没有空洞时压缩应该不做任何操作', () => {
storage.addComponent(1, new TestComponent(100));
storage.addComponent(2, new TestComponent(200));
const statsBefore = storage.getStats();
storage.compact();
const statsAfter = storage.getStats();
expect(statsBefore).toEqual(statsAfter);
});
test('应该能够获取存储统计信息', () => {
storage.addComponent(1, new TestComponent(100));
storage.addComponent(2, new TestComponent(200));
storage.addComponent(3, new TestComponent(300));
// 移除一个组件创建空洞
storage.removeComponent(2);
const stats = storage.getStats();
expect(stats.totalSlots).toBe(3);
expect(stats.usedSlots).toBe(2);
expect(stats.freeSlots).toBe(1);
expect(stats.fragmentation).toBeCloseTo(1/3);
});
});
describe('边界情况', () => {
test('空存储器的统计信息应该正确', () => {
const stats = storage.getStats();
expect(stats.totalSlots).toBe(0);
expect(stats.usedSlots).toBe(0);
expect(stats.freeSlots).toBe(0);
expect(stats.fragmentation).toBe(0);
});
test('遍历空存储器应该安全', () => {
let callCount = 0;
storage.forEach(() => { callCount++; });
expect(callCount).toBe(0);
});
test('获取空存储器的密集数组应该返回空数组', () => {
const { components, entityIds } = storage.getDenseArray();
expect(components.length).toBe(0);
expect(entityIds.length).toBe(0);
});
});
});
describe('ComponentStorageManager - 组件存储管理器测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
// 重置注册表
(ComponentRegistry as any).componentTypes = new Map<Function, number>();
(ComponentRegistry as any).nextBitIndex = 0;
manager = new ComponentStorageManager();
});
describe('存储器管理', () => {
test('应该能够创建组件存储管理器', () => {
expect(manager).toBeInstanceOf(ComponentStorageManager);
});
test('应该能够获取或创建组件存储器', () => {
const storage1 = manager.getStorage(TestComponent);
const storage2 = manager.getStorage(TestComponent);
expect(storage1).toBeInstanceOf(ComponentStorage);
expect(storage1).toBe(storage2); // 应该是同一个实例
});
test('不同组件类型应该有不同的存储器', () => {
const storage1 = manager.getStorage(TestComponent);
const storage2 = manager.getStorage(PositionComponent);
expect(storage1).not.toBe(storage2);
expect(storage1.type).toBe(TestComponent);
expect(storage2.type).toBe(PositionComponent);
});
});
describe('组件操作', () => {
test('应该能够添加组件', () => {
const component = new TestComponent(100);
manager.addComponent(1, component);
expect(manager.hasComponent(1, TestComponent)).toBe(true);
expect(manager.getComponent(1, TestComponent)).toBe(component);
});
test('应该能够获取组件', () => {
const testComponent = new TestComponent(100);
const positionComponent = new PositionComponent(10, 20);
manager.addComponent(1, testComponent);
manager.addComponent(1, positionComponent);
expect(manager.getComponent(1, TestComponent)).toBe(testComponent);
expect(manager.getComponent(1, PositionComponent)).toBe(positionComponent);
});
test('获取不存在的组件应该返回null', () => {
const result = manager.getComponent(999, TestComponent);
expect(result).toBeNull();
});
test('应该能够检查实体是否有组件', () => {
expect(manager.hasComponent(1, TestComponent)).toBe(false);
manager.addComponent(1, new TestComponent(100));
expect(manager.hasComponent(1, TestComponent)).toBe(true);
});
test('应该能够移除组件', () => {
const component = new TestComponent(100);
manager.addComponent(1, component);
const removed = manager.removeComponent(1, TestComponent);
expect(removed).toBe(component);
expect(manager.hasComponent(1, TestComponent)).toBe(false);
});
test('移除不存在的组件应该返回null', () => {
const removed = manager.removeComponent(999, TestComponent);
expect(removed).toBeNull();
});
test('应该能够移除实体的所有组件', () => {
manager.addComponent(1, new TestComponent(100));
manager.addComponent(1, new PositionComponent(10, 20));
manager.addComponent(1, new VelocityComponent(1, 2));
expect(manager.hasComponent(1, TestComponent)).toBe(true);
expect(manager.hasComponent(1, PositionComponent)).toBe(true);
expect(manager.hasComponent(1, VelocityComponent)).toBe(true);
manager.removeAllComponents(1);
expect(manager.hasComponent(1, TestComponent)).toBe(false);
expect(manager.hasComponent(1, PositionComponent)).toBe(false);
expect(manager.hasComponent(1, VelocityComponent)).toBe(false);
});
});
describe('位掩码功能', () => {
test('应该能够获取实体的组件位掩码', () => {
// 确保组件已注册
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
ComponentRegistry.register(VelocityComponent);
manager.addComponent(1, new TestComponent(100));
manager.addComponent(1, new PositionComponent(10, 20));
const mask = manager.getComponentMask(1);
// 应该包含TestComponent(位0)和PositionComponent(位1)的掩码
const expectedMask = BigInt(1) | BigInt(2); // 0b11
expect(mask).toBe(expectedMask);
});
test('没有组件的实体应该有零掩码', () => {
const mask = manager.getComponentMask(999);
expect(mask).toBe(BigInt(0));
});
test('添加和移除组件应该更新掩码', () => {
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
manager.addComponent(1, new TestComponent(100));
let mask = manager.getComponentMask(1);
expect(mask).toBe(BigInt(1));
manager.addComponent(1, new PositionComponent(10, 20));
mask = manager.getComponentMask(1);
expect(mask).toBe(BigInt(3)); // 0b11
manager.removeComponent(1, TestComponent);
mask = manager.getComponentMask(1);
expect(mask).toBe(BigInt(2)); // 0b10
});
});
describe('管理器级别操作', () => {
test('应该能够压缩所有存储器', () => {
manager.addComponent(1, new TestComponent(100));
manager.addComponent(2, new TestComponent(200));
manager.addComponent(3, new TestComponent(300));
manager.addComponent(1, new PositionComponent(10, 20));
manager.addComponent(2, new PositionComponent(30, 40));
// 移除部分组件创建空洞
manager.removeComponent(2, TestComponent);
manager.removeComponent(1, PositionComponent);
expect(() => {
manager.compactAll();
}).not.toThrow();
});
test('应该能够获取所有存储器的统计信息', () => {
manager.addComponent(1, new TestComponent(100));
manager.addComponent(2, new TestComponent(200));
manager.addComponent(1, new PositionComponent(10, 20));
const allStats = manager.getAllStats();
expect(allStats).toBeInstanceOf(Map);
expect(allStats.size).toBe(2);
expect(allStats.has('TestComponent')).toBe(true);
expect(allStats.has('PositionComponent')).toBe(true);
const testStats = allStats.get('TestComponent');
expect(testStats.usedSlots).toBe(2);
const positionStats = allStats.get('PositionComponent');
expect(positionStats.usedSlots).toBe(1);
});
test('应该能够清空所有存储器', () => {
manager.addComponent(1, new TestComponent(100));
manager.addComponent(2, new PositionComponent(10, 20));
manager.addComponent(3, new VelocityComponent(1, 2));
expect(manager.hasComponent(1, TestComponent)).toBe(true);
expect(manager.hasComponent(2, PositionComponent)).toBe(true);
expect(manager.hasComponent(3, VelocityComponent)).toBe(true);
manager.clear();
expect(manager.hasComponent(1, TestComponent)).toBe(false);
expect(manager.hasComponent(2, PositionComponent)).toBe(false);
expect(manager.hasComponent(3, VelocityComponent)).toBe(false);
});
});
describe('边界情况和错误处理', () => {
test('对不存在存储器的操作应该安全处理', () => {
expect(manager.getComponent(1, TestComponent)).toBeNull();
expect(manager.hasComponent(1, TestComponent)).toBe(false);
expect(manager.removeComponent(1, TestComponent)).toBeNull();
});
test('移除所有组件对空实体应该安全', () => {
expect(() => {
manager.removeAllComponents(999);
}).not.toThrow();
});
test('统计信息应该处理未知组件类型', () => {
// 创建一个匿名组件类来测试未知类型处理
const AnonymousComponent = class extends Component {};
manager.addComponent(1, new AnonymousComponent());
const stats = manager.getAllStats();
// 检查是否有任何统计条目(匿名类可能显示为空字符串或其他名称)
expect(stats.size).toBeGreaterThan(0);
});
test('多次清空应该安全', () => {
manager.addComponent(1, new TestComponent(100));
manager.clear();
manager.clear(); // 第二次清空应该安全
expect(manager.hasComponent(1, TestComponent)).toBe(false);
});
});
describe('性能和内存测试', () => {
test('大量组件操作应该高效', () => {
const entityCount = 1000;
// 添加大量组件
for (let i = 1; i <= entityCount; i++) {
manager.addComponent(i, new TestComponent(i));
if (i % 2 === 0) {
manager.addComponent(i, new PositionComponent(i, i));
}
}
// 验证添加成功
expect(manager.hasComponent(1, TestComponent)).toBe(true);
expect(manager.hasComponent(500, TestComponent)).toBe(true);
expect(manager.hasComponent(2, PositionComponent)).toBe(true);
expect(manager.hasComponent(1, PositionComponent)).toBe(false);
// 移除部分组件
for (let i = 1; i <= entityCount; i += 3) {
manager.removeComponent(i, TestComponent);
}
// 验证移除成功
expect(manager.hasComponent(1, TestComponent)).toBe(false);
expect(manager.hasComponent(2, TestComponent)).toBe(true);
const stats = manager.getAllStats();
expect(stats.get('TestComponent').usedSlots).toBeLessThan(entityCount);
});
});
});

View File

@@ -0,0 +1,529 @@
import { EventBus, GlobalEventBus, EventHandler, AsyncEventHandler } from '../../../src/ECS/Core/EventBus';
import { IEventListenerConfig, IEventStats } from '../../../src/Types';
import { ECSEventType, EventPriority } from '../../../src/ECS/CoreEvents';
// 测试数据接口
interface TestEventData {
message: string;
value: number;
}
interface MockEntityData {
entityId: number;
timestamp: number;
eventId?: string;
source?: string;
}
interface MockComponentData {
entityId: number;
componentType: string;
timestamp: number;
eventId?: string;
source?: string;
}
describe('EventBus - 事件总线测试', () => {
let eventBus: EventBus;
beforeEach(() => {
eventBus = new EventBus(false);
});
afterEach(() => {
eventBus.clear();
});
describe('基本事件功能', () => {
test('应该能够创建事件总线', () => {
expect(eventBus).toBeInstanceOf(EventBus);
});
test('应该能够发射和监听同步事件', () => {
let receivedData: TestEventData | null = null;
const listenerId = eventBus.on<TestEventData>('test:event', (data) => {
receivedData = data;
});
const testData: TestEventData = { message: 'hello', value: 42 };
eventBus.emit('test:event', testData);
expect(receivedData).not.toBeNull();
expect(receivedData!.message).toBe('hello');
expect(receivedData!.value).toBe(42);
expect(typeof listenerId).toBe('string');
});
test('应该能够发射和监听异步事件', async () => {
let receivedData: TestEventData | null = null;
eventBus.onAsync<TestEventData>('async:event', async (data) => {
receivedData = data;
});
const testData: TestEventData = { message: 'async hello', value: 100 };
await eventBus.emitAsync('async:event', testData);
expect(receivedData).not.toBeNull();
expect(receivedData!.message).toBe('async hello');
expect(receivedData!.value).toBe(100);
});
test('应该能够一次性监听事件', () => {
let callCount = 0;
eventBus.once<TestEventData>('once:event', () => {
callCount++;
});
eventBus.emit('once:event', { message: 'first', value: 1 });
eventBus.emit('once:event', { message: 'second', value: 2 });
expect(callCount).toBe(1);
});
test('应该能够移除事件监听器', () => {
let callCount = 0;
const listenerId = eventBus.on<TestEventData>('removable:event', () => {
callCount++;
});
eventBus.emit('removable:event', { message: 'test', value: 1 });
expect(callCount).toBe(1);
const removed = eventBus.off('removable:event', listenerId);
expect(removed).toBe(true);
eventBus.emit('removable:event', { message: 'test', value: 2 });
expect(callCount).toBe(1); // 应该没有增加
});
test('应该能够移除所有事件监听器', () => {
let callCount1 = 0;
let callCount2 = 0;
eventBus.on<TestEventData>('multi:event', () => { callCount1++; });
eventBus.on<TestEventData>('multi:event', () => { callCount2++; });
eventBus.emit('multi:event', { message: 'test', value: 1 });
expect(callCount1).toBe(1);
expect(callCount2).toBe(1);
eventBus.offAll('multi:event');
eventBus.emit('multi:event', { message: 'test', value: 2 });
expect(callCount1).toBe(1);
expect(callCount2).toBe(1);
});
});
describe('事件配置和优先级', () => {
test('应该能够使用事件监听器配置', () => {
let receivedData: TestEventData | null = null;
const config: IEventListenerConfig = {
once: false,
priority: EventPriority.HIGH,
async: false
};
eventBus.on<TestEventData>('config:event', (data) => {
receivedData = data;
}, config);
eventBus.emit('config:event', { message: 'configured', value: 99 });
expect(receivedData).not.toBeNull();
expect(receivedData!.message).toBe('configured');
});
test('应该能够检查事件是否有监听器', () => {
expect(eventBus.hasListeners('nonexistent:event')).toBe(false);
eventBus.on('existing:event', () => {});
expect(eventBus.hasListeners('existing:event')).toBe(true);
});
test('应该能够获取监听器数量', () => {
expect(eventBus.getListenerCount('count:event')).toBe(0);
eventBus.on('count:event', () => {});
eventBus.on('count:event', () => {});
expect(eventBus.getListenerCount('count:event')).toBe(2);
});
});
describe('系统配置和管理', () => {
test('应该能够启用和禁用事件系统', () => {
let callCount = 0;
eventBus.on('disable:event', () => { callCount++; });
eventBus.emit('disable:event', { message: 'enabled', value: 1 });
expect(callCount).toBe(1);
eventBus.setEnabled(false);
eventBus.emit('disable:event', { message: 'disabled', value: 2 });
expect(callCount).toBe(1); // 应该没有增加
eventBus.setEnabled(true);
eventBus.emit('disable:event', { message: 'enabled again', value: 3 });
expect(callCount).toBe(2);
});
test('应该能够设置调试模式', () => {
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
eventBus.setDebugMode(true);
eventBus.on('debug:event', () => {});
eventBus.emit('debug:event', { message: 'debug', value: 1 });
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
test('应该能够设置最大监听器数量', () => {
expect(() => {
eventBus.setMaxListeners(5);
}).not.toThrow();
});
test('应该能够清空所有监听器', () => {
eventBus.on('clear:event1', () => {});
eventBus.on('clear:event2', () => {});
expect(eventBus.hasListeners('clear:event1')).toBe(true);
expect(eventBus.hasListeners('clear:event2')).toBe(true);
eventBus.clear();
expect(eventBus.hasListeners('clear:event1')).toBe(false);
expect(eventBus.hasListeners('clear:event2')).toBe(false);
});
});
describe('批处理功能', () => {
test('应该能够设置批处理配置', () => {
expect(() => {
eventBus.setBatchConfig('batch:event', 5, 100);
}).not.toThrow();
});
test('应该能够刷新批处理队列', () => {
eventBus.setBatchConfig('flush:event', 10, 200);
expect(() => {
eventBus.flushBatch('flush:event');
}).not.toThrow();
});
});
describe('事件统计', () => {
test('应该能够获取事件统计信息', () => {
eventBus.on('stats:event', () => {});
eventBus.emit('stats:event', { message: 'stat test', value: 1 });
eventBus.emit('stats:event', { message: 'stat test', value: 2 });
const stats = eventBus.getStats('stats:event') as IEventStats;
expect(stats).toBeDefined();
expect(stats.eventType).toBe('stats:event');
expect(stats.triggerCount).toBe(2);
expect(stats.listenerCount).toBe(1);
});
test('应该能够获取所有事件的统计信息', () => {
eventBus.on('all-stats:event1', () => {});
eventBus.on('all-stats:event2', () => {});
eventBus.emit('all-stats:event1', { message: 'test1', value: 1 });
eventBus.emit('all-stats:event2', { message: 'test2', value: 2 });
const allStats = eventBus.getStats() as Map<string, IEventStats>;
expect(allStats).toBeInstanceOf(Map);
expect(allStats.size).toBeGreaterThan(0);
});
test('应该能够重置事件统计', () => {
eventBus.on('reset:event', () => {});
eventBus.emit('reset:event', { message: 'before reset', value: 1 });
let stats = eventBus.getStats('reset:event') as IEventStats;
expect(stats.triggerCount).toBe(1);
eventBus.resetStats('reset:event');
stats = eventBus.getStats('reset:event') as IEventStats;
expect(stats.triggerCount).toBe(0);
});
});
describe('预定义ECS事件', () => {
test('应该能够发射和监听实体创建事件', () => {
let receivedData: MockEntityData | null = null;
eventBus.onEntityCreated((data) => {
receivedData = data;
});
const entityData: MockEntityData = {
entityId: 1,
timestamp: Date.now()
};
eventBus.emitEntityCreated(entityData);
expect(receivedData).not.toBeNull();
expect(receivedData!.entityId).toBe(1);
expect(receivedData!.timestamp).toBeDefined();
expect(receivedData!.eventId).toBeDefined();
expect(receivedData!.source).toBeDefined();
});
test('应该能够发射和监听组件添加事件', () => {
let receivedData: MockComponentData | null = null;
eventBus.onComponentAdded((data) => {
receivedData = data;
});
const componentData: MockComponentData = {
entityId: 1,
componentType: 'PositionComponent',
timestamp: Date.now()
};
eventBus.emitComponentAdded(componentData);
expect(receivedData).not.toBeNull();
expect(receivedData!.entityId).toBe(1);
expect(receivedData!.componentType).toBe('PositionComponent');
});
test('应该能够监听系统错误事件', () => {
let errorReceived = false;
eventBus.onSystemError(() => {
errorReceived = true;
});
eventBus.emit(ECSEventType.SYSTEM_ERROR, {
systemName: 'TestSystem',
error: 'Test error'
});
expect(errorReceived).toBe(true);
});
test('应该能够监听性能警告事件', () => {
let warningReceived = false;
eventBus.onPerformanceWarning(() => {
warningReceived = true;
});
eventBus.emitPerformanceWarning({
level: 'warning',
message: 'FPS dropped below threshold',
data: { fps: 30, threshold: 60 },
timestamp: Date.now()
});
expect(warningReceived).toBe(true);
});
test('应该能够发射其他预定义事件', () => {
let entityDestroyedReceived = false;
let componentRemovedReceived = false;
let systemAddedReceived = false;
let systemRemovedReceived = false;
let sceneChangedReceived = false;
eventBus.on(ECSEventType.ENTITY_DESTROYED, () => { entityDestroyedReceived = true; });
eventBus.on(ECSEventType.COMPONENT_REMOVED, () => { componentRemovedReceived = true; });
eventBus.on(ECSEventType.SYSTEM_ADDED, () => { systemAddedReceived = true; });
eventBus.on(ECSEventType.SYSTEM_REMOVED, () => { systemRemovedReceived = true; });
eventBus.on(ECSEventType.SCENE_ACTIVATED, () => { sceneChangedReceived = true; });
eventBus.emitEntityDestroyed({ entityId: 1, timestamp: Date.now() });
eventBus.emitComponentRemoved({ entityId: 1, componentType: 'Test', timestamp: Date.now() });
eventBus.emitSystemAdded({ systemName: 'TestSystem', systemType: 'EntitySystem', timestamp: Date.now() });
eventBus.emitSystemRemoved({ systemName: 'TestSystem', systemType: 'EntitySystem', timestamp: Date.now() });
eventBus.emitSceneChanged({ sceneName: 'TestScene', timestamp: Date.now() });
expect(entityDestroyedReceived).toBe(true);
expect(componentRemovedReceived).toBe(true);
expect(systemAddedReceived).toBe(true);
expect(systemRemovedReceived).toBe(true);
expect(sceneChangedReceived).toBe(true);
});
});
describe('数据增强功能', () => {
test('应该能够自动增强事件数据', () => {
let receivedData: any = null;
eventBus.on('enhanced:event', (data) => {
receivedData = data;
});
const originalData = { message: 'test' };
eventBus.emit('enhanced:event', originalData);
expect(receivedData.message).toBe('test');
expect(receivedData.timestamp).toBeDefined();
expect(receivedData.eventId).toBeDefined();
expect(receivedData.source).toBeDefined();
expect(typeof receivedData.timestamp).toBe('number');
expect(typeof receivedData.eventId).toBe('string');
expect(receivedData.source).toBe('EventBus');
});
test('增强数据时不应该覆盖现有属性', () => {
let receivedData: any = null;
eventBus.on('no-override:event', (data) => {
receivedData = data;
});
const originalData = {
message: 'test',
timestamp: 12345,
eventId: 'custom-id',
source: 'CustomSource'
};
eventBus.emit('no-override:event', originalData);
expect(receivedData.timestamp).toBe(12345);
expect(receivedData.eventId).toBe('custom-id');
expect(receivedData.source).toBe('CustomSource');
});
});
describe('边界情况和错误处理', () => {
test('移除不存在的监听器应该返回false', () => {
const removed = eventBus.off('nonexistent:event', 'invalid-id');
expect(removed).toBe(false);
});
test('获取不存在事件的监听器数量应该返回0', () => {
const count = eventBus.getListenerCount('nonexistent:event');
expect(count).toBe(0);
});
test('检查不存在事件的监听器应该返回false', () => {
const hasListeners = eventBus.hasListeners('nonexistent:event');
expect(hasListeners).toBe(false);
});
test('对不存在的事件类型执行操作应该安全', () => {
expect(() => {
eventBus.offAll('nonexistent:event');
eventBus.resetStats('nonexistent:event');
eventBus.flushBatch('nonexistent:event');
}).not.toThrow();
});
test('传入空数据应该安全处理', () => {
let receivedData: any = null;
eventBus.on('null-data:event', (data) => {
receivedData = data;
});
expect(() => {
eventBus.emit('null-data:event', null);
eventBus.emit('null-data:event', undefined);
eventBus.emit('null-data:event', {});
}).not.toThrow();
expect(receivedData).toBeDefined();
});
});
});
describe('GlobalEventBus - 全局事件总线测试', () => {
afterEach(() => {
// 重置全局实例以避免测试间干扰
GlobalEventBus.reset();
});
test('应该能够获取全局事件总线实例', () => {
const instance1 = GlobalEventBus.getInstance();
const instance2 = GlobalEventBus.getInstance();
expect(instance1).toBeInstanceOf(EventBus);
expect(instance1).toBe(instance2); // 应该是同一个实例
});
test('应该能够重置全局事件总线实例', () => {
const instance1 = GlobalEventBus.getInstance();
instance1.on('test:event', () => {});
expect(instance1.hasListeners('test:event')).toBe(true);
const instance2 = GlobalEventBus.reset();
expect(instance2).toBeInstanceOf(EventBus);
expect(instance2).not.toBe(instance1);
expect(instance2.hasListeners('test:event')).toBe(false);
});
test('应该能够使用调试模式创建全局实例', () => {
const instance = GlobalEventBus.getInstance(true);
expect(instance).toBeInstanceOf(EventBus);
});
});
describe('事件装饰器测试', () => {
// Mock class for testing decorators
class TestClass {
public receivedEvents: any[] = [];
@EventHandler('decorator:event')
handleEvent(data: any) {
this.receivedEvents.push(data);
}
@AsyncEventHandler('async:decorator:event')
async handleAsyncEvent(data: any) {
this.receivedEvents.push(data);
}
}
test('EventHandler装饰器应该能够自动注册监听器', () => {
const instance = new TestClass();
// 手动调用初始化方法(在实际应用中会在构造函数中调用)
if (typeof instance.initEventListeners === 'function') {
(instance as any).initEventListeners();
}
const eventBus = GlobalEventBus.getInstance();
eventBus.emit('decorator:event', { message: 'decorated event' });
expect(instance.receivedEvents.length).toBe(1);
expect(instance.receivedEvents[0].message).toBe('decorated event');
});
test('AsyncEventHandler装饰器应该能够自动注册异步监听器', async () => {
const instance = new TestClass();
// 手动调用初始化方法
if (typeof instance.initEventListeners === 'function') {
(instance as any).initEventListeners();
}
const eventBus = GlobalEventBus.getInstance();
await eventBus.emitAsync('async:decorator:event', { message: 'async decorated event' });
expect(instance.receivedEvents.length).toBe(1);
expect(instance.receivedEvents[0].message).toBe('async decorated event');
});
afterEach(() => {
GlobalEventBus.reset();
});
});

View File

@@ -1,6 +1,7 @@
import { QuerySystem } from '../../../src/ECS/Core/QuerySystem';
import { QuerySystem, QueryBuilder, QueryConditionType } from '../../../src/ECS/Core/QuerySystem';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { ComponentRegistry, ComponentType } from '../../../src/ECS/Core/ComponentStorage';
// 测试组件
class PositionComponent extends Component {
@@ -611,4 +612,217 @@ describe('QuerySystem - 查询系统测试', () => {
expect(stats.queryStats.totalQueries).toBeGreaterThan(0);
});
});
describe('QueryBuilder - 查询构建器功能', () => {
let builder: QueryBuilder;
beforeEach(() => {
builder = new QueryBuilder(querySystem);
// 设置测试实体的组件
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
entities[2].addComponent(new PositionComponent(30, 40));
entities[2].addComponent(new VelocityComponent(2, 2));
});
test('应该能够创建查询构建器', () => {
expect(builder).toBeInstanceOf(QueryBuilder);
});
test('应该能够构建包含所有组件的查询', () => {
const result = builder
.withAll(PositionComponent)
.execute();
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[2]);
});
test('应该能够构建包含任意组件的查询', () => {
const result = builder
.withAny(PositionComponent, VelocityComponent)
.execute();
expect(result.entities.length).toBe(3);
});
test('应该能够构建排除组件的查询', () => {
const result = builder
.without(HealthComponent)
.execute();
expect(result.entities.length).toBe(3);
});
test('应该能够重置查询构建器', () => {
builder.withAll(PositionComponent);
const resetBuilder = builder.reset();
expect(resetBuilder).toBe(builder);
const result = builder.execute();
expect(result.entities.length).toBe(0); // 没有条件,返回空结果
});
test('多条件查询应该返回空结果(当前实现限制)', () => {
const result = builder
.withAll(PositionComponent)
.withAny(VelocityComponent)
.execute();
// 当前实现只支持单一条件,多条件返回空结果
expect(result.entities.length).toBe(0);
});
test('链式调用应该工作正常', () => {
const result = builder
.withAll(PositionComponent)
.execute();
expect(result).toBeDefined();
expect(result.executionTime).toBeGreaterThanOrEqual(0);
});
});
describe('高级查询功能', () => {
test('应该能够按标签查询实体', () => {
entities[0].tag = 100;
entities[1].tag = 200;
entities[2].tag = 100;
const result = querySystem.queryByTag(100);
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[2]);
});
test('应该能够按名称查询实体', () => {
const result = querySystem.queryByName('Entity_1');
expect(result.entities.length).toBe(1);
expect(result.entities).toContain(entities[1]);
});
test('应该能够查询包含任意指定组件的实体', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
entities[2].addComponent(new HealthComponent(100));
const result = querySystem.queryAny(PositionComponent, VelocityComponent);
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[1]);
});
test('应该能够查询不包含指定组件的实体', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
const result = querySystem.queryNone(HealthComponent);
expect(result.entities.length).toBe(entities.length);
});
test('应该能够按单个组件类型查询', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
const result = querySystem.queryByComponent(PositionComponent);
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[1]);
});
});
describe('实体管理功能', () => {
test('应该能够添加和移除单个实体', () => {
const newEntity = new Entity('NewEntity', 999);
querySystem.addEntity(newEntity);
let stats = querySystem.getStats();
expect(stats.entityCount).toBe(entities.length + 1);
querySystem.removeEntity(newEntity);
stats = querySystem.getStats();
expect(stats.entityCount).toBe(entities.length);
});
test('应该能够批量添加实体', () => {
const newEntities = [
new Entity('Batch1', 997),
new Entity('Batch2', 998),
new Entity('Batch3', 999)
];
querySystem.addEntities(newEntities);
const stats = querySystem.getStats();
expect(stats.entityCount).toBe(entities.length + 3);
});
test('应该能够批量添加实体(无重复检查)', () => {
const newEntities = [
new Entity('Unchecked1', 995),
new Entity('Unchecked2', 996)
];
querySystem.addEntitiesUnchecked(newEntities);
const stats = querySystem.getStats();
expect(stats.entityCount).toBe(entities.length + 2);
});
test('应该能够批量更新组件', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
const updates = [
{ entityId: entities[0].id, componentMask: BigInt(0b1011) },
{ entityId: entities[1].id, componentMask: BigInt(0b1101) }
];
expect(() => {
querySystem.batchUpdateComponents(updates);
}).not.toThrow();
});
test('应该能够标记实体为脏', () => {
entities[0].addComponent(new PositionComponent(10, 20));
expect(() => {
querySystem.markEntityDirty(entities[0], [PositionComponent]);
}).not.toThrow();
});
});
describe('性能优化和配置', () => {
test('应该能够手动触发性能优化', () => {
expect(() => {
querySystem.optimizePerformance();
}).not.toThrow();
});
test('应该能够配置脏标记系统', () => {
expect(() => {
querySystem.configureDirtyTracking(10, 16);
}).not.toThrow();
});
test('应该能够管理帧生命周期', () => {
expect(() => {
querySystem.beginFrame();
querySystem.endFrame();
}).not.toThrow();
});
test('应该能够获取实体的原型信息', () => {
entities[0].addComponent(new PositionComponent(10, 20));
const archetype = querySystem.getEntityArchetype(entities[0]);
expect(archetype === undefined || typeof archetype === 'object').toBe(true);
});
});
});