覆盖querysystem/eventbus/componentstorage测试
This commit is contained in:
@@ -75,6 +75,14 @@ export class ComponentRegistry {
|
||||
public static getAllRegisteredTypes(): Map<Function, number> {
|
||||
return new Map(this.componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置注册表(用于测试)
|
||||
*/
|
||||
public static reset(): void {
|
||||
this.componentTypes.clear();
|
||||
this.nextBitIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
639
tests/ECS/Core/ComponentStorage.test.ts
Normal file
639
tests/ECS/Core/ComponentStorage.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
529
tests/ECS/Core/EventBus.test.ts
Normal file
529
tests/ECS/Core/EventBus.test.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user