使用Lerna 和 monorepo管理项目结构

This commit is contained in:
YHH
2025-08-07 13:29:12 +08:00
parent 4479f0fab0
commit ea8523be35
135 changed files with 7058 additions and 372 deletions

View File

@@ -0,0 +1,277 @@
import { Component } from '../../src/ECS/Component';
import { Entity } from '../../src/ECS/Entity';
// 测试组件
class TestComponent extends Component {
public value: number = 100;
public onAddedCalled = false;
public onRemovedCalled = false;
public onEnabledCalled = false;
public onDisabledCalled = false;
public updateCalled = false;
public override onAddedToEntity(): void {
this.onAddedCalled = true;
}
public override onRemovedFromEntity(): void {
this.onRemovedCalled = true;
}
public override onEnabled(): void {
this.onEnabledCalled = true;
}
public override onDisabled(): void {
this.onDisabledCalled = true;
}
public override update(): void {
this.updateCalled = true;
}
}
class AnotherTestComponent extends Component {
public name: string = 'test';
}
describe('Component - 组件基类测试', () => {
let component: TestComponent;
let entity: Entity;
beforeEach(() => {
// Reset component ID generator to avoid BigInt issues
Component._idGenerator = 0;
component = new TestComponent();
entity = new Entity('TestEntity', 1);
});
describe('基本功能', () => {
test('应该能够创建组件实例', () => {
expect(component).toBeInstanceOf(Component);
expect(component).toBeInstanceOf(TestComponent);
expect(component.id).toBeGreaterThanOrEqual(0);
});
test('每个组件应该有唯一的ID', () => {
const component1 = new TestComponent();
const component2 = new TestComponent();
const component3 = new AnotherTestComponent();
expect(component1.id).not.toBe(component2.id);
expect(component2.id).not.toBe(component3.id);
expect(component1.id).not.toBe(component3.id);
});
test('组件ID应该递增分配', () => {
const startId = Component._idGenerator;
const component1 = new TestComponent();
const component2 = new TestComponent();
expect(component2.id).toBe(component1.id + 1);
expect(component1.id).toBeGreaterThanOrEqual(startId);
});
});
describe('启用状态管理', () => {
test('组件默认应该是启用的', () => {
expect(component.enabled).toBe(true);
});
test('设置组件禁用状态应该工作', () => {
component.enabled = false;
expect(component.enabled).toBe(false);
expect(component.onDisabledCalled).toBe(true);
});
test('重新启用组件应该工作', () => {
component.enabled = false;
component.onDisabledCalled = false;
component.onEnabledCalled = false;
component.enabled = true;
expect(component.enabled).toBe(true);
expect(component.onEnabledCalled).toBe(true);
});
test('设置相同的状态不应该触发回调', () => {
component.enabled = true; // 已经是true
expect(component.onEnabledCalled).toBe(false);
component.enabled = false;
component.onDisabledCalled = false;
component.enabled = false; // 已经是false
expect(component.onDisabledCalled).toBe(false);
});
test('组件启用状态应该受实体状态影响', () => {
entity.addComponent(component);
expect(component.enabled).toBe(true);
// 禁用实体应该让组件表现为禁用
entity.enabled = false;
expect(component.enabled).toBe(false);
// 重新启用实体
entity.enabled = true;
expect(component.enabled).toBe(true);
});
test('组件自身禁用时即使实体启用也应该是禁用的', () => {
entity.addComponent(component);
component.enabled = false;
entity.enabled = true;
expect(component.enabled).toBe(false);
});
test('没有实体时组件状态应该只取决于自身', () => {
// 组件还没有添加到实体
expect(component.enabled).toBe(true);
component.enabled = false;
expect(component.enabled).toBe(false);
});
});
describe('更新顺序', () => {
test('组件默认更新顺序应该是0', () => {
expect(component.updateOrder).toBe(0);
});
test('应该能够设置更新顺序', () => {
component.updateOrder = 10;
expect(component.updateOrder).toBe(10);
component.updateOrder = -5;
expect(component.updateOrder).toBe(-5);
});
});
describe('生命周期回调', () => {
test('添加到实体时应该调用onAddedToEntity', () => {
expect(component.onAddedCalled).toBe(false);
entity.addComponent(component);
expect(component.onAddedCalled).toBe(true);
});
test('从实体移除时应该调用onRemovedFromEntity', () => {
entity.addComponent(component);
expect(component.onRemovedCalled).toBe(false);
entity.removeComponent(component);
expect(component.onRemovedCalled).toBe(true);
});
test('启用时应该调用onEnabled', () => {
component.enabled = false;
component.onEnabledCalled = false;
component.enabled = true;
expect(component.onEnabledCalled).toBe(true);
});
test('禁用时应该调用onDisabled', () => {
expect(component.onDisabledCalled).toBe(false);
component.enabled = false;
expect(component.onDisabledCalled).toBe(true);
});
});
describe('更新方法', () => {
test('update方法应该可以被调用', () => {
expect(component.updateCalled).toBe(false);
component.update();
expect(component.updateCalled).toBe(true);
});
test('基类的默认生命周期方法应该安全调用', () => {
const baseComponent = new (class extends Component {})();
// 这些方法不应该抛出异常
expect(() => {
baseComponent.onAddedToEntity();
baseComponent.onRemovedFromEntity();
baseComponent.onEnabled();
baseComponent.onDisabled();
baseComponent.update();
}).not.toThrow();
});
});
describe('实体关联', () => {
test('组件应该能够访问其所属的实体', () => {
entity.addComponent(component);
expect(component.entity).toBe(entity);
});
test('组件移除后entity引用行为', () => {
entity.addComponent(component);
expect(component.entity).toBe(entity);
entity.removeComponent(component);
// 移除后entity引用可能被清空这是正常行为
// 具体行为取决于实现,这里只测试不会抛出异常
expect(() => {
const _ = component.entity;
}).not.toThrow();
});
});
describe('边界情况', () => {
test('多次启用禁用应该工作正常', () => {
for (let i = 0; i < 10; i++) {
component.enabled = false;
expect(component.enabled).toBe(false);
component.enabled = true;
expect(component.enabled).toBe(true);
}
});
test('极端更新顺序值应该被接受', () => {
component.updateOrder = 999999;
expect(component.updateOrder).toBe(999999);
component.updateOrder = -999999;
expect(component.updateOrder).toBe(-999999);
});
test('大量组件创建应该有不同的ID', () => {
const components: Component[] = [];
const count = 1000;
for (let i = 0; i < count; i++) {
components.push(new TestComponent());
}
// 检查所有ID都不同
const ids = new Set(components.map(c => c.id));
expect(ids.size).toBe(count);
});
});
describe('继承和多态', () => {
test('不同类型的组件应该都继承自Component', () => {
const test1 = new TestComponent();
const test2 = new AnotherTestComponent();
expect(test1).toBeInstanceOf(Component);
expect(test2).toBeInstanceOf(Component);
expect(test1).toBeInstanceOf(TestComponent);
expect(test2).toBeInstanceOf(AnotherTestComponent);
});
test('组件应该能够重写基类方法', () => {
const test = new TestComponent();
test.update();
expect(test.updateCalled).toBe(true);
});
});
});

View File

@@ -0,0 +1,60 @@
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
describe('批量创建功能测试', () => {
let entityManager: EntityManager;
beforeEach(() => {
ComponentTypeManager.instance.reset();
entityManager = new EntityManager();
});
test('批量创建实体应该使用ID作为名称', () => {
const entities = entityManager.createEntitiesBatch(5, "Test");
expect(entities).toHaveLength(5);
// 验证实体名称使用ID而不是索引
for (const entity of entities) {
expect(entity.name).toBe(`Test_${entity.id}`);
}
// 验证ID是唯一的
const ids = entities.map(e => e.id);
const uniqueIds = new Set(ids);
expect(uniqueIds.size).toBe(5);
});
test('单个创建实体应该使用ID作为默认名称', () => {
const entity1 = entityManager.createEntity();
const entity2 = entityManager.createEntity();
const entity3 = entityManager.createEntity("CustomName");
expect(entity1.name).toBe(`Entity_${entity1.id}`);
expect(entity2.name).toBe(`Entity_${entity2.id}`);
expect(entity3.name).toBe("CustomName");
// 确保ID是连续的或至少是唯一的
expect(entity1.id).not.toBe(entity2.id);
expect(entity2.id).not.toBe(entity3.id);
});
test('混合创建方式的名称应该一致', () => {
// 先单个创建
const single = entityManager.createEntity();
// 再批量创建
const batch = entityManager.createEntitiesBatch(3, "Batch");
// 再单个创建
const single2 = entityManager.createEntity();
// 验证名称格式一致
expect(single.name).toBe(`Entity_${single.id}`);
expect(single2.name).toBe(`Entity_${single2.id}`);
for (const entity of batch) {
expect(entity.name).toBe(`Batch_${entity.id}`);
}
});
});

View File

@@ -0,0 +1,308 @@
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
// 测试用组件
class TestComponent extends Component {
public value: number = 0;
constructor(...args: unknown[]) {
super();
if (args.length >= 1) this.value = args[0] as number;
}
}
class AnotherTestComponent extends Component {
public name: string = 'test';
constructor(...args: unknown[]) {
super();
if (args.length >= 1) this.name = args[0] as string;
}
}
describe('ComponentIndexManager功能测试', () => {
let entityManager: EntityManager;
beforeEach(() => {
ComponentTypeManager.instance.reset();
entityManager = new EntityManager();
});
describe('基本功能测试', () => {
test('应该能够正确创建空实体', () => {
const entity = entityManager.createEntity('TestEntity');
expect(entity).toBeDefined();
expect(entity.name).toBe('TestEntity');
expect(entity.components.length).toBe(0);
expect(entity.id).toBeGreaterThanOrEqual(0);
});
test('应该能够正确管理实体的组件索引', () => {
const entity = entityManager.createEntity('TestEntity');
const component = new TestComponent(42);
// 添加组件前
expect(entity.hasComponent(TestComponent)).toBe(false);
// 添加组件
entity.addComponent(component);
// 添加组件后
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.getComponent(TestComponent)).toBe(component);
expect(entity.components.length).toBe(1);
});
test('应该能够正确处理组件查询', () => {
const entity1 = entityManager.createEntity('Entity1');
const entity2 = entityManager.createEntity('Entity2');
entity1.addComponent(new TestComponent(1));
entity2.addComponent(new TestComponent(2));
entity2.addComponent(new AnotherTestComponent('test2'));
// 查询包含TestComponent的实体
const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
expect(entitiesWithTest).toHaveLength(2);
expect(entitiesWithTest).toContain(entity1);
expect(entitiesWithTest).toContain(entity2);
// 查询包含AnotherTestComponent的实体
const entitiesWithAnother = entityManager.getEntitiesWithComponent(AnotherTestComponent);
expect(entitiesWithAnother).toHaveLength(1);
expect(entitiesWithAnother).toContain(entity2);
});
test('应该能够正确处理复杂查询', () => {
const entity1 = entityManager.createEntity('Entity1');
const entity2 = entityManager.createEntity('Entity2');
const entity3 = entityManager.createEntity('Entity3');
// entity1: TestComponent
entity1.addComponent(new TestComponent(1));
// entity2: TestComponent + AnotherTestComponent
entity2.addComponent(new TestComponent(2));
entity2.addComponent(new AnotherTestComponent('test2'));
// entity3: AnotherTestComponent
entity3.addComponent(new AnotherTestComponent('test3'));
// AND查询同时包含两个组件的实体
const bothComponents = entityManager.queryWithComponentIndex(
[TestComponent, AnotherTestComponent],
'AND'
);
expect(bothComponents.size).toBe(1);
expect(bothComponents.has(entity2)).toBe(true);
// OR查询包含任一组件的实体
const eitherComponent = entityManager.queryWithComponentIndex(
[TestComponent, AnotherTestComponent],
'OR'
);
expect(eitherComponent.size).toBe(3);
expect(eitherComponent.has(entity1)).toBe(true);
expect(eitherComponent.has(entity2)).toBe(true);
expect(eitherComponent.has(entity3)).toBe(true);
});
});
describe('组件移除功能测试', () => {
test('应该能够正确移除组件并更新索引', () => {
const entity = entityManager.createEntity('TestEntity');
const component = new TestComponent(42);
// 添加组件
entity.addComponent(component);
expect(entity.hasComponent(TestComponent)).toBe(true);
// 移除组件
entity.removeComponent(component);
expect(entity.hasComponent(TestComponent)).toBe(false);
expect(entity.getComponent(TestComponent)).toBeNull();
expect(entity.components.length).toBe(0);
// 索引应该被正确更新
const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
expect(entitiesWithTest).toHaveLength(0);
});
test('应该能够正确处理实体销毁', () => {
const entity = entityManager.createEntity('TestEntity');
entity.addComponent(new TestComponent(42));
// 确认实体存在且有组件
expect(entityManager.getEntity(entity.id)).toBe(entity);
expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(1);
// 销毁实体
const destroyed = entityManager.destroyEntity(entity);
expect(destroyed).toBe(true);
// 确认实体被正确销毁
expect(entityManager.getEntity(entity.id)).toBeNull();
expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(0);
});
});
describe('批量操作功能测试', () => {
test('应该能够正确处理批量创建实体', () => {
const entities = entityManager.createEntitiesBatch(5, 'BatchEntity');
expect(entities).toHaveLength(5);
entities.forEach((entity, index) => {
expect(entity.name).toMatch(/^BatchEntity_\d+$/);
expect(entity.components.length).toBe(0);
expect(entityManager.getEntity(entity.id)).toBe(entity);
});
expect(entityManager.entityCount).toBe(5);
});
test('批量创建的实体应该有正确的索引', () => {
const entities = entityManager.createEntitiesBatch(3, 'IndexTest');
// 给第一个和第三个实体添加组件
entities[0].addComponent(new TestComponent(1));
entities[2].addComponent(new TestComponent(3));
const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent);
expect(entitiesWithTest).toHaveLength(2);
expect(entitiesWithTest).toContain(entities[0]);
expect(entitiesWithTest).toContain(entities[2]);
});
});
describe('查询构建器功能测试', () => {
test('应该能够使用查询构建器进行复杂查询', () => {
const entity1 = entityManager.createEntity('Active1');
const entity2 = entityManager.createEntity('Active2');
const entity3 = entityManager.createEntity('Inactive');
entity1.addComponent(new TestComponent(1));
entity1.active = true;
entity2.addComponent(new TestComponent(2));
entity2.addComponent(new AnotherTestComponent('test2'));
entity2.active = true;
entity3.addComponent(new TestComponent(3));
entity3.active = false;
// 查询激活状态且包含TestComponent的实体
const activeWithTest = entityManager.query()
.withAll(TestComponent)
.active()
.execute();
expect(activeWithTest).toHaveLength(2);
expect(activeWithTest).toContain(entity1);
expect(activeWithTest).toContain(entity2);
expect(activeWithTest).not.toContain(entity3);
// 查询同时包含两个组件的实体
const withBothComponents = entityManager.query()
.withAll(TestComponent, AnotherTestComponent)
.execute();
expect(withBothComponents).toHaveLength(1);
expect(withBothComponents).toContain(entity2);
});
test('查询构建器应该支持自定义过滤条件', () => {
const entity1 = entityManager.createEntity('Player1');
const entity2 = entityManager.createEntity('Enemy1');
const entity3 = entityManager.createEntity('Player2');
entity1.addComponent(new TestComponent(100));
entity2.addComponent(new TestComponent(50));
entity3.addComponent(new TestComponent(200));
// 查询名称以"Player"开头且TestComponent值大于150的实体
const strongPlayers = entityManager.query()
.withAll(TestComponent)
.where(entity => entity.name.startsWith('Player'))
.where(entity => {
const testComp = entity.getComponent(TestComponent);
return testComp !== null && testComp.value > 150;
})
.execute();
expect(strongPlayers).toHaveLength(1);
expect(strongPlayers).toContain(entity3);
});
});
describe('统计信息功能测试', () => {
test('应该能够获取正确的统计信息', () => {
// 创建一些实体和组件
const entity1 = entityManager.createEntity('StatTest1');
const entity2 = entityManager.createEntity('StatTest2');
entity1.addComponent(new TestComponent(1));
entity2.addComponent(new TestComponent(2));
entity2.addComponent(new AnotherTestComponent('test'));
const stats = entityManager.getOptimizationStats();
expect(stats).toBeDefined();
expect(stats.componentIndex).toBeDefined();
expect(stats.componentIndex.type).toBe('hash');
expect(stats.archetypeSystem).toBeDefined();
expect(stats.dirtyTracking).toBeDefined();
});
test('应该能够正确统计实体数量', () => {
expect(entityManager.entityCount).toBe(0);
expect(entityManager.activeEntityCount).toBe(0);
const entity1 = entityManager.createEntity('Count1');
const entity2 = entityManager.createEntity('Count2');
expect(entityManager.entityCount).toBe(2);
expect(entityManager.activeEntityCount).toBe(2);
entity1.active = false;
expect(entityManager.activeEntityCount).toBe(1);
entityManager.destroyEntity(entity2);
expect(entityManager.entityCount).toBe(1);
expect(entityManager.activeEntityCount).toBe(0);
});
});
describe('边界情况测试', () => {
test('应该能够处理空组件列表的查询', () => {
const entity = entityManager.createEntity('EmptyTest');
const emptyQuery = entityManager.queryWithComponentIndex([], 'AND');
expect(emptyQuery.size).toBe(0);
const emptyOrQuery = entityManager.queryWithComponentIndex([], 'OR');
expect(emptyOrQuery.size).toBe(0);
});
test('应该能够处理不存在的组件类型查询', () => {
class NonExistentComponent extends Component {}
const entities = entityManager.getEntitiesWithComponent(NonExistentComponent);
expect(entities).toHaveLength(0);
});
test('应该能够处理重复添加相同组件类型', () => {
const entity = entityManager.createEntity('DuplicateTest');
const component1 = new TestComponent(1);
const component2 = new TestComponent(2);
entity.addComponent(component1);
expect(() => entity.addComponent(component2)).toThrow();
// 第一个组件应该仍然存在
expect(entity.getComponent(TestComponent)).toBe(component1);
});
});
});

View File

@@ -0,0 +1,470 @@
import { ComponentPool, ComponentPoolManager } from '../../../src/ECS/Core/ComponentPool';
import { Component } from '../../../src/ECS/Component';
// 测试用组件类
class TestComponent extends Component {
public value: number = 0;
public name: string = '';
reset(): void {
this.value = 0;
this.name = '';
}
}
class AnotherTestComponent extends Component {
public data: string = '';
reset(): void {
this.data = '';
}
}
describe('ComponentPool - 组件对象池测试', () => {
let pool: ComponentPool<TestComponent>;
let createFn: () => TestComponent;
let resetFn: (component: TestComponent) => void;
beforeEach(() => {
createFn = () => new TestComponent();
resetFn = (component: TestComponent) => component.reset();
pool = new ComponentPool(createFn, resetFn, 10);
});
describe('基本功能测试', () => {
it('应该能够创建组件池', () => {
expect(pool).toBeDefined();
expect(pool.getAvailableCount()).toBe(0);
expect(pool.getMaxSize()).toBe(10);
});
it('应该能够获取组件实例', () => {
const component = pool.acquire();
expect(component).toBeInstanceOf(TestComponent);
expect(component.value).toBe(0);
});
it('第一次获取应该创建新实例', () => {
const component = pool.acquire();
expect(component).toBeInstanceOf(TestComponent);
expect(pool.getAvailableCount()).toBe(0);
});
it('应该能够释放组件回池中', () => {
const component = pool.acquire();
component.value = 42;
component.name = 'test';
pool.release(component);
expect(pool.getAvailableCount()).toBe(1);
expect(component.value).toBe(0); // 应该被重置
expect(component.name).toBe(''); // 应该被重置
});
it('从池中获取的组件应该是之前释放的', () => {
const component1 = pool.acquire();
pool.release(component1);
const component2 = pool.acquire();
expect(component2).toBe(component1); // 应该是同一个实例
});
});
describe('池容量管理', () => {
it('应该能够设置最大容量', () => {
const smallPool = new ComponentPool(createFn, resetFn, 2);
expect(smallPool.getMaxSize()).toBe(2);
});
it('超过最大容量的组件不应该被存储', () => {
const smallPool = new ComponentPool(createFn, resetFn, 2);
const components = [];
for (let i = 0; i < 5; i++) {
components.push(smallPool.acquire());
}
// 释放所有组件
components.forEach(comp => smallPool.release(comp));
// 只有2个组件被存储在池中
expect(smallPool.getAvailableCount()).toBe(2);
});
it('应该正确处理默认最大容量', () => {
const defaultPool = new ComponentPool(createFn);
expect(defaultPool.getMaxSize()).toBe(1000); // 默认值
});
});
describe('重置功能测试', () => {
it('没有重置函数时应该正常工作', () => {
const poolWithoutReset = new ComponentPool<TestComponent>(createFn);
const component = poolWithoutReset.acquire();
component.value = 42;
poolWithoutReset.release(component);
// 没有重置函数,值应该保持不变
expect(component.value).toBe(42);
expect(poolWithoutReset.getAvailableCount()).toBe(1);
});
it('重置函数应该在释放时被调用', () => {
const mockReset = jest.fn();
const poolWithMockReset = new ComponentPool(createFn, mockReset);
const component = poolWithMockReset.acquire();
poolWithMockReset.release(component);
expect(mockReset).toHaveBeenCalledWith(component);
});
});
describe('预热功能', () => {
it('应该能够预填充对象池', () => {
pool.prewarm(5);
expect(pool.getAvailableCount()).toBe(5);
});
it('预热不应该超过最大容量', () => {
pool.prewarm(15); // 超过最大容量10
expect(pool.getAvailableCount()).toBe(10);
});
it('预热0个对象应该安全', () => {
pool.prewarm(0);
expect(pool.getAvailableCount()).toBe(0);
});
it('多次预热应该正确累加', () => {
pool.prewarm(3);
pool.prewarm(2);
expect(pool.getAvailableCount()).toBe(5);
});
});
describe('清空功能', () => {
it('应该能够清空对象池', () => {
pool.prewarm(5);
expect(pool.getAvailableCount()).toBe(5);
pool.clear();
expect(pool.getAvailableCount()).toBe(0);
});
it('空池清空应该安全', () => {
pool.clear();
expect(pool.getAvailableCount()).toBe(0);
});
});
describe('边界情况测试', () => {
it('应该处理连续的获取和释放', () => {
const components: TestComponent[] = [];
// 获取多个组件
for (let i = 0; i < 5; i++) {
components.push(pool.acquire());
}
// 释放所有组件
components.forEach(comp => pool.release(comp));
expect(pool.getAvailableCount()).toBe(5);
// 再次获取应该复用之前的实例
const reusedComponents: TestComponent[] = [];
for (let i = 0; i < 5; i++) {
reusedComponents.push(pool.acquire());
}
expect(pool.getAvailableCount()).toBe(0);
// 验证复用的组件确实是之前的实例
components.forEach(originalComp => {
expect(reusedComponents).toContain(originalComp);
});
});
it('应该处理空池的多次获取', () => {
const component1 = pool.acquire();
const component2 = pool.acquire();
const component3 = pool.acquire();
expect(component1).not.toBe(component2);
expect(component2).not.toBe(component3);
expect(component1).not.toBe(component3);
});
});
});
describe('ComponentPoolManager - 组件池管理器测试', () => {
let manager: ComponentPoolManager;
beforeEach(() => {
manager = ComponentPoolManager.getInstance();
// 重置管理器以确保测试隔离
manager.reset();
});
describe('单例模式测试', () => {
it('应该返回同一个实例', () => {
const instance1 = ComponentPoolManager.getInstance();
const instance2 = ComponentPoolManager.getInstance();
expect(instance1).toBe(instance2);
});
});
describe('池注册和管理', () => {
it('应该能够注册组件池', () => {
const createFn = () => new TestComponent();
const resetFn = (comp: TestComponent) => comp.reset();
manager.registerPool('TestComponent', createFn, resetFn, 20);
const stats = manager.getPoolStats();
expect(stats.has('TestComponent')).toBe(true);
expect(stats.get('TestComponent')?.maxSize).toBe(20);
});
it('应该能够注册多个不同类型的池', () => {
manager.registerPool('TestComponent', () => new TestComponent());
manager.registerPool('AnotherTestComponent', () => new AnotherTestComponent());
const stats = manager.getPoolStats();
expect(stats.size).toBe(2);
expect(stats.has('TestComponent')).toBe(true);
expect(stats.has('AnotherTestComponent')).toBe(true);
});
it('注册池时应该使用默认参数', () => {
manager.registerPool('TestComponent', () => new TestComponent());
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.maxSize).toBe(1000); // 默认值
});
});
describe('组件获取和释放', () => {
beforeEach(() => {
manager.registerPool('TestComponent', () => new TestComponent(), (comp) => comp.reset());
});
it('应该能够获取组件实例', () => {
const component = manager.acquireComponent<TestComponent>('TestComponent');
expect(component).toBeInstanceOf(TestComponent);
});
it('获取未注册池的组件应该返回null', () => {
const component = manager.acquireComponent<TestComponent>('UnknownComponent');
expect(component).toBeNull();
});
it('应该能够释放组件实例', () => {
const component = manager.acquireComponent<TestComponent>('TestComponent')!;
component.value = 42;
manager.releaseComponent('TestComponent', component);
// 验证组件被重置并返回池中
const reusedComponent = manager.acquireComponent<TestComponent>('TestComponent');
expect(reusedComponent).toBe(component);
expect(reusedComponent!.value).toBe(0); // 应该被重置
});
it('释放到未注册池应该安全处理', () => {
const component = new TestComponent();
expect(() => {
manager.releaseComponent('UnknownComponent', component);
}).not.toThrow();
});
});
describe('批量操作', () => {
beforeEach(() => {
manager.registerPool('TestComponent', () => new TestComponent());
manager.registerPool('AnotherTestComponent', () => new AnotherTestComponent());
});
it('应该能够预热所有池', () => {
manager.prewarmAll(5);
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(5);
expect(stats.get('AnotherTestComponent')?.available).toBe(5);
});
it('应该能够清空所有池', () => {
manager.prewarmAll(5);
manager.clearAll();
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(0);
expect(stats.get('AnotherTestComponent')?.available).toBe(0);
});
it('prewarmAll应该使用默认值', () => {
manager.prewarmAll(); // 默认100
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(100);
expect(stats.get('AnotherTestComponent')?.available).toBe(100);
});
});
describe('统计信息', () => {
beforeEach(() => {
manager.registerPool('TestComponent', () => new TestComponent(), undefined, 50);
});
it('应该能够获取池统计信息', () => {
const stats = manager.getPoolStats();
expect(stats.has('TestComponent')).toBe(true);
const poolStat = stats.get('TestComponent')!;
expect(poolStat.available).toBe(0);
expect(poolStat.maxSize).toBe(50);
});
it('应该能够获取池利用率信息', () => {
manager.prewarmAll(30); // 预热30个
const utilization = manager.getPoolUtilization();
const testComponentUtil = utilization.get('TestComponent')!;
expect(testComponentUtil.used).toBe(20); // 50 - 30 = 20
expect(testComponentUtil.total).toBe(50);
expect(testComponentUtil.utilization).toBe(40); // 20/50 * 100
});
it('应该能够获取指定组件的池利用率', () => {
manager.prewarmAll(30);
const utilization = manager.getComponentUtilization('TestComponent');
expect(utilization).toBe(40); // (50-30)/50 * 100
});
it('获取未注册组件的利用率应该返回0', () => {
const utilization = manager.getComponentUtilization('UnknownComponent');
expect(utilization).toBe(0);
});
it('空池的利用率应该为0', () => {
// 完全重置管理器,移除所有池
manager.reset();
const utilization = manager.getComponentUtilization('TestComponent');
expect(utilization).toBe(0);
});
});
describe('动态使用场景测试', () => {
beforeEach(() => {
manager.registerPool('TestComponent', () => new TestComponent(), (comp) => comp.reset(), 10);
});
it('应该正确跟踪组件使用情况', () => {
// 获取5个组件
const components = [];
for (let i = 0; i < 5; i++) {
const comp = manager.acquireComponent<TestComponent>('TestComponent')!;
comp.value = i;
components.push(comp);
}
// 利用率 = (maxSize - available) / maxSize * 100
// 获取了5个池中应该没有可用的因为是从空池开始所以利用率是 10/10 * 100 = 100%
let utilization = manager.getComponentUtilization('TestComponent');
expect(utilization).toBe(100); // 10/10 * 100
// 释放3个组件
for (let i = 0; i < 3; i++) {
manager.releaseComponent('TestComponent', components[i]);
}
// 现在池中有3个可用7个在使用
utilization = manager.getComponentUtilization('TestComponent');
expect(utilization).toBe(70); // 7/10 * 100
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(3); // 池中有3个可用
});
it('应该处理池满的情况', () => {
// 预热到满容量
manager.prewarmAll(10);
// 获取所有组件
const components = [];
for (let i = 0; i < 10; i++) {
components.push(manager.acquireComponent<TestComponent>('TestComponent')!);
}
expect(manager.getComponentUtilization('TestComponent')).toBe(100);
// 尝试释放更多组件(超过容量)
for (let i = 0; i < 15; i++) {
const extraComp = new TestComponent();
manager.releaseComponent('TestComponent', extraComp);
}
// 池应该仍然是满的,不会超过容量
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(10);
});
});
describe('性能测试', () => {
beforeEach(() => {
manager.registerPool('TestComponent', () => new TestComponent(), (comp) => comp.reset());
});
it('大量组件获取和释放应该高效', () => {
const startTime = performance.now();
const components = [];
// 获取1000个组件
for (let i = 0; i < 1000; i++) {
components.push(manager.acquireComponent<TestComponent>('TestComponent')!);
}
// 释放所有组件
components.forEach(comp => manager.releaseComponent('TestComponent', comp));
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
});
});
describe('边界情况和错误处理', () => {
it('空管理器的统计信息应该正确', () => {
// 完全重置管理器以确保清洁状态
const emptyManager = ComponentPoolManager.getInstance();
emptyManager.reset();
const stats = emptyManager.getPoolStats();
const utilization = emptyManager.getPoolUtilization();
expect(stats.size).toBe(0);
expect(utilization.size).toBe(0);
});
it('重复注册同一组件类型应该覆盖之前的池', () => {
manager.registerPool('TestComponent', () => new TestComponent(), undefined, 10);
manager.registerPool('TestComponent', () => new TestComponent(), undefined, 20);
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.maxSize).toBe(20);
});
it('处理极端的预热数量', () => {
manager.registerPool('TestComponent', () => new TestComponent(), undefined, 5);
// 预热超过最大容量
manager.prewarmAll(100);
const stats = manager.getPoolStats();
expect(stats.get('TestComponent')?.available).toBe(5); // 不应该超过最大容量
});
});
});

View File

@@ -0,0 +1,88 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA } from '../../../src/ECS/Core/ComponentStorage';
// 默认原始存储组件
class PositionComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
}
// 启用SoA优化的组件用于大规模批量操作
@EnableSoA
class LargeScaleComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
public vx: number = 0;
public vy: number = 0;
public vz: number = 0;
}
describe('SoA优化选择测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('默认使用原始存储', () => {
const storage = manager.getStorage(PositionComponent);
// 添加组件
manager.addComponent(1, new PositionComponent());
// 验证能正常工作
const component = manager.getComponent(1, PositionComponent);
expect(component).toBeTruthy();
expect(component?.x).toBe(0);
// 验证使用原始存储
expect(storage.constructor.name).toBe('ComponentStorage');
});
test('@EnableSoA装饰器启用优化', () => {
const storage = manager.getStorage(LargeScaleComponent);
// 添加组件
const component = new LargeScaleComponent();
component.x = 100;
component.vx = 10;
manager.addComponent(1, component);
// 验证能正常工作
const retrieved = manager.getComponent(1, LargeScaleComponent);
expect(retrieved).toBeTruthy();
expect(retrieved?.x).toBe(100);
expect(retrieved?.vx).toBe(10);
// 验证使用SoA存储
expect(storage.constructor.name).toBe('SoAStorage');
});
test('SoA存储功能验证', () => {
const entityCount = 1000;
// 创建实体使用SoA优化
for (let i = 0; i < entityCount; i++) {
const component = new LargeScaleComponent();
component.x = i;
component.y = i * 2;
component.vx = 1;
component.vy = 2;
manager.addComponent(i, component);
}
// 验证数据正确性
const testComponent = manager.getComponent(100, LargeScaleComponent);
expect(testComponent?.x).toBe(100);
expect(testComponent?.y).toBe(200);
expect(testComponent?.vx).toBe(1);
expect(testComponent?.vy).toBe(2);
// 验证存储类型
const storage = manager.getStorage(LargeScaleComponent);
expect(storage.constructor.name).toBe('SoAStorage');
console.log(`成功创建 ${entityCount} 个SoA实体数据验证通过`);
});
});

View File

@@ -0,0 +1,536 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorage, ComponentStorageManager, EnableSoA } from '../../../src/ECS/Core/ComponentStorage';
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
// 测试用统一组件结构启用SoA
@EnableSoA
class TestPositionComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0, z = 0] = args as [number?, number?, number?];
this.x = x;
this.y = y;
this.z = z;
}
}
@EnableSoA
class TestVelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
public vz: number = 0;
public maxSpeed: number = 100;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0, vz = 0] = args as [number?, number?, number?];
this.vx = vx;
this.vy = vy;
this.vz = vz;
}
}
@EnableSoA
class TestHealthComponent extends Component {
public current: number = 100;
public max: number = 100;
public regeneration: number = 1;
constructor(...args: unknown[]) {
super();
const [current = 100, max = 100] = args as [number?, number?];
this.current = current;
this.max = max;
}
}
// 用于原始存储测试的版本(默认原始存储)
class OriginalPositionComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0, z = 0] = args as [number?, number?, number?];
this.x = x;
this.y = y;
this.z = z;
}
}
class OriginalVelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
public vz: number = 0;
public maxSpeed: number = 100;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0, vz = 0] = args as [number?, number?, number?];
this.vx = vx;
this.vy = vy;
this.vz = vz;
}
}
class OriginalHealthComponent extends Component {
public current: number = 100;
public max: number = 100;
public regeneration: number = 1;
constructor(...args: unknown[]) {
super();
const [current = 100, max = 100] = args as [number?, number?];
this.current = current;
this.max = max;
}
}
interface PerformanceResult {
name: string;
storageType: 'Original' | 'SoA';
entityCount: number;
operations: number;
totalTime: number;
averageTime: number;
operationsPerSecond: number;
}
describe('ComponentStorage 严谨性能对比测试', () => {
const entityCounts = [1000, 5000, 20000];
let results: PerformanceResult[] = [];
afterAll(() => {
generateDetailedReport();
});
describe('存储器创建和初始化', () => {
test('验证SoA和原始存储使用相同接口', () => {
const originalManager = new ComponentStorageManager();
const soaManager = new ComponentStorageManager();
const originalStorage = originalManager.getStorage(OriginalPositionComponent);
const soaStorage = soaManager.getStorage(TestPositionComponent);
// 验证都实现了相同的接口
expect(typeof originalStorage.addComponent).toBe('function');
expect(typeof originalStorage.getComponent).toBe('function');
expect(typeof originalStorage.hasComponent).toBe('function');
expect(typeof originalStorage.removeComponent).toBe('function');
expect(typeof soaStorage.addComponent).toBe('function');
expect(typeof soaStorage.getComponent).toBe('function');
expect(typeof soaStorage.hasComponent).toBe('function');
expect(typeof soaStorage.removeComponent).toBe('function');
// 验证存储器类型
expect(originalStorage).toBeInstanceOf(ComponentStorage);
expect(soaStorage).toBeInstanceOf(SoAStorage);
});
});
describe('实体创建性能对比', () => {
entityCounts.forEach(entityCount => {
test(`创建 ${entityCount} 个完整实体`, () => {
console.log(`\\n=== 实体创建性能测试: ${entityCount} 个实体 ===`);
// 原始存储测试
const originalResult = measureOriginalEntityCreation(entityCount);
results.push(originalResult);
// SoA存储测试
const soaResult = measureSoAEntityCreation(entityCount);
results.push(soaResult);
// 输出对比结果
console.log(`原始存储: ${originalResult.totalTime.toFixed(2)}ms (${originalResult.operationsPerSecond.toFixed(0)} ops/sec)`);
console.log(`SoA存储: ${soaResult.totalTime.toFixed(2)}ms (${soaResult.operationsPerSecond.toFixed(0)} ops/sec)`);
const speedup = originalResult.totalTime / soaResult.totalTime;
const improvement = ((speedup - 1) * 100);
console.log(`性能对比: ${speedup.toFixed(2)}x ${improvement > 0 ? '提升' : '下降'} ${Math.abs(improvement).toFixed(1)}%`);
// 验证功能正确性
expect(originalResult.operations).toBe(soaResult.operations);
expect(originalResult.totalTime).toBeGreaterThan(0);
expect(soaResult.totalTime).toBeGreaterThan(0);
});
});
});
describe('组件访问性能对比', () => {
entityCounts.forEach(entityCount => {
test(`随机访问 ${entityCount} 个实体组件`, () => {
console.log(`\\n=== 组件访问性能测试: ${entityCount} 个实体 ===`);
// 原始存储测试
const originalResult = measureOriginalComponentAccess(entityCount, 100);
results.push(originalResult);
// SoA存储测试
const soaResult = measureSoAComponentAccess(entityCount, 100);
results.push(soaResult);
// 输出对比结果
console.log(`原始存储: ${originalResult.totalTime.toFixed(2)}ms (${originalResult.operationsPerSecond.toFixed(0)} ops/sec)`);
console.log(`SoA存储: ${soaResult.totalTime.toFixed(2)}ms (${soaResult.operationsPerSecond.toFixed(0)} ops/sec)`);
const speedup = originalResult.totalTime / soaResult.totalTime;
const improvement = ((speedup - 1) * 100);
console.log(`性能对比: ${speedup.toFixed(2)}x ${improvement > 0 ? '提升' : '下降'} ${Math.abs(improvement).toFixed(1)}%`);
expect(originalResult.operations).toBe(soaResult.operations);
expect(originalResult.totalTime).toBeGreaterThan(0);
expect(soaResult.totalTime).toBeGreaterThan(0);
});
});
});
describe('批量更新性能对比SoA优势场景', () => {
entityCounts.forEach(entityCount => {
test(`批量更新 ${entityCount} 个实体`, () => {
console.log(`\\n=== 批量更新性能测试: ${entityCount} 个实体 ===`);
// 原始存储测试
const originalResult = measureOriginalBatchUpdate(entityCount, 50);
results.push(originalResult);
// SoA存储测试向量化操作
const soaResult = measureSoABatchUpdate(entityCount, 50);
results.push(soaResult);
// 输出对比结果
console.log(`原始存储: ${originalResult.totalTime.toFixed(2)}ms (${originalResult.operationsPerSecond.toFixed(0)} ops/sec)`);
console.log(`SoA存储: ${soaResult.totalTime.toFixed(2)}ms (${soaResult.operationsPerSecond.toFixed(0)} ops/sec)`);
const speedup = originalResult.totalTime / soaResult.totalTime;
const improvement = ((speedup - 1) * 100);
console.log(`性能对比: ${speedup.toFixed(2)}x ${improvement > 0 ? '提升' : '下降'} ${Math.abs(improvement).toFixed(1)}%`);
// 这是SoA的优势场景应该有性能提升
if (entityCount > 5000) {
expect(speedup).toBeGreaterThan(1.0); // SoA应该更快
}
expect(originalResult.operations).toBe(soaResult.operations);
expect(originalResult.totalTime).toBeGreaterThan(0);
expect(soaResult.totalTime).toBeGreaterThan(0);
});
});
});
// 测试辅助函数
function measureOriginalEntityCreation(entityCount: number): PerformanceResult {
const manager = new ComponentStorageManager();
const startTime = performance.now();
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new OriginalPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
manager.addComponent(i, new OriginalVelocityComponent(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 10
));
if (i % 2 === 0) {
manager.addComponent(i, new OriginalHealthComponent(
80 + Math.random() * 20,
100
));
}
}
const totalTime = performance.now() - startTime;
const operations = entityCount * 2.5; // 平均每个实体2.5个组件
return {
name: 'Entity Creation',
storageType: 'Original',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function measureSoAEntityCreation(entityCount: number): PerformanceResult {
const manager = new ComponentStorageManager();
const startTime = performance.now();
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new TestPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
manager.addComponent(i, new TestVelocityComponent(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 10
));
if (i % 2 === 0) {
manager.addComponent(i, new TestHealthComponent(
80 + Math.random() * 20,
100
));
}
}
const totalTime = performance.now() - startTime;
const operations = entityCount * 2.5;
return {
name: 'Entity Creation',
storageType: 'SoA',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function measureOriginalComponentAccess(entityCount: number, iterations: number): PerformanceResult {
const manager = new ComponentStorageManager();
// 预创建实体
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new OriginalPositionComponent(i, i, i));
manager.addComponent(i, new OriginalVelocityComponent(1, 1, 1));
if (i % 2 === 0) {
manager.addComponent(i, new OriginalHealthComponent(100, 100));
}
}
const startTime = performance.now();
for (let iter = 0; iter < iterations; iter++) {
for (let i = 0; i < entityCount; i++) {
const pos = manager.getComponent(i, OriginalPositionComponent);
const vel = manager.getComponent(i, OriginalVelocityComponent);
if (pos && vel) {
// 模拟简单的读取操作
const sum = pos.x + pos.y + pos.z + vel.vx + vel.vy + vel.vz;
if (sum < 0) continue; // 防止优化
}
}
}
const totalTime = performance.now() - startTime;
const operations = entityCount * iterations;
return {
name: 'Component Access',
storageType: 'Original',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function measureSoAComponentAccess(entityCount: number, iterations: number): PerformanceResult {
const manager = new ComponentStorageManager();
// 预创建实体
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new TestPositionComponent(i, i, i));
manager.addComponent(i, new TestVelocityComponent(1, 1, 1));
if (i % 2 === 0) {
manager.addComponent(i, new TestHealthComponent(100, 100));
}
}
const startTime = performance.now();
for (let iter = 0; iter < iterations; iter++) {
for (let i = 0; i < entityCount; i++) {
const pos = manager.getComponent(i, TestPositionComponent);
const vel = manager.getComponent(i, TestVelocityComponent);
if (pos && vel) {
// 模拟简单的读取操作
const sum = pos.x + pos.y + pos.z + vel.vx + vel.vy + vel.vz;
if (sum < 0) continue; // 防止优化
}
}
}
const totalTime = performance.now() - startTime;
const operations = entityCount * iterations;
return {
name: 'Component Access',
storageType: 'SoA',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function measureOriginalBatchUpdate(entityCount: number, iterations: number): PerformanceResult {
const manager = new ComponentStorageManager();
// 预创建实体
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new OriginalPositionComponent(i, i, 0));
manager.addComponent(i, new OriginalVelocityComponent(1, 1, 0));
}
const startTime = performance.now();
const deltaTime = 0.016;
for (let iter = 0; iter < iterations; iter++) {
for (let i = 0; i < entityCount; i++) {
const pos = manager.getComponent(i, OriginalPositionComponent);
const vel = manager.getComponent(i, OriginalVelocityComponent);
if (pos && vel) {
// 物理更新
pos.x += vel.vx * deltaTime;
pos.y += vel.vy * deltaTime;
pos.z += vel.vz * deltaTime;
}
}
}
const totalTime = performance.now() - startTime;
const operations = entityCount * iterations;
return {
name: 'Batch Update',
storageType: 'Original',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function measureSoABatchUpdate(entityCount: number, iterations: number): PerformanceResult {
const manager = new ComponentStorageManager();
// 预创建实体
for (let i = 0; i < entityCount; i++) {
manager.addComponent(i, new TestPositionComponent(i, i, 0));
manager.addComponent(i, new TestVelocityComponent(1, 1, 0));
}
const startTime = performance.now();
const deltaTime = 0.016;
// 获取SoA存储器进行向量化操作
const posStorage = manager.getStorage(TestPositionComponent) as SoAStorage<TestPositionComponent>;
const velStorage = manager.getStorage(TestVelocityComponent) as SoAStorage<TestVelocityComponent>;
for (let iter = 0; iter < iterations; iter++) {
// 使用向量化操作
posStorage.performVectorizedOperation((posFields, activeIndices) => {
const velFields = velStorage.getFieldArray('vx') ?
new Map([
['vx', velStorage.getFieldArray('vx')!],
['vy', velStorage.getFieldArray('vy')!],
['vz', velStorage.getFieldArray('vz')!]
]) : new Map();
const posX = posFields.get('x') as Float32Array;
const posY = posFields.get('y') as Float32Array;
const posZ = posFields.get('z') as Float32Array;
const velX = velFields.get('vx') as Float32Array;
const velY = velFields.get('vy') as Float32Array;
const velZ = velFields.get('vz') as Float32Array;
// 向量化物理更新
for (let j = 0; j < activeIndices.length; j++) {
const idx = activeIndices[j];
posX[idx] += velX[idx] * deltaTime;
posY[idx] += velY[idx] * deltaTime;
posZ[idx] += velZ[idx] * deltaTime;
}
});
}
const totalTime = performance.now() - startTime;
const operations = entityCount * iterations;
return {
name: 'Batch Update',
storageType: 'SoA',
entityCount,
operations,
totalTime,
averageTime: totalTime / operations,
operationsPerSecond: operations / (totalTime / 1000)
};
}
function generateDetailedReport(): void {
console.log('\\n' + '='.repeat(80));
console.log('ComponentStorage 严谨性能对比报告');
console.log('='.repeat(80));
// 按测试类型分组
const groupedResults = new Map<string, PerformanceResult[]>();
for (const result of results) {
const key = `${result.name}-${result.entityCount}`;
if (!groupedResults.has(key)) {
groupedResults.set(key, []);
}
groupedResults.get(key)!.push(result);
}
let totalOriginalTime = 0;
let totalSoATime = 0;
let testCount = 0;
for (const [key, testResults] of groupedResults.entries()) {
console.log(`\\n${key}:`);
const originalResult = testResults.find(r => r.storageType === 'Original');
const soaResult = testResults.find(r => r.storageType === 'SoA');
if (originalResult && soaResult) {
const speedup = originalResult.totalTime / soaResult.totalTime;
const improvement = ((speedup - 1) * 100);
console.log(` 原始存储: ${originalResult.totalTime.toFixed(2)}ms (${originalResult.operationsPerSecond.toFixed(0)} ops/sec)`);
console.log(` SoA存储: ${soaResult.totalTime.toFixed(2)}ms (${soaResult.operationsPerSecond.toFixed(0)} ops/sec)`);
console.log(` 性能对比: ${speedup.toFixed(2)}x ${improvement > 0 ? '提升' : '下降'} ${Math.abs(improvement).toFixed(1)}%`);
totalOriginalTime += originalResult.totalTime;
totalSoATime += soaResult.totalTime;
testCount++;
}
}
if (testCount > 0) {
const overallSpeedup = totalOriginalTime / totalSoATime;
const overallImprovement = ((overallSpeedup - 1) * 100);
console.log('\\n' + '='.repeat(80));
console.log('总体性能对比:');
console.log(` 原始存储总耗时: ${totalOriginalTime.toFixed(2)}ms`);
console.log(` SoA存储总耗时: ${totalSoATime.toFixed(2)}ms`);
console.log(` 总体性能对比: ${overallSpeedup.toFixed(2)}x ${overallImprovement > 0 ? '提升' : '下降'} ${Math.abs(overallImprovement).toFixed(1)}%`);
console.log('\\n结论: SoA优化在批量操作场景中表现优异在小规模随机访问场景中有轻微开销。');
console.log('建议: 对于大规模游戏实体和批量系统更新SoA优化能带来显著性能提升。');
console.log('='.repeat(80));
}
}
});

View File

@@ -0,0 +1,661 @@
import {
ComponentRegistry,
ComponentStorage,
ComponentStorageManager,
ComponentType
} from '../../../src/ECS/Core/ComponentStorage';
import { Component } from '../../../src/ECS/Component';
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
// 测试组件类(默认使用原始存储)
class TestComponent extends Component {
public value: number;
constructor(...args: unknown[]) {
super();
const [value = 0] = args as [number?];
this.value = value;
}
}
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class HealthComponent extends Component {
public health: number;
public maxHealth: number;
constructor(...args: unknown[]) {
super();
const [health = 100, maxHealth = 100] = args as [number?, number?];
this.health = health;
this.maxHealth = maxHealth;
}
}
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.toString()).toBe('1'); // 2^0
expect(mask2.toString()).toBe('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)的掩码
expect(mask.toString()).toBe('3'); // 1 | 2 = 3
});
test('没有组件的实体应该有零掩码', () => {
const mask = manager.getComponentMask(999);
expect(mask.isZero()).toBe(true);
});
test('添加和移除组件应该更新掩码', () => {
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(PositionComponent);
manager.addComponent(1, new TestComponent(100));
let mask = manager.getComponentMask(1);
expect(mask.toString()).toBe('1');
manager.addComponent(1, new PositionComponent(10, 20));
mask = manager.getComponentMask(1);
expect(mask.toString()).toBe('3'); // 0b11
manager.removeComponent(1, TestComponent);
mask = manager.getComponentMask(1);
expect(mask.toString()).toBe('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,433 @@
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
import { EventBus } from '../../../src/ECS/Core/EventBus';
// 测试组件
class TransformComponent extends Component {
public x: number = 0;
public y: number = 0;
public rotation: number = 0;
constructor(...args: unknown[]) {
super();
if (args.length >= 1) this.x = args[0] as number;
if (args.length >= 2) this.y = args[1] as number;
if (args.length >= 3) this.rotation = args[2] as number;
}
}
class VelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
constructor(...args: unknown[]) {
super();
if (args.length >= 1) this.vx = args[0] as number;
if (args.length >= 2) this.vy = args[1] as number;
}
}
class HealthComponent extends Component {
public health: number = 100;
public maxHealth: number = 100;
constructor(...args: unknown[]) {
super();
if (args.length >= 1) this.health = args[0] as number;
if (args.length >= 2) this.maxHealth = args[1] as number;
}
}
// 简单的事件装饰器实现(用于测试)
function EventHandler(eventType: string, priority: number = 0) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
// 在原型上标记事件处理器信息
if (!target.constructor._eventHandlers) {
target.constructor._eventHandlers = [];
}
target.constructor._eventHandlers.push({
eventType,
methodName: propertyKey,
priority,
handler: originalMethod
});
return descriptor;
};
}
// 自动初始化事件监听器的基类
class EventAwareSystem extends EntitySystem {
private eventListenerIds: string[] = [];
constructor(matcher: Matcher) {
super(matcher);
}
public override initialize(): void {
super.initialize();
this.initializeEventHandlers();
}
private initializeEventHandlers(): void {
const eventHandlers = (this.constructor as any)._eventHandlers;
if (!eventHandlers || !this.scene?.eventSystem) {
return;
}
// 按优先级排序并注册事件处理器
eventHandlers
.sort((a: any, b: any) => b.priority - a.priority)
.forEach((handlerInfo: any) => {
const listenerId = this.scene!.eventSystem.on(
handlerInfo.eventType,
handlerInfo.handler.bind(this),
{ priority: handlerInfo.priority }
);
this.eventListenerIds.push(listenerId);
});
}
public cleanup(): void {
// 清理事件监听器
if (this.scene?.eventSystem) {
this.eventListenerIds.forEach(id => {
// 注意这里需要修改EventSystem来支持通过ID移除监听器
// this.scene!.eventSystem.removeListener(id);
});
}
this.eventListenerIds = [];
}
}
// 使用装饰器的测试系统
class DecoratedMovementSystem extends EventAwareSystem {
public processedEntities: Entity[] = [];
public receivedEvents: any[] = [];
public entityMovedEvents: any[] = [];
constructor() {
super(Matcher.empty().all(TransformComponent, VelocityComponent));
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
for (const entity of entities) {
const transform = entity.getComponent(TransformComponent)!;
const velocity = entity.getComponent(VelocityComponent)!;
const oldX = transform.x;
const oldY = transform.y;
// 更新位置
transform.x += velocity.vx;
transform.y += velocity.vy;
// 发射实体移动事件
if (this.scene?.eventSystem) {
this.scene.eventSystem.emit('entity:moved', {
entityId: entity.id,
entityName: entity.name,
oldPosition: { x: oldX, y: oldY },
newPosition: { x: transform.x, y: transform.y }
});
}
}
}
@EventHandler('entity:moved', 10)
onEntityMoved(data: any): void {
this.entityMovedEvents.push(data);
}
@EventHandler('entity:health_changed', 5)
onHealthChanged(data: any): void {
this.receivedEvents.push({ type: 'health_changed', data });
}
@EventHandler('system:initialized', 15)
onSystemInitialized(data: any): void {
this.receivedEvents.push({ type: 'system_initialized', data });
}
}
class HealthSystem extends EventAwareSystem {
public processedEntities: Entity[] = [];
public receivedEvents: any[] = [];
constructor() {
super(Matcher.empty().all(HealthComponent));
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
for (const entity of entities) {
const health = entity.getComponent(HealthComponent)!;
// 模拟健康值变化
if (health.health > 0) {
const oldHealth = health.health;
health.health = Math.max(0, health.health - 1);
// 发射健康值变化事件
if (this.scene?.eventSystem && oldHealth !== health.health) {
this.scene.eventSystem.emit('entity:health_changed', {
entityId: entity.id,
entityName: entity.name,
oldHealth,
newHealth: health.health,
isDead: health.health <= 0
});
}
}
}
}
@EventHandler('entity:health_changed', 8)
onHealthChanged(data: any): void {
this.receivedEvents.push(data);
// 如果实体死亡,禁用它
if (data.isDead) {
const entity = this.scene?.findEntityById(data.entityId);
if (entity) {
entity.enabled = false;
}
}
}
}
describe('装饰器系统测试', () => {
let scene: Scene;
beforeEach(() => {
scene = new Scene();
scene.name = "DecoratorTestScene";
});
describe('事件装饰器基础功能', () => {
test('装饰器应该正确注册事件处理器', () => {
const movementSystem = new DecoratedMovementSystem();
// 检查装饰器是否正确注册了事件处理器信息
const eventHandlers = (DecoratedMovementSystem as any)._eventHandlers;
expect(eventHandlers).toBeDefined();
expect(eventHandlers.length).toBe(3);
// 检查事件处理器信息
const entityMovedHandler = eventHandlers.find((h: any) => h.eventType === 'entity:moved');
expect(entityMovedHandler).toBeDefined();
expect(entityMovedHandler.priority).toBe(10);
expect(entityMovedHandler.methodName).toBe('onEntityMoved');
});
test('系统初始化时应该自动注册事件监听器', () => {
const entity = scene.createEntity("TestEntity");
entity.addComponent(new TransformComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
// 验证系统正确初始化
expect(movementSystem.entities.length).toBe(1);
// 运行一次更新应该触发entity:moved事件
scene.update();
// 检查事件是否被正确处理
expect(movementSystem.entityMovedEvents.length).toBe(1);
expect(movementSystem.entityMovedEvents[0].entityId).toBe(entity.id);
expect(movementSystem.entityMovedEvents[0].newPosition.x).toBe(1);
expect(movementSystem.entityMovedEvents[0].newPosition.y).toBe(1);
});
});
describe('多系统事件交互', () => {
test('多个系统应该能够响应同一事件', () => {
const entity = scene.createEntity("TestEntity");
entity.addComponent(new TransformComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
entity.addComponent(new HealthComponent(10));
const movementSystem = new DecoratedMovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 运行几次更新
scene.update();
scene.update();
scene.update();
// 检查健康系统是否处理了健康变化事件
expect(healthSystem.receivedEvents.length).toBeGreaterThan(0);
// 检查移动系统是否也接收到了健康变化事件
const healthChangedEvents = movementSystem.receivedEvents.filter(e => e.type === 'health_changed');
expect(healthChangedEvents.length).toBeGreaterThan(0);
});
test('事件优先级应该正确工作', () => {
const entity = scene.createEntity("TestEntity");
entity.addComponent(new TransformComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
// 发射系统初始化事件(如果有的话)
if (scene.eventSystem) {
scene.eventSystem.emit('system:initialized', {
systemName: 'DecoratedMovementSystem',
timestamp: Date.now()
});
}
// 检查事件是否被接收
scene.update();
// 验证不同优先级的事件都被处理了
const systemInitEvents = movementSystem.receivedEvents.filter(e => e.type === 'system_initialized');
expect(systemInitEvents.length).toBeGreaterThanOrEqual(0);
});
});
describe('装饰器系统的时序问题', () => {
test('先添加实体再添加装饰器系统 - 事件应该正常工作', () => {
// 先创建实体
const entity = scene.createEntity("TestEntity");
entity.addComponent(new TransformComponent(5, 5));
entity.addComponent(new VelocityComponent(2, 3));
// 然后添加装饰器系统
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
// 验证系统正确识别了实体
expect(movementSystem.entities.length).toBe(1);
// 运行更新,应该触发事件
scene.update();
// 检查事件装饰器是否正常工作
expect(movementSystem.entityMovedEvents.length).toBe(1);
expect(movementSystem.entityMovedEvents[0].oldPosition.x).toBe(5);
expect(movementSystem.entityMovedEvents[0].newPosition.x).toBe(7);
});
test('动态添加组件后装饰器事件应该正常', () => {
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
const entity = scene.createEntity("DynamicEntity");
entity.addComponent(new TransformComponent(0, 0));
// 初始状态:系统不匹配
expect(movementSystem.entities.length).toBe(0);
// 动态添加速度组件
entity.addComponent(new VelocityComponent(1, 1));
// 系统应该匹配
expect(movementSystem.entities.length).toBe(1);
// 运行更新,事件应该正常触发
scene.update();
expect(movementSystem.entityMovedEvents.length).toBe(1);
});
});
describe('装饰器系统的清理', () => {
test('系统移除时应该清理事件监听器', () => {
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
// 验证系统已添加
expect(scene.entityProcessors.count).toBe(1);
// 移除系统
scene.removeEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(0);
// 清理事件监听器
movementSystem.cleanup();
// 验证事件监听器已清理(这里主要是检查不抛出异常)
expect(() => {
if (scene.eventSystem) {
scene.eventSystem.emit('entity:moved', { test: true });
}
}).not.toThrow();
});
});
describe('边界情况测试', () => {
test('没有装饰器的系统应该正常工作', () => {
class SimpleSystem extends EventAwareSystem {
public processedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(TransformComponent));
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
}
}
const entity = scene.createEntity("SimpleEntity");
entity.addComponent(new TransformComponent(1, 1));
const simpleSystem = new SimpleSystem();
expect(() => {
scene.addEntityProcessor(simpleSystem);
}).not.toThrow();
expect(simpleSystem.entities.length).toBe(1);
scene.update();
expect(simpleSystem.processedEntities.length).toBe(1);
});
test('空事件数据应该正常处理', () => {
const movementSystem = new DecoratedMovementSystem();
scene.addEntityProcessor(movementSystem);
// 发射空事件
if (scene.eventSystem) {
scene.eventSystem.emit('entity:health_changed', null);
scene.eventSystem.emit('entity:health_changed', undefined);
scene.eventSystem.emit('entity:health_changed', {});
}
// 系统应该能够处理空数据而不崩溃
expect(() => {
scene.update();
}).not.toThrow();
});
});
afterEach(() => {
// 清理场景
scene.destroyAllEntities();
// 清理系统并清理它们的事件监听器
const processors = [...scene.entityProcessors.processors];
processors.forEach(processor => {
if (processor instanceof EventAwareSystem) {
processor.cleanup();
}
scene.removeEntityProcessor(processor);
});
});
});

View File

@@ -0,0 +1,227 @@
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
import { Entity } from '../../../src/ECS/Entity';
describe('详细性能分析 - 逐步测量', () => {
let entityManager: EntityManager;
beforeEach(() => {
ComponentTypeManager.instance.reset();
entityManager = new EntityManager();
});
test('精确测量createEntity中每个步骤的耗时', () => {
const testCount = 1000;
console.log(`\n=== 详细性能分析 (${testCount}个实体) ===`);
const timings = {
idCheckOut: 0,
nameGeneration: 0,
entityConstruction: 0,
mapSet: 0,
nameIndexUpdate: 0,
tagIndexUpdate: 0,
componentIndexManager: 0,
archetypeSystem: 0,
dirtyTracking: 0,
eventEmission: 0,
total: 0
};
const totalStart = performance.now();
for (let i = 0; i < testCount; i++) {
// 步骤1: ID分配
let stepStart = performance.now();
const id = entityManager['_identifierPool'].checkOut();
timings.idCheckOut += performance.now() - stepStart;
// 步骤2: 名称生成
stepStart = performance.now();
const name = `Entity_${id}`;
timings.nameGeneration += performance.now() - stepStart;
// 步骤3: Entity构造
stepStart = performance.now();
const entity = new Entity(name, id);
timings.entityConstruction += performance.now() - stepStart;
// 步骤4: Map存储
stepStart = performance.now();
entityManager['_entities'].set(id, entity);
timings.mapSet += performance.now() - stepStart;
// 步骤5: 名称索引更新
stepStart = performance.now();
entityManager['updateNameIndex'](entity, true);
timings.nameIndexUpdate += performance.now() - stepStart;
// 步骤6: 标签索引更新
stepStart = performance.now();
entityManager['updateTagIndex'](entity, true);
timings.tagIndexUpdate += performance.now() - stepStart;
// 步骤7: 组件索引管理器
stepStart = performance.now();
entityManager['_componentIndexManager'].addEntity(entity);
timings.componentIndexManager += performance.now() - stepStart;
// 步骤8: 原型系统
stepStart = performance.now();
entityManager['_archetypeSystem'].addEntity(entity);
timings.archetypeSystem += performance.now() - stepStart;
// 步骤9: 脏标记系统
stepStart = performance.now();
entityManager['_dirtyTrackingSystem'].markDirty(entity, 1); // DirtyFlag.COMPONENT_ADDED
timings.dirtyTracking += performance.now() - stepStart;
// 步骤10: 事件发射
stepStart = performance.now();
entityManager['_eventBus'].emitEntityCreated({
timestamp: Date.now(),
source: 'EntityManager',
entityId: entity.id,
entityName: entity.name,
entityTag: entity.tag?.toString()
});
timings.eventEmission += performance.now() - stepStart;
}
timings.total = performance.now() - totalStart;
console.log('\n各步骤耗时统计:');
console.log(`总耗时: ${timings.total.toFixed(2)}ms`);
console.log(`平均每个实体: ${(timings.total / testCount).toFixed(3)}ms`);
console.log('\n详细分解:');
const sortedTimings = Object.entries(timings)
.filter(([key]) => key !== 'total')
.sort(([,a], [,b]) => b - a)
.map(([key, time]) => ({
step: key,
timeMs: time,
percentage: (time / timings.total * 100),
avgPerEntity: (time / testCount * 1000) // 转换为微秒
}));
for (const timing of sortedTimings) {
console.log(` ${timing.step.padEnd(20)}: ${timing.timeMs.toFixed(2)}ms (${timing.percentage.toFixed(1)}%) - ${timing.avgPerEntity.toFixed(1)}μs/entity`);
}
console.log('\n最耗时的前3个步骤:');
for (let i = 0; i < Math.min(3, sortedTimings.length); i++) {
const timing = sortedTimings[i];
console.log(` ${i + 1}. ${timing.step}: ${timing.percentage.toFixed(1)}% (${timing.timeMs.toFixed(2)}ms)`);
}
});
test('对比纯Entity创建和完整创建流程', () => {
const testCount = 1000;
console.log(`\n=== 创建方式对比 (${testCount}个实体) ===`);
// 1. 纯Entity创建
let startTime = performance.now();
const pureEntities = [];
for (let i = 0; i < testCount; i++) {
pureEntities.push(new Entity(`Pure_${i}`, i));
}
const pureTime = performance.now() - startTime;
// 2. 完整EntityManager创建
startTime = performance.now();
const managedEntities = [];
for (let i = 0; i < testCount; i++) {
managedEntities.push(entityManager.createEntity(`Managed_${i}`));
}
const managedTime = performance.now() - startTime;
console.log(`纯Entity创建: ${pureTime.toFixed(2)}ms`);
console.log(`EntityManager创建: ${managedTime.toFixed(2)}ms`);
console.log(`性能差距: ${(managedTime / pureTime).toFixed(1)}`);
console.log(`管理开销: ${(managedTime - pureTime).toFixed(2)}ms (${((managedTime - pureTime) / managedTime * 100).toFixed(1)}%)`);
});
test('测量批量操作的效果', () => {
const testCount = 1000;
console.log(`\n=== 批量操作效果测试 (${testCount}个实体) ===`);
// 1. 逐个处理
let startTime = performance.now();
for (let i = 0; i < testCount; i++) {
entityManager.createEntity(`Individual_${i}`);
}
const individualTime = performance.now() - startTime;
entityManager = new EntityManager();
// 2. 批量处理
startTime = performance.now();
entityManager.createEntitiesBatch(testCount, "Batch", false);
const batchTime = performance.now() - startTime;
entityManager = new EntityManager();
// 3. 批量处理(跳过事件)
startTime = performance.now();
entityManager.createEntitiesBatch(testCount, "BatchNoEvents", true);
const batchNoEventsTime = performance.now() - startTime;
console.log(`逐个创建: ${individualTime.toFixed(2)}ms`);
console.log(`批量创建: ${batchTime.toFixed(2)}ms`);
console.log(`批量创建(跳过事件): ${batchNoEventsTime.toFixed(2)}ms`);
console.log(`批量vs逐个: ${(individualTime / batchTime).toFixed(2)}x`);
console.log(`跳过事件优化: ${(batchTime / batchNoEventsTime).toFixed(2)}x`);
});
test('分析最耗时组件的内部实现', () => {
console.log(`\n=== 最耗时组件内部分析 ===`);
const testCount = 500; // 较少数量以便详细分析
// 单独测试各个重要组件
const entity = new Entity("TestEntity", 1);
// 测试组件索引管理器
let startTime = performance.now();
for (let i = 0; i < testCount; i++) {
const testEntity = new Entity(`Test_${i}`, i);
entityManager['_componentIndexManager'].addEntity(testEntity);
}
const componentIndexTime = performance.now() - startTime;
// 测试原型系统
startTime = performance.now();
for (let i = 0; i < testCount; i++) {
const testEntity = new Entity(`Test_${i}`, i + testCount);
entityManager['_archetypeSystem'].addEntity(testEntity);
}
const archetypeTime = performance.now() - startTime;
// 测试脏标记系统
startTime = performance.now();
for (let i = 0; i < testCount; i++) {
const testEntity = new Entity(`Test_${i}`, i + testCount * 2);
entityManager['_dirtyTrackingSystem'].markDirty(testEntity, 1);
}
const dirtyTrackingTime = performance.now() - startTime;
// 测试事件发射
startTime = performance.now();
for (let i = 0; i < testCount; i++) {
entityManager['_eventBus'].emitEntityCreated({
timestamp: Date.now(),
source: 'EntityManager',
entityId: i,
entityName: `Event_${i}`,
entityTag: undefined
});
}
const eventTime = performance.now() - startTime;
console.log(`组件索引管理器: ${componentIndexTime.toFixed(2)}ms (${(componentIndexTime / testCount * 1000).toFixed(1)}μs/entity)`);
console.log(`原型系统: ${archetypeTime.toFixed(2)}ms (${(archetypeTime / testCount * 1000).toFixed(1)}μs/entity)`);
console.log(`脏标记系统: ${dirtyTrackingTime.toFixed(2)}ms (${(dirtyTrackingTime / testCount * 1000).toFixed(1)}μs/entity)`);
console.log(`事件发射: ${eventTime.toFixed(2)}ms (${(eventTime / testCount * 1000).toFixed(1)}μs/entity)`);
});
});

View File

@@ -0,0 +1,226 @@
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
describe('实体创建性能分析', () => {
let entityManager: EntityManager;
beforeEach(() => {
ComponentTypeManager.instance.reset();
entityManager = new EntityManager();
});
test('性能分析创建10000个实体', () => {
const entityCount = 10000;
console.log(`开始创建 ${entityCount} 个实体...`);
// 预热
for (let i = 0; i < 100; i++) {
entityManager.createEntity(`Warmup_${i}`);
}
// 重新创建EntityManager来清理
entityManager = new EntityManager();
// 测试不同的创建方式
console.log('\n=== 性能对比测试 ===');
// 1. 使用默认名称包含Date.now()
let startTime = performance.now();
const entitiesWithDefaultName: any[] = [];
for (let i = 0; i < entityCount; i++) {
entitiesWithDefaultName.push(entityManager.createEntity());
}
let endTime = performance.now();
console.log(`1. 默认名称创建: ${(endTime - startTime).toFixed(2)}ms`);
entityManager = new EntityManager();
// 2. 使用预设名称避免Date.now()
startTime = performance.now();
const entitiesWithPresetName: any[] = [];
for (let i = 0; i < entityCount; i++) {
entitiesWithPresetName.push(entityManager.createEntity(`Entity_${i}`));
}
endTime = performance.now();
console.log(`2. 预设名称创建: ${(endTime - startTime).toFixed(2)}ms`);
entityManager = new EntityManager();
// 3. 使用相同名称(减少字符串创建)
startTime = performance.now();
const entitiesWithSameName: any[] = [];
const sameName = 'SameName';
for (let i = 0; i < entityCount; i++) {
entitiesWithSameName.push(entityManager.createEntity(sameName));
}
endTime = performance.now();
console.log(`3. 相同名称创建: ${(endTime - startTime).toFixed(2)}ms`);
entityManager = new EntityManager();
// 4. 直接创建Entity对象绕过EntityManager
startTime = performance.now();
const directEntities: any[] = [];
for (let i = 0; i < entityCount; i++) {
// 直接创建Entity不通过EntityManager的复杂逻辑
directEntities.push(new (require('../../../src/ECS/Entity').Entity)(`Direct_${i}`, i));
}
endTime = performance.now();
console.log(`4. 直接创建Entity: ${(endTime - startTime).toFixed(2)}ms`);
console.log('\n=== 性能分析结论 ===');
console.log('如果相同名称创建明显更快,说明字符串操作是瓶颈');
console.log('如果直接创建Entity更快说明EntityManager的逻辑太重');
});
test('详细分析EntityManager中的性能瓶颈', () => {
const entityCount = 1000; // 较小数量便于分析
console.log('\n=== 详细性能分析 ===');
// 分析各个步骤的耗时
let totalTime = 0;
const stepTimes: Record<string, number> = {};
for (let i = 0; i < entityCount; i++) {
const stepStart = performance.now();
// 模拟EntityManager.createEntity的各个步骤
const name = `PerfTest_${i}`;
// 步骤1: ID分配
let stepTime = performance.now();
const id = entityManager['_identifierPool'].checkOut();
stepTimes['ID分配'] = (stepTimes['ID分配'] || 0) + (performance.now() - stepTime);
// 步骤2: Entity创建
stepTime = performance.now();
const entity = new (require('../../../src/ECS/Entity').Entity)(name, id);
stepTimes['Entity创建'] = (stepTimes['Entity创建'] || 0) + (performance.now() - stepTime);
// 步骤3: 各种索引更新
stepTime = performance.now();
entityManager['_entities'].set(id, entity);
stepTimes['Map存储'] = (stepTimes['Map存储'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
entityManager['updateNameIndex'](entity, true);
stepTimes['名称索引'] = (stepTimes['名称索引'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
entityManager['updateTagIndex'](entity, true);
stepTimes['标签索引'] = (stepTimes['标签索引'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
entityManager['_componentIndexManager'].addEntity(entity);
stepTimes['组件索引'] = (stepTimes['组件索引'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
entityManager['_archetypeSystem'].addEntity(entity);
stepTimes['原型系统'] = (stepTimes['原型系统'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
entityManager['_dirtyTrackingSystem'].markDirty(entity, 1); // DirtyFlag.COMPONENT_ADDED
stepTimes['脏标记'] = (stepTimes['脏标记'] || 0) + (performance.now() - stepTime);
stepTime = performance.now();
// 跳过事件发射,因为它涉及复杂的对象创建
stepTimes['其他'] = (stepTimes['其他'] || 0) + (performance.now() - stepTime);
totalTime += (performance.now() - stepStart);
}
console.log(`总耗时: ${totalTime.toFixed(2)}ms`);
console.log('各步骤平均耗时:');
for (const [step, time] of Object.entries(stepTimes)) {
console.log(` ${step}: ${(time / entityCount * 1000).toFixed(3)}μs/entity`);
}
// 找出最耗时的步骤
const maxTime = Math.max(...Object.values(stepTimes));
const slowestStep = Object.entries(stepTimes).find(([_, time]) => time === maxTime)?.[0];
console.log(`最耗时的步骤: ${slowestStep} (${(maxTime / entityCount * 1000).toFixed(3)}μs/entity)`);
});
test('测试批量创建优化方案', () => {
const entityCount = 10000;
console.log(`\n=== 批量创建优化测试 ===`);
// 当前方式:逐个创建
let startTime = performance.now();
for (let i = 0; i < entityCount; i++) {
entityManager.createEntity(`Current_${i}`);
}
let endTime = performance.now();
const currentTime = endTime - startTime;
console.log(`当前方式: ${currentTime.toFixed(2)}ms`);
entityManager = new EntityManager();
// 如果有批量创建方法的话...
// 这里只是演示概念实际的批量创建需要在EntityManager中实现
console.log('建议:实现批量创建方法,减少重复的索引更新和事件发射');
});
test('验证批量创建优化效果', () => {
const entityCount = 10000;
console.log(`\n=== 批量创建优化效果验证 ===`);
// 测试新的批量创建方法
let startTime = performance.now();
const batchEntities = entityManager.createEntitiesBatch(entityCount, "Batch", false);
let endTime = performance.now();
const batchTime = endTime - startTime;
console.log(`批量创建(含事件): ${batchTime.toFixed(2)}ms`);
entityManager = new EntityManager();
// 测试跳过事件的批量创建
startTime = performance.now();
const batchEntitiesNoEvents = entityManager.createEntitiesBatch(entityCount, "BatchNoEvents", true);
endTime = performance.now();
const batchTimeNoEvents = endTime - startTime;
console.log(`批量创建(跳过事件): ${batchTimeNoEvents.toFixed(2)}ms`);
entityManager = new EntityManager();
// 对比单个创建使用优化后的createEntity
startTime = performance.now();
const singleEntities: any[] = [];
for (let i = 0; i < entityCount; i++) {
singleEntities.push(entityManager.createEntity(`Single_${i}`));
}
endTime = performance.now();
const singleTime = endTime - startTime;
console.log(`优化后单个创建: ${singleTime.toFixed(2)}ms`);
console.log(`\n性能提升:`);
console.log(`批量创建 vs 单个创建: ${(singleTime / batchTime).toFixed(1)}x faster`);
console.log(`批量创建(跳过事件) vs 单个创建: ${(singleTime / batchTimeNoEvents).toFixed(1)}x faster`);
// 验证功能正确性
expect(batchEntities.length).toBe(entityCount);
expect(batchEntitiesNoEvents.length).toBe(entityCount);
expect(singleEntities.length).toBe(entityCount);
});
test('验证createEntity的Date.now()优化', () => {
console.log(`\n=== createEntity优化验证 ===`);
const testCount = 1000;
// 测试优化后的默认名称生成
let startTime = performance.now();
for (let i = 0; i < testCount; i++) {
entityManager.createEntity(); // 使用优化后的计数器命名
}
let endTime = performance.now();
console.log(`计数器命名: ${(endTime - startTime).toFixed(2)}ms`);
entityManager = new EntityManager();
// 对比模拟使用Date.now()的方式
startTime = performance.now();
for (let i = 0; i < testCount; i++) {
entityManager.createEntity(`Entity_${Date.now()}_${i}`); // 模拟原来的方式
}
endTime = performance.now();
console.log(`Date.now()命名: ${(endTime - startTime).toFixed(2)}ms`);
});
});

View File

@@ -0,0 +1,626 @@
import { EntityManager, EntityQueryBuilder } from '../../../src/ECS/Core/EntityManager';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
// 测试组件
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class HealthComponent extends Component {
public health: number;
public maxHealth: number;
constructor(...args: unknown[]) {
super();
const [health = 100, maxHealth = 100] = args as [number?, number?];
this.health = health;
this.maxHealth = maxHealth;
}
}
class RenderComponent extends Component {
public visible: boolean;
public color: string;
constructor(...args: unknown[]) {
super();
const [visible = true, color = 'white'] = args as [boolean?, string?];
this.visible = visible;
this.color = color;
}
}
class AIComponent extends Component {
public intelligence: number;
constructor(...args: unknown[]) {
super();
const [intelligence = 50] = args as [number?];
this.intelligence = intelligence;
}
}
class PlayerComponent extends Component {
public name: string;
constructor(...args: unknown[]) {
super();
const [name = 'Player'] = args as [string?];
this.name = name;
}
}
describe('EntityManager - 实体管理器测试', () => {
let entityManager: EntityManager;
beforeEach(() => {
entityManager = new EntityManager();
});
describe('基本功能测试', () => {
test('应该能够创建EntityManager实例', () => {
expect(entityManager).toBeDefined();
expect(entityManager).toBeInstanceOf(EntityManager);
expect(entityManager.entityCount).toBe(0);
});
test('应该能够创建实体', () => {
const entity = entityManager.createEntity('TestEntity');
expect(entity).toBeDefined();
expect(entity.name).toBe('TestEntity');
expect(entity.id).toBeGreaterThan(0);
expect(entityManager.entityCount).toBe(1);
});
test('应该能够创建实体使用默认名称', () => {
const entity = entityManager.createEntity();
expect(entity).toBeDefined();
expect(entity.name).toContain('Entity_');
expect(entity.id).toBeGreaterThan(0);
});
test('应该能够批量创建实体', () => {
const entities: Entity[] = [];
for (let i = 0; i < 5; i++) {
entities.push(entityManager.createEntity(`Entity_${i}`));
}
expect(entities.length).toBe(5);
expect(entityManager.entityCount).toBe(5);
for (let i = 0; i < entities.length; i++) {
expect(entities[i].name).toBe(`Entity_${i}`);
expect(entities[i].id).toBeGreaterThan(0);
}
});
test('应该能够通过ID查找实体', () => {
const entity = entityManager.createEntity('TestEntity');
const found = entityManager.getEntity(entity.id);
expect(found).toBe(entity);
});
test('查找不存在的实体应该返回null', () => {
const found = entityManager.getEntity(999999);
expect(found).toBeNull();
});
test('应该能够销毁实体', () => {
const entity = entityManager.createEntity('TestEntity');
const entityId = entity.id;
const result = entityManager.destroyEntity(entity);
expect(result).toBe(true);
expect(entityManager.getEntity(entityId)).toBeNull();
expect(entityManager.entityCount).toBe(0);
});
test('应该能够通过ID销毁实体', () => {
const entity = entityManager.createEntity('TestEntity');
const entityId = entity.id;
const result = entityManager.destroyEntity(entityId);
expect(result).toBe(true);
expect(entityManager.getEntity(entityId)).toBeNull();
});
test('销毁不存在的实体应该返回false', () => {
const result = entityManager.destroyEntity(999999);
expect(result).toBe(false);
});
test('应该正确统计激活状态的实体', () => {
const entity1 = entityManager.createEntity('Active1');
const entity2 = entityManager.createEntity('Active2');
const entity3 = entityManager.createEntity('Inactive');
entity3.active = false;
expect(entityManager.activeEntityCount).toBe(2);
expect(entityManager.entityCount).toBe(3);
});
});
describe('实体标签功能测试', () => {
test('实体应该有默认标签', () => {
const entity = entityManager.createEntity('TaggedEntity');
expect(entity.tag).toBe(0); // 默认标签为0
});
test('应该能够为实体设置标签', () => {
const entity = entityManager.createEntity('TaggedEntity');
entity.tag = 1;
expect(entity.tag).toBe(1);
});
test('应该能够按标签查询实体', () => {
const entity1 = entityManager.createEntity('Entity1');
const entity2 = entityManager.createEntity('Entity2');
const entity3 = entityManager.createEntity('Entity3');
entity1.tag = 1;
entity2.tag = 1;
entity3.tag = 2;
const tag1Entities = entityManager.getEntitiesByTag(1);
const tag2Entities = entityManager.getEntitiesByTag(2);
expect(tag1Entities.length).toBe(2);
expect(tag2Entities.length).toBe(1);
expect(tag1Entities).toContain(entity1);
expect(tag1Entities).toContain(entity2);
expect(tag2Entities).toContain(entity3);
});
test('查询不存在的标签应该返回空数组', () => {
const entities = entityManager.getEntitiesByTag(999);
expect(entities).toEqual([]);
});
});
describe('查询构建器测试', () => {
let player: Entity;
let enemy1: Entity;
let enemy2: Entity;
let npc: Entity;
beforeEach(() => {
// 创建测试实体
player = entityManager.createEntity('Player');
player.addComponent(new PositionComponent(50, 50));
player.addComponent(new HealthComponent(100, 100));
player.addComponent(new PlayerComponent('Hero'));
player.tag = 1;
enemy1 = entityManager.createEntity('Enemy1');
enemy1.addComponent(new PositionComponent(10, 10));
enemy1.addComponent(new VelocityComponent(1, 0));
enemy1.addComponent(new HealthComponent(50, 50));
enemy1.addComponent(new AIComponent(30));
enemy1.tag = 2;
enemy2 = entityManager.createEntity('Enemy2');
enemy2.addComponent(new PositionComponent(90, 90));
enemy2.addComponent(new VelocityComponent(-1, 0));
enemy2.addComponent(new HealthComponent(75, 75));
enemy2.addComponent(new AIComponent(45));
enemy2.tag = 2;
npc = entityManager.createEntity('NPC');
npc.addComponent(new PositionComponent(25, 75));
npc.addComponent(new RenderComponent(true, 'blue'));
npc.tag = 3;
});
test('应该能够查询具有所有指定组件的实体', () => {
const results = entityManager.query()
.withAll(PositionComponent, HealthComponent)
.execute();
expect(results.length).toBe(3); // player, enemy1, enemy2
expect(results).toContain(player);
expect(results).toContain(enemy1);
expect(results).toContain(enemy2);
expect(results).not.toContain(npc);
});
test('应该能够查询具有任意指定组件的实体', () => {
const results = entityManager.query()
.withAny(PlayerComponent, AIComponent)
.execute();
expect(results.length).toBe(3); // player, enemy1, enemy2
expect(results).toContain(player);
expect(results).toContain(enemy1);
expect(results).toContain(enemy2);
expect(results).not.toContain(npc);
});
test('应该能够排除具有指定组件的实体', () => {
const results = entityManager.query()
.withAll(PositionComponent)
.without(AIComponent)
.execute();
expect(results.length).toBe(2); // player, npc
expect(results).toContain(player);
expect(results).toContain(npc);
expect(results).not.toContain(enemy1);
expect(results).not.toContain(enemy2);
});
test('应该能够按标签过滤实体', () => {
const results = entityManager.query()
.withTag(2)
.execute();
expect(results.length).toBe(2); // enemy1, enemy2
expect(results).toContain(enemy1);
expect(results).toContain(enemy2);
expect(results).not.toContain(player);
expect(results).not.toContain(npc);
});
test('应该能够排除特定标签的实体', () => {
const results = entityManager.query()
.withAll(PositionComponent)
.withoutTag(2)
.execute();
expect(results.length).toBe(2); // player, npc
expect(results).toContain(player);
expect(results).toContain(npc);
expect(results).not.toContain(enemy1);
expect(results).not.toContain(enemy2);
});
test('应该能够只查询激活状态的实体', () => {
enemy1.active = false;
const results = entityManager.query()
.withAll(PositionComponent, HealthComponent)
.active()
.execute();
expect(results.length).toBe(2); // player, enemy2
expect(results).toContain(player);
expect(results).toContain(enemy2);
expect(results).not.toContain(enemy1);
});
test('应该能够只查询启用状态的实体', () => {
npc.enabled = false;
const results = entityManager.query()
.withAll(PositionComponent)
.enabled()
.execute();
expect(results.length).toBe(3); // player, enemy1, enemy2
expect(results).toContain(player);
expect(results).toContain(enemy1);
expect(results).toContain(enemy2);
expect(results).not.toContain(npc);
});
test('应该能够使用自定义过滤条件', () => {
const results = entityManager.query()
.withAll(HealthComponent)
.where(entity => {
const health = entity.getComponent(HealthComponent);
return health!.health > 60;
})
.execute();
expect(results.length).toBe(2); // player, enemy2
expect(results).toContain(player);
expect(results).toContain(enemy2);
expect(results).not.toContain(enemy1);
});
test('应该能够组合多个查询条件', () => {
const results = entityManager.query()
.withAll(PositionComponent, HealthComponent)
.without(PlayerComponent)
.withTag(2)
.where(entity => {
const position = entity.getComponent(PositionComponent);
return position!.x < 50;
})
.execute();
expect(results.length).toBe(1); // enemy1
expect(results).toContain(enemy1);
expect(results).not.toContain(player);
expect(results).not.toContain(enemy2);
expect(results).not.toContain(npc);
});
test('空查询应该返回所有实体', () => {
const results = entityManager.query().execute();
expect(results.length).toBe(4); // all entities
expect(results).toContain(player);
expect(results).toContain(enemy1);
expect(results).toContain(enemy2);
expect(results).toContain(npc);
});
test('不匹配的查询应该返回空数组', () => {
const results = entityManager.query()
.withAll(PlayerComponent, AIComponent) // 不可能的组合
.execute();
expect(results).toEqual([]);
});
});
describe('事件系统集成', () => {
test('创建实体应该触发事件', () => {
let eventData: any = null;
entityManager.eventBus.onEntityCreated((data) => {
eventData = data;
});
const entity = entityManager.createEntity('EventEntity');
expect(eventData).toBeDefined();
expect(eventData.entityName).toBe('EventEntity');
expect(eventData.entityId).toBe(entity.id);
});
test('销毁实体应该触发事件', () => {
let eventData: any = null;
entityManager.eventBus.on('entity:destroyed', (data: any) => {
eventData = data;
});
const entity = entityManager.createEntity('EventEntity');
entityManager.destroyEntity(entity);
expect(eventData).toBeDefined();
expect(eventData.entityName).toBe('EventEntity');
expect(eventData.entityId).toBe(entity.id);
});
test('添加组件应该触发事件', () => {
let eventData: any = null;
entityManager.eventBus.onComponentAdded((data) => {
eventData = data;
});
const entity = entityManager.createEntity('ComponentEntity');
entity.addComponent(new PositionComponent(10, 20));
expect(eventData).toBeDefined();
expect(eventData.componentType).toBe('PositionComponent');
expect(eventData.entityId).toBe(entity.id);
});
test('移除组件应该触发事件', () => {
let eventData: any = null;
entityManager.eventBus.on('component:removed', (data: any) => {
eventData = data;
});
const entity = entityManager.createEntity('ComponentEntity');
const component = entity.addComponent(new PositionComponent(10, 20));
entity.removeComponent(component);
expect(eventData).toBeDefined();
expect(eventData.componentType).toBe('PositionComponent');
expect(eventData.entityId).toBe(entity.id);
});
});
describe('性能和内存测试', () => {
test('大量实体创建性能应该可接受', () => {
const entityCount = 10000;
const startTime = performance.now();
const entities: Entity[] = [];
for (let i = 0; i < entityCount; i++) {
entities.push(entityManager.createEntity(`PerfEntity_${i}`));
}
const endTime = performance.now();
const duration = endTime - startTime;
expect(entities.length).toBe(entityCount);
expect(entityManager.entityCount).toBe(entityCount);
expect(duration).toBeLessThan(1000); // 应该在1秒内完成
console.log(`创建${entityCount}个实体耗时: ${duration.toFixed(2)}ms`);
});
test('大量实体查询性能应该可接受', () => {
const entityCount = 5000;
// 创建大量实体并添加组件
for (let i = 0; i < entityCount; i++) {
const entity = entityManager.createEntity(`Entity_${i}`);
entity.addComponent(new PositionComponent(i, i));
if (i % 2 === 0) {
entity.addComponent(new VelocityComponent(1, 1));
}
if (i % 3 === 0) {
entity.addComponent(new HealthComponent(100));
}
}
const startTime = performance.now();
// 执行多个查询
const positionResults = entityManager.query().withAll(PositionComponent).execute();
const velocityResults = entityManager.query().withAll(VelocityComponent).execute();
const healthResults = entityManager.query().withAll(HealthComponent).execute();
const complexResults = entityManager.query()
.withAll(PositionComponent, VelocityComponent)
.without(HealthComponent)
.execute();
const endTime = performance.now();
const duration = endTime - startTime;
expect(positionResults.length).toBe(entityCount);
expect(velocityResults.length).toBe(entityCount / 2);
expect(healthResults.length).toBe(Math.floor(entityCount / 3) + 1);
expect(duration).toBeLessThan(200); // 应该在200ms内完成
console.log(`${entityCount}个实体的复杂查询耗时: ${duration.toFixed(2)}ms`);
});
test('实体销毁应该正确清理内存', () => {
const entityCount = 1000;
const entities: Entity[] = [];
// 创建实体
for (let i = 0; i < entityCount; i++) {
const entity = entityManager.createEntity(`MemoryEntity_${i}`);
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new HealthComponent(100));
entities.push(entity);
}
expect(entityManager.entityCount).toBe(entityCount);
// 销毁所有实体
entities.forEach(entity => {
entityManager.destroyEntity(entity);
});
// 验证所有实体都已被清理
expect(entityManager.entityCount).toBe(0);
entities.forEach(entity => {
expect(entityManager.getEntity(entity.id)).toBeNull();
});
// 查询应该返回空结果
const positionResults = entityManager.query().withAll(PositionComponent).execute();
const healthResults = entityManager.query().withAll(HealthComponent).execute();
expect(positionResults).toEqual([]);
expect(healthResults).toEqual([]);
});
});
describe('错误处理和边界情况', () => {
test('对已销毁实体的查询操作应该安全处理', () => {
const entity = entityManager.createEntity('ToBeDestroyed');
entity.addComponent(new PositionComponent(0, 0));
entityManager.destroyEntity(entity);
// 查询不应该包含已销毁的实体
const results = entityManager.query().withAll(PositionComponent).execute();
expect(results).not.toContain(entity);
});
test('空查询构建器应该能正常工作', () => {
const builder = entityManager.query();
expect(() => {
const results = builder.execute();
expect(Array.isArray(results)).toBe(true);
}).not.toThrow();
});
test('重复销毁同一实体应该安全处理', () => {
const entity = entityManager.createEntity('TestEntity');
const result1 = entityManager.destroyEntity(entity);
const result2 = entityManager.destroyEntity(entity);
expect(result1).toBe(true);
expect(result2).toBe(false);
});
test('销毁实体后其组件应该正确清理', () => {
const entity = entityManager.createEntity('TestEntity');
entity.addComponent(new PositionComponent(10, 20));
entity.addComponent(new HealthComponent(100));
const initialPositionResults = entityManager.query().withAll(PositionComponent).execute();
expect(initialPositionResults).toContain(entity);
entityManager.destroyEntity(entity);
const finalPositionResults = entityManager.query().withAll(PositionComponent).execute();
expect(finalPositionResults).not.toContain(entity);
});
});
describe('统计和调试信息', () => {
test('应该能够获取实体管理器统计信息', () => {
// 创建一些实体和组件
const entities: Entity[] = [];
for (let i = 0; i < 10; i++) {
const entity = entityManager.createEntity(`StatsEntity_${i}`);
entity.addComponent(new PositionComponent(0, 0));
entities.push(entity);
}
// EntityManager doesn't have getStats method, use basic counts
expect(entityManager.entityCount).toBe(10);
expect(entityManager.activeEntityCount).toBe(10);
});
test('销毁实体后统计信息应该更新', () => {
const entities: Entity[] = [];
for (let i = 0; i < 5; i++) {
entities.push(entityManager.createEntity(`StatsEntity_${i}`));
}
entityManager.destroyEntity(entities[0]);
entityManager.destroyEntity(entities[1]);
expect(entityManager.entityCount).toBe(3);
expect(entityManager.activeEntityCount).toBe(3);
});
test('非激活实体应该在统计中正确反映', () => {
const entities: Entity[] = [];
for (let i = 0; i < 5; i++) {
entities.push(entityManager.createEntity(`StatsEntity_${i}`));
}
entities[0].active = false;
entities[1].active = false;
expect(entityManager.entityCount).toBe(5);
expect(entityManager.activeEntityCount).toBe(3);
});
});
});

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({
operation: 'frame_render',
executionTime: 16.67,
metadata: { fps: 30, threshold: 60, message: 'FPS dropped below threshold' },
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 as any).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 as any).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

@@ -0,0 +1,624 @@
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 () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
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);
consoleSpy.mockRestore();
});
});
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('监听器中的错误不应该影响其他监听器', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
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);
consoleSpy.mockRestore();
});
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);
});
});
});

View File

@@ -0,0 +1,691 @@
import {
EntityBuilder,
SceneBuilder,
ComponentBuilder,
ECSFluentAPI,
EntityBatchOperator,
createECSAPI,
initializeECS,
ECS
} from '../../../src/ECS/Core/FluentAPI';
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { QuerySystem } from '../../../src/ECS/Core/QuerySystem';
import { TypeSafeEventSystem } from '../../../src/ECS/Core/EventSystem';
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
// 测试组件
class TestComponent extends Component {
public value: number;
constructor(...args: unknown[]) {
super();
const [value = 0] = args as [number?];
this.value = value;
}
}
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
// 测试系统
class TestSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(TestComponent));
}
protected override process(entities: Entity[]): void {
// 测试系统
}
}
describe('FluentAPI - 流式API测试', () => {
let scene: Scene;
let querySystem: QuerySystem;
let eventSystem: TypeSafeEventSystem;
beforeEach(() => {
scene = new Scene();
querySystem = new QuerySystem();
eventSystem = new TypeSafeEventSystem();
});
describe('EntityBuilder - 实体构建器', () => {
let builder: EntityBuilder;
beforeEach(() => {
builder = new EntityBuilder(scene, scene.componentStorageManager);
});
test('应该能够创建实体构建器', () => {
expect(builder).toBeInstanceOf(EntityBuilder);
});
test('应该能够设置实体名称', () => {
const entity = builder.named('TestEntity').build();
expect(entity.name).toBe('TestEntity');
});
test('应该能够设置实体标签', () => {
const entity = builder.tagged(42).build();
expect(entity.tag).toBe(42);
});
test('应该能够添加组件', () => {
const component = new TestComponent(100);
const entity = builder.with(component).build();
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.getComponent(TestComponent)).toBe(component);
});
test('应该能够添加多个组件', () => {
const comp1 = new TestComponent(100);
const comp2 = new PositionComponent(10, 20);
const comp3 = new VelocityComponent(1, 2);
const entity = builder.withComponents(comp1, comp2, comp3).build();
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.hasComponent(PositionComponent)).toBe(true);
expect(entity.hasComponent(VelocityComponent)).toBe(true);
});
test('应该能够条件性添加组件', () => {
const comp1 = new TestComponent(100);
const comp2 = new PositionComponent(10, 20);
const entity = builder
.withIf(true, comp1)
.withIf(false, comp2)
.build();
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.hasComponent(PositionComponent)).toBe(false);
});
test('应该能够使用工厂函数创建组件', () => {
const entity = builder
.withFactory(() => new TestComponent(200))
.build();
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.getComponent(TestComponent)!.value).toBe(200);
});
test('应该能够配置组件属性', () => {
const entity = builder
.with(new TestComponent(100))
.configure(TestComponent, (comp) => {
comp.value = 300;
})
.build();
expect(entity.getComponent(TestComponent)!.value).toBe(300);
});
test('配置不存在的组件应该安全处理', () => {
expect(() => {
builder.configure(TestComponent, (comp) => {
comp.value = 300;
}).build();
}).not.toThrow();
});
test('应该能够设置实体启用状态', () => {
const entity1 = builder.enabled(true).build();
const entity2 = new EntityBuilder(scene, scene.componentStorageManager).enabled(false).build();
expect(entity1.enabled).toBe(true);
expect(entity2.enabled).toBe(false);
});
test('应该能够设置实体活跃状态', () => {
const entity1 = builder.active(true).build();
const entity2 = new EntityBuilder(scene, scene.componentStorageManager).active(false).build();
expect(entity1.active).toBe(true);
expect(entity2.active).toBe(false);
});
test('应该能够添加子实体', () => {
const childBuilder = new EntityBuilder(scene, scene.componentStorageManager)
.named('Child')
.with(new TestComponent(50));
const parent = builder
.named('Parent')
.withChild(childBuilder)
.build();
expect(parent.children.length).toBe(1);
expect(parent.children[0].name).toBe('Child');
});
test('应该能够添加多个子实体', () => {
const child1 = new EntityBuilder(scene, scene.componentStorageManager).named('Child1');
const child2 = new EntityBuilder(scene, scene.componentStorageManager).named('Child2');
const child3 = new EntityBuilder(scene, scene.componentStorageManager).named('Child3');
const parent = builder
.named('Parent')
.withChildren(child1, child2, child3)
.build();
expect(parent.children.length).toBe(3);
expect(parent.children[0].name).toBe('Child1');
expect(parent.children[1].name).toBe('Child2');
expect(parent.children[2].name).toBe('Child3');
});
test('应该能够使用工厂函数创建子实体', () => {
const parent = builder
.named('Parent')
.withChildFactory((parentEntity) => {
return new EntityBuilder(scene, scene.componentStorageManager)
.named(`Child_of_${parentEntity.name}`)
.with(new TestComponent(100));
})
.build();
expect(parent.children.length).toBe(1);
expect(parent.children[0].name).toBe('Child_of_Parent');
});
test('应该能够条件性添加子实体', () => {
const child1 = new EntityBuilder(scene, scene.componentStorageManager).named('Child1');
const child2 = new EntityBuilder(scene, scene.componentStorageManager).named('Child2');
const parent = builder
.named('Parent')
.withChildIf(true, child1)
.withChildIf(false, child2)
.build();
expect(parent.children.length).toBe(1);
expect(parent.children[0].name).toBe('Child1');
});
test('应该能够构建实体并添加到场景', () => {
const entity = builder
.named('SpawnedEntity')
.with(new TestComponent(100))
.spawn();
expect(entity.name).toBe('SpawnedEntity');
expect(entity.scene).toBe(scene);
});
test('应该能够克隆构建器', () => {
const originalBuilder = builder.named('Original').with(new TestComponent(100));
const clonedBuilder = originalBuilder.clone();
expect(clonedBuilder).toBeInstanceOf(EntityBuilder);
expect(clonedBuilder).not.toBe(originalBuilder);
});
test('流式调用应该工作正常', () => {
const entity = builder
.named('ComplexEntity')
.tagged(42)
.with(new TestComponent(100))
.with(new PositionComponent(10, 20))
.enabled(true)
.active(true)
.configure(TestComponent, (comp) => {
comp.value = 200;
})
.build();
expect(entity.name).toBe('ComplexEntity');
expect(entity.tag).toBe(42);
expect(entity.enabled).toBe(true);
expect(entity.active).toBe(true);
expect(entity.hasComponent(TestComponent)).toBe(true);
expect(entity.hasComponent(PositionComponent)).toBe(true);
expect(entity.getComponent(TestComponent)!.value).toBe(200);
});
});
describe('SceneBuilder - 场景构建器', () => {
let builder: SceneBuilder;
beforeEach(() => {
builder = new SceneBuilder();
});
test('应该能够创建场景构建器', () => {
expect(builder).toBeInstanceOf(SceneBuilder);
});
test('应该能够设置场景名称', () => {
const scene = builder.named('TestScene').build();
expect(scene.name).toBe('TestScene');
});
test('应该能够添加实体', () => {
const entity = new Entity('TestEntity', 1);
const scene = builder.withEntity(entity).build();
expect(scene.entities.count).toBe(1);
expect(scene.findEntity('TestEntity')).toBe(entity);
});
test('应该能够使用实体构建器添加实体', () => {
const scene = builder
.withEntityBuilder((builder) => {
return builder
.named('BuilderEntity')
.with(new TestComponent(100));
})
.build();
expect(scene.entities.count).toBe(1);
expect(scene.findEntity('BuilderEntity')).not.toBeNull();
});
test('应该能够批量添加实体', () => {
const entity1 = new Entity('Entity1', 1);
const entity2 = new Entity('Entity2', 2);
const entity3 = new Entity('Entity3', 3);
const scene = builder
.withEntities(entity1, entity2, entity3)
.build();
expect(scene.entities.count).toBe(3);
});
test('应该能够添加系统', () => {
const system = new TestSystem();
const scene = builder.withSystem(system).build();
expect(scene.systems.length).toBe(1);
expect(scene.systems[0]).toBe(system);
});
test('应该能够批量添加系统', () => {
const system1 = new TestSystem();
const system2 = new TestSystem();
const scene = builder
.withSystems(system1, system2)
.build();
expect(scene.systems.length).toBe(2);
});
test('流式调用应该工作正常', () => {
const entity = new Entity('TestEntity', 1);
const system = new TestSystem();
const scene = builder
.named('ComplexScene')
.withEntity(entity)
.withSystem(system)
.withEntityBuilder((builder) => {
return builder.named('BuilderEntity');
})
.build();
expect(scene.name).toBe('ComplexScene');
expect(scene.entities.count).toBe(2);
expect(scene.systems.length).toBe(1);
});
});
describe('ComponentBuilder - 组件构建器', () => {
test('应该能够创建组件构建器', () => {
const builder = new ComponentBuilder(TestComponent, 100);
expect(builder).toBeInstanceOf(ComponentBuilder);
});
test('应该能够设置组件属性', () => {
const component = new ComponentBuilder(TestComponent, 100)
.set('value', 200)
.build();
expect(component.value).toBe(200);
});
test('应该能够使用配置函数', () => {
const component = new ComponentBuilder(PositionComponent, 10, 20)
.configure((comp) => {
comp.x = 30;
comp.y = 40;
})
.build();
expect(component.x).toBe(30);
expect(component.y).toBe(40);
});
test('应该能够条件性设置属性', () => {
const component = new ComponentBuilder(TestComponent, 100)
.setIf(true, 'value', 200)
.setIf(false, 'value', 300)
.build();
expect(component.value).toBe(200);
});
test('流式调用应该工作正常', () => {
const component = new ComponentBuilder(PositionComponent, 0, 0)
.set('x', 10)
.set('y', 20)
.setIf(true, 'x', 30)
.configure((comp) => {
comp.y = 40;
})
.build();
expect(component.x).toBe(30);
expect(component.y).toBe(40);
});
});
describe('ECSFluentAPI - 主API', () => {
let api: ECSFluentAPI;
beforeEach(() => {
api = new ECSFluentAPI(scene, querySystem, eventSystem);
});
test('应该能够创建ECS API', () => {
expect(api).toBeInstanceOf(ECSFluentAPI);
});
test('应该能够创建实体构建器', () => {
const builder = api.createEntity();
expect(builder).toBeInstanceOf(EntityBuilder);
});
test('应该能够创建场景构建器', () => {
const builder = api.createScene();
expect(builder).toBeInstanceOf(SceneBuilder);
});
test('应该能够创建组件构建器', () => {
const builder = api.createComponent(TestComponent, 100);
expect(builder).toBeInstanceOf(ComponentBuilder);
});
test('应该能够创建查询构建器', () => {
const builder = api.query();
expect(builder).toBeDefined();
});
test('应该能够查找实体', () => {
const entity = scene.createEntity('TestEntity');
entity.addComponent(new TestComponent(100));
querySystem.setEntities([entity]);
const results = api.find(TestComponent);
expect(results.length).toBe(1);
expect(results[0]).toBe(entity);
});
test('应该能够查找第一个匹配的实体', () => {
const entity1 = scene.createEntity('Entity1');
const entity2 = scene.createEntity('Entity2');
entity1.addComponent(new TestComponent(100));
entity2.addComponent(new TestComponent(200));
querySystem.setEntities([entity1, entity2]);
const result = api.findFirst(TestComponent);
expect(result).not.toBeNull();
expect([entity1, entity2]).toContain(result);
});
test('查找不存在的实体应该返回null', () => {
const result = api.findFirst(TestComponent);
expect(result).toBeNull();
});
test('应该能够按名称查找实体', () => {
const entity = scene.createEntity('TestEntity');
const result = api.findByName('TestEntity');
expect(result).toBe(entity);
});
test('应该能够按标签查找实体', () => {
const entity1 = scene.createEntity('Entity1');
const entity2 = scene.createEntity('Entity2');
entity1.tag = 42;
entity2.tag = 42;
const results = api.findByTag(42);
expect(results.length).toBe(2);
expect(results).toContain(entity1);
expect(results).toContain(entity2);
});
test('应该能够触发同步事件', () => {
let eventReceived = false;
let eventData: any = null;
api.on('test:event', (data) => {
eventReceived = true;
eventData = data;
});
api.emit('test:event', { message: 'hello' });
expect(eventReceived).toBe(true);
expect(eventData.message).toBe('hello');
});
test('应该能够触发异步事件', async () => {
let eventReceived = false;
let eventData: any = null;
api.on('test:event', (data) => {
eventReceived = true;
eventData = data;
});
await api.emitAsync('test:event', { message: 'hello' });
expect(eventReceived).toBe(true);
expect(eventData.message).toBe('hello');
});
test('应该能够一次性监听事件', () => {
let callCount = 0;
api.once('test:event', () => {
callCount++;
});
api.emit('test:event', {});
api.emit('test:event', {});
expect(callCount).toBe(1);
});
test('应该能够移除事件监听器', () => {
let callCount = 0;
const listenerId = api.on('test:event', () => {
callCount++;
});
api.emit('test:event', {});
api.off('test:event', listenerId);
api.emit('test:event', {});
expect(callCount).toBe(1);
});
test('应该能够创建批量操作器', () => {
const entity1 = new Entity('Entity1', 1);
const entity2 = new Entity('Entity2', 2);
const batch = api.batch([entity1, entity2]);
expect(batch).toBeInstanceOf(EntityBatchOperator);
});
test('应该能够获取统计信息', () => {
const stats = api.getStats();
expect(stats).toBeDefined();
expect(stats.entityCount).toBeDefined();
expect(stats.systemCount).toBeDefined();
expect(stats.componentStats).toBeDefined();
expect(stats.queryStats).toBeDefined();
expect(stats.eventStats).toBeDefined();
});
});
describe('EntityBatchOperator - 批量操作器', () => {
let entity1: Entity;
let entity2: Entity;
let entity3: Entity;
let batchOp: EntityBatchOperator;
beforeEach(() => {
entity1 = new Entity('Entity1', 1);
entity2 = new Entity('Entity2', 2);
entity3 = new Entity('Entity3', 3);
batchOp = new EntityBatchOperator([entity1, entity2, entity3]);
});
test('应该能够创建批量操作器', () => {
expect(batchOp).toBeInstanceOf(EntityBatchOperator);
});
test('应该能够批量添加组件', () => {
const component = new TestComponent(100);
batchOp.addComponent(component);
expect(entity1.hasComponent(TestComponent)).toBe(true);
expect(entity2.hasComponent(TestComponent)).toBe(true);
expect(entity3.hasComponent(TestComponent)).toBe(true);
});
test('应该能够批量移除组件', () => {
entity1.addComponent(new TestComponent(100));
entity2.addComponent(new TestComponent(200));
entity3.addComponent(new TestComponent(300));
batchOp.removeComponent(TestComponent);
expect(entity1.hasComponent(TestComponent)).toBe(false);
expect(entity2.hasComponent(TestComponent)).toBe(false);
expect(entity3.hasComponent(TestComponent)).toBe(false);
});
test('应该能够批量设置活跃状态', () => {
batchOp.setActive(false);
expect(entity1.active).toBe(false);
expect(entity2.active).toBe(false);
expect(entity3.active).toBe(false);
});
test('应该能够批量设置标签', () => {
batchOp.setTag(42);
expect(entity1.tag).toBe(42);
expect(entity2.tag).toBe(42);
expect(entity3.tag).toBe(42);
});
test('应该能够批量执行操作', () => {
const names: string[] = [];
const indices: number[] = [];
batchOp.forEach((entity, index) => {
names.push(entity.name);
indices.push(index);
});
expect(names).toEqual(['Entity1', 'Entity2', 'Entity3']);
expect(indices).toEqual([0, 1, 2]);
});
test('应该能够过滤实体', () => {
entity1.tag = 1;
entity2.tag = 2;
entity3.tag = 1;
const filtered = batchOp.filter(entity => entity.tag === 1);
expect(filtered.count()).toBe(2);
expect(filtered.toArray()).toContain(entity1);
expect(filtered.toArray()).toContain(entity3);
});
test('应该能够获取实体数组', () => {
const entities = batchOp.toArray();
expect(entities.length).toBe(3);
expect(entities).toContain(entity1);
expect(entities).toContain(entity2);
expect(entities).toContain(entity3);
});
test('应该能够获取实体数量', () => {
expect(batchOp.count()).toBe(3);
});
test('流式调用应该工作正常', () => {
const result = batchOp
.addComponent(new TestComponent(100))
.setActive(false)
.setTag(42)
.forEach((entity) => {
entity.name = entity.name + '_Modified';
});
expect(result).toBe(batchOp);
expect(entity1.hasComponent(TestComponent)).toBe(true);
expect(entity1.active).toBe(false);
expect(entity1.tag).toBe(42);
expect(entity1.name).toBe('Entity1_Modified');
});
});
describe('工厂函数和全局API', () => {
test('createECSAPI应该创建API实例', () => {
const api = createECSAPI(scene, querySystem, eventSystem);
expect(api).toBeInstanceOf(ECSFluentAPI);
});
test('initializeECS应该初始化全局ECS', () => {
initializeECS(scene, querySystem, eventSystem);
expect(ECS).toBeInstanceOf(ECSFluentAPI);
});
test('全局ECS应该可用', () => {
initializeECS(scene, querySystem, eventSystem);
const builder = ECS.createEntity();
expect(builder).toBeInstanceOf(EntityBuilder);
});
});
});

View File

@@ -0,0 +1,176 @@
import { Entity } from '../../../src/ECS/Entity';
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
import { ComponentType } from '../../../src/ECS/Core/ComponentStorage';
describe('初始化方式性能对比', () => {
test('对比不同初始化方式的性能', () => {
const testCount = 10000;
console.log(`\n=== 初始化方式对比 (${testCount}个实体) ===`);
// 方式1字段直接初始化原来的方式
class EntityWithFieldInit {
public name: string;
public id: number;
private _componentMask = BigIntFactory.zero();
private _componentTypeToIndex = new Map<ComponentType, number>();
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
// 方式2构造函数初始化新方式
class EntityWithConstructorInit {
public name: string;
public id: number;
private _componentMask: any;
private _componentTypeToIndex: Map<ComponentType, number>;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
this._componentMask = BigIntFactory.zero();
this._componentTypeToIndex = new Map<ComponentType, number>();
}
}
// 方式3完全延迟初始化
class EntityWithLazyInit {
public name: string;
public id: number;
private _componentMask: any;
private _componentTypeToIndex: Map<ComponentType, number> | undefined;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
// 什么都不初始化
}
private ensureInit() {
if (!this._componentTypeToIndex) {
this._componentMask = BigIntFactory.zero();
this._componentTypeToIndex = new Map<ComponentType, number>();
}
}
}
// 测试方式1字段直接初始化
let startTime = performance.now();
const entities1 = [];
for (let i = 0; i < testCount; i++) {
entities1.push(new EntityWithFieldInit(`Entity_${i}`, i));
}
const fieldInitTime = performance.now() - startTime;
// 测试方式2构造函数初始化
startTime = performance.now();
const entities2 = [];
for (let i = 0; i < testCount; i++) {
entities2.push(new EntityWithConstructorInit(`Entity_${i}`, i));
}
const constructorInitTime = performance.now() - startTime;
// 测试方式3延迟初始化
startTime = performance.now();
const entities3 = [];
for (let i = 0; i < testCount; i++) {
entities3.push(new EntityWithLazyInit(`Entity_${i}`, i));
}
const lazyInitTime = performance.now() - startTime;
// 测试方式4只创建基本对象
startTime = performance.now();
const entities4 = [];
for (let i = 0; i < testCount; i++) {
entities4.push({ name: `Entity_${i}`, id: i });
}
const basicObjectTime = performance.now() - startTime;
console.log(`字段直接初始化: ${fieldInitTime.toFixed(2)}ms`);
console.log(`构造函数初始化: ${constructorInitTime.toFixed(2)}ms`);
console.log(`延迟初始化: ${lazyInitTime.toFixed(2)}ms`);
console.log(`基本对象创建: ${basicObjectTime.toFixed(2)}ms`);
console.log(`\n性能对比:`);
console.log(`构造函数 vs 字段初始化: ${(fieldInitTime / constructorInitTime).toFixed(2)}x`);
console.log(`延迟 vs 构造函数: ${(constructorInitTime / lazyInitTime).toFixed(2)}x`);
console.log(`延迟 vs 基本对象: ${(lazyInitTime / basicObjectTime).toFixed(2)}x`);
});
test('测试BigIntFactory.zero()的性能', () => {
const testCount = 10000;
console.log(`\n=== BigIntFactory.zero()性能测试 ===`);
// 测试1每次调用BigIntFactory.zero()
let startTime = performance.now();
const values1 = [];
for (let i = 0; i < testCount; i++) {
values1.push(BigIntFactory.zero());
}
const directCallTime = performance.now() - startTime;
// 测试2重复使用同一个实例
const sharedZero = BigIntFactory.zero();
startTime = performance.now();
const values2 = [];
for (let i = 0; i < testCount; i++) {
values2.push(sharedZero);
}
const sharedInstanceTime = performance.now() - startTime;
// 测试3使用数字0
startTime = performance.now();
const values3 = [];
for (let i = 0; i < testCount; i++) {
values3.push(0);
}
const numberZeroTime = performance.now() - startTime;
console.log(`每次调用BigIntFactory.zero(): ${directCallTime.toFixed(2)}ms`);
console.log(`重复使用同一实例: ${sharedInstanceTime.toFixed(2)}ms`);
console.log(`使用数字0: ${numberZeroTime.toFixed(2)}ms`);
console.log(`性能提升:`);
console.log(`共享实例 vs 每次调用: ${(directCallTime / sharedInstanceTime).toFixed(2)}x faster`);
console.log(`数字0 vs BigIntFactory: ${(directCallTime / numberZeroTime).toFixed(2)}x faster`);
});
test('测试Map创建的性能', () => {
const testCount = 10000;
console.log(`\n=== Map创建性能测试 ===`);
// 测试1每次new Map()
let startTime = performance.now();
const maps1 = [];
for (let i = 0; i < testCount; i++) {
maps1.push(new Map());
}
const newMapTime = performance.now() - startTime;
// 测试2使用对象字面量
startTime = performance.now();
const objects = [];
for (let i = 0; i < testCount; i++) {
objects.push({});
}
const objectTime = performance.now() - startTime;
// 测试3延迟创建Map
startTime = performance.now();
const lazyMaps = [];
for (let i = 0; i < testCount; i++) {
lazyMaps.push(null); // 先不创建
}
const lazyTime = performance.now() - startTime;
console.log(`每次new Map(): ${newMapTime.toFixed(2)}ms`);
console.log(`对象字面量: ${objectTime.toFixed(2)}ms`);
console.log(`延迟创建: ${lazyTime.toFixed(2)}ms`);
console.log(`性能对比:`);
console.log(`对象 vs Map: ${(newMapTime / objectTime).toFixed(2)}x faster`);
console.log(`延迟 vs Map: ${(newMapTime / lazyTime).toFixed(2)}x faster`);
});
});

View File

@@ -0,0 +1,171 @@
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
import { Entity } from '../../../src/ECS/Entity';
describe('优化后的性能分析 - ComponentIndexManager优化', () => {
let entityManager: EntityManager;
beforeEach(() => {
ComponentTypeManager.instance.reset();
entityManager = new EntityManager();
});
test('测试优化后的实体创建性能', () => {
const testCount = 10000;
console.log(`\n=== 优化后的实体创建性能测试 (${testCount}个实体) ===`);
const startTime = performance.now();
const entities = [];
for (let i = 0; i < testCount; i++) {
entities.push(entityManager.createEntity(`Entity_${i}`));
}
const totalTime = performance.now() - startTime;
const avgTime = totalTime / testCount;
console.log(`总耗时: ${totalTime.toFixed(2)}ms`);
console.log(`平均每个实体: ${avgTime.toFixed(3)}ms`);
console.log(`每秒创建实体数: ${Math.round(1000 / avgTime)}`);
if (totalTime < 140) {
console.log(`✅ 性能优化成功!实际耗时 ${totalTime.toFixed(2)}ms < 140ms 目标`);
} else {
console.log(`❌ 仍需进一步优化,实际耗时 ${totalTime.toFixed(2)}ms >= 140ms 目标`);
}
// 性能基准应该在140ms以下
expect(totalTime).toBeLessThan(200); // 放宽一些给CI环境
});
test('对比批量创建与逐个创建的性能', () => {
const testCount = 5000;
console.log(`\n=== 批量创建vs逐个创建对比 (${testCount}个实体) ===`);
// 逐个创建
let startTime = performance.now();
for (let i = 0; i < testCount; i++) {
entityManager.createEntity(`Individual_${i}`);
}
const individualTime = performance.now() - startTime;
// 重置管理器
entityManager = new EntityManager();
// 批量创建
startTime = performance.now();
entityManager.createEntitiesBatch(testCount, "Batch", false);
const batchTime = performance.now() - startTime;
// 重置管理器
entityManager = new EntityManager();
// 批量创建(跳过事件)
startTime = performance.now();
entityManager.createEntitiesBatch(testCount, "BatchNoEvents", true);
const batchNoEventsTime = performance.now() - startTime;
console.log(`逐个创建: ${individualTime.toFixed(2)}ms`);
console.log(`批量创建: ${batchTime.toFixed(2)}ms`);
console.log(`批量创建(跳过事件): ${batchNoEventsTime.toFixed(2)}ms`);
console.log(`批量优化倍数: ${(individualTime / batchTime).toFixed(2)}x`);
console.log(`跳过事件优化倍数: ${(individualTime / batchNoEventsTime).toFixed(2)}x`);
});
test('测试组件索引管理器对空实体的优化效果', () => {
const testCount = 10000;
console.log(`\n=== 空实体优化效果测试 (${testCount}个空实体) ===`);
const startTime = performance.now();
const entities = [];
for (let i = 0; i < testCount; i++) {
const entity = entityManager.createEntity(`EmptyEntity_${i}`);
entities.push(entity);
}
const totalTime = performance.now() - startTime;
// 验证前几个实体确实没有组件
for (let i = 0; i < Math.min(5, entities.length); i++) {
expect(entities[i].components.length).toBe(0);
}
console.log(`空实体创建总耗时: ${totalTime.toFixed(2)}ms`);
console.log(`平均每个空实体: ${(totalTime / testCount).toFixed(3)}ms`);
// 获取优化统计信息
const stats = entityManager.getOptimizationStats();
console.log(`组件索引统计:`, stats.componentIndex);
// 空实体创建应该非常快放宽限制以适应CI环境
expect(totalTime).toBeLessThan(150);
});
test('测试Set对象池的效果', () => {
const testCount = 1000;
console.log(`\n=== Set对象池效果测试 (${testCount}次添加/删除) ===`);
// 创建实体
const entities = [];
for (let i = 0; i < testCount; i++) {
entities.push(entityManager.createEntity(`PoolTest_${i}`));
}
// 测试删除和重新创建的性能
const startTime = performance.now();
// 删除一半实体
for (let i = 0; i < testCount / 2; i++) {
entityManager.destroyEntity(entities[i]);
}
// 重新创建实体
for (let i = 0; i < testCount / 2; i++) {
entityManager.createEntity(`RecycledEntity_${i}`);
}
const totalTime = performance.now() - startTime;
console.log(`删除+重新创建耗时: ${totalTime.toFixed(2)}ms`);
console.log(`平均每次操作: ${(totalTime / testCount).toFixed(3)}ms`);
// 对象池优化应该让重复操作更快,放宽限制适应不同环境
expect(totalTime).toBeLessThan(100);
});
test('内存使用量分析', () => {
const testCount = 5000;
console.log(`\n=== 内存使用量分析 (${testCount}个实体) ===`);
// 获取初始内存使用情况
const initialStats = entityManager.getOptimizationStats();
const initialMemory = initialStats.componentIndex.memoryUsage;
// 创建实体
const entities = [];
for (let i = 0; i < testCount; i++) {
entities.push(entityManager.createEntity(`MemoryTest_${i}`));
}
// 获取创建后的内存使用情况
const afterStats = entityManager.getOptimizationStats();
const afterMemory = afterStats.componentIndex.memoryUsage;
console.log(`初始内存使用: ${initialMemory} 字节`);
console.log(`创建后内存使用: ${afterMemory} 字节`);
console.log(`增加的内存: ${afterMemory - initialMemory} 字节`);
console.log(`平均每个实体内存: ${((afterMemory - initialMemory) / testCount).toFixed(2)} 字节`);
// 清理并观察内存回收
for (const entity of entities) {
entityManager.destroyEntity(entity);
}
const cleanupStats = entityManager.getOptimizationStats();
const cleanupMemory = cleanupStats.componentIndex.memoryUsage;
console.log(`清理后内存使用: ${cleanupMemory} 字节`);
console.log(`内存回收率: ${(((afterMemory - cleanupMemory) / (afterMemory - initialMemory)) * 100).toFixed(1)}%`);
});
});

View File

@@ -0,0 +1,872 @@
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 {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class HealthComponent extends Component {
public health: number;
public maxHealth: number;
constructor(...args: unknown[]) {
super();
const [health = 100, maxHealth = 100] = args as [number?, number?];
this.health = health;
this.maxHealth = maxHealth;
}
}
class RenderComponent extends Component {
public visible: boolean;
public layer: number;
constructor(...args: unknown[]) {
super();
const [visible = true, layer = 0] = args as [boolean?, number?];
this.visible = visible;
this.layer = layer;
}
}
class AIComponent extends Component {
public behavior: string;
constructor(...args: unknown[]) {
super();
const [behavior = 'idle'] = args as [string?];
this.behavior = behavior;
}
}
class PhysicsComponent extends Component {
public mass: number;
constructor(...args: unknown[]) {
super();
const [mass = 1.0] = args as [number?];
this.mass = mass;
}
}
describe('QuerySystem - 查询系统测试', () => {
let querySystem: QuerySystem;
let entities: Entity[];
let originalAddComponent: any;
let originalRemoveComponent: any;
let originalRemoveAllComponents: any;
beforeEach(() => {
querySystem = new QuerySystem();
entities = [];
// 创建测试实体
for (let i = 0; i < 10; i++) {
const entity = new Entity(`Entity_${i}`, i + 1);
entities.push(entity);
}
// 将实体添加到查询系统
querySystem.setEntities(entities);
// 监听实体组件变化以保持查询系统同步
originalAddComponent = Entity.prototype.addComponent;
originalRemoveComponent = Entity.prototype.removeComponent;
originalRemoveAllComponents = Entity.prototype.removeAllComponents;
Entity.prototype.addComponent = function<T extends Component>(component: T): T {
const result = originalAddComponent.call(this, component);
// 通知查询系统实体已更新,重建所有索引
querySystem.setEntities(entities);
return result;
};
Entity.prototype.removeComponent = function(component: Component): void {
originalRemoveComponent.call(this, component);
// 通知查询系统实体已更新,重建所有索引
querySystem.setEntities(entities);
};
Entity.prototype.removeAllComponents = function(): void {
originalRemoveAllComponents.call(this);
// 通知查询系统实体已更新,重建所有索引
querySystem.setEntities(entities);
};
});
afterEach(() => {
// 恢复原始方法
Entity.prototype.addComponent = originalAddComponent;
Entity.prototype.removeComponent = originalRemoveComponent;
Entity.prototype.removeAllComponents = originalRemoveAllComponents;
});
describe('基本查询功能', () => {
test('应该能够查询单个组件类型', () => {
// 为部分实体添加Position组件
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
entities[2].addComponent(new PositionComponent(50, 60));
const result = querySystem.queryAll(PositionComponent);
expect(result.entities.length).toBe(3);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[1]);
expect(result.entities).toContain(entities[2]);
});
test('应该能够查询多个组件类型', () => {
// 创建不同组件组合的实体
entities[0].addComponent(new PositionComponent(10, 20));
entities[0].addComponent(new VelocityComponent(1, 1));
entities[1].addComponent(new PositionComponent(30, 40));
entities[1].addComponent(new HealthComponent(80));
entities[2].addComponent(new PositionComponent(50, 60));
entities[2].addComponent(new VelocityComponent(2, 2));
const result = querySystem.queryAll(PositionComponent, VelocityComponent);
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[2]);
expect(result.entities).not.toContain(entities[1]);
});
test('应该能够查询复杂的组件组合', () => {
// 创建复杂的组件组合
entities[0].addComponent(new PositionComponent(10, 20));
entities[0].addComponent(new VelocityComponent(1, 1));
entities[0].addComponent(new HealthComponent(100));
entities[1].addComponent(new PositionComponent(30, 40));
entities[1].addComponent(new VelocityComponent(2, 2));
entities[1].addComponent(new RenderComponent(true));
entities[2].addComponent(new PositionComponent(50, 60));
entities[2].addComponent(new HealthComponent(80));
entities[2].addComponent(new RenderComponent(false));
const result = querySystem.queryAll(PositionComponent, VelocityComponent, HealthComponent);
expect(result.entities.length).toBe(1);
expect(result.entities).toContain(entities[0]);
});
test('查询不存在的组件应该返回空结果', () => {
const result = querySystem.queryAll(AIComponent);
expect(result.entities.length).toBe(0);
expect(result.entities).toEqual([]);
});
test('空查询应该返回所有实体', () => {
// 添加一些组件以确保实体被追踪
entities.forEach((entity, index) => {
if (!entity.hasComponent(PositionComponent)) {
entity.addComponent(new PositionComponent(0, 0));
}
});
const result = querySystem.queryAll();
expect(result.entities.length).toBe(entities.length);
});
});
describe('查询缓存机制', () => {
test('相同查询应该使用缓存', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
const result1 = querySystem.queryAll(PositionComponent);
const result2 = querySystem.queryAll(PositionComponent);
// 第二次查询应该来自缓存
expect(result2.fromCache).toBe(true);
expect(result1.entities).toEqual(result2.entities);
expect(result1.entities.length).toBe(2);
});
test('组件变化应该使缓存失效', () => {
entities[0].addComponent(new PositionComponent(10, 20));
const result1 = querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(1);
// 添加新的匹配实体
entities[1].addComponent(new PositionComponent(30, 40));
const result2 = querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(2);
expect(result2.entities).toContain(entities[1]);
});
test('移除组件应该更新缓存', () => {
const positionComp = entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
const result1 = querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(2);
// 移除组件
entities[0].removeComponent(positionComp);
const result2 = querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(1);
expect(result2.entities).not.toContain(entities[0]);
});
test('实体销毁应该更新缓存', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
const result1 = querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(2);
// 销毁实体(通过移除所有组件模拟)
entities[0].removeAllComponents();
const result2 = querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(1);
expect(result2.entities).not.toContain(entities[0]);
});
});
describe('Archetype系统集成', () => {
test('具有相同组件组合的实体应该被分组', () => {
// 创建具有相同组件组合的实体
entities[0].addComponent(new PositionComponent(10, 20));
entities[0].addComponent(new VelocityComponent(1, 1));
entities[1].addComponent(new PositionComponent(30, 40));
entities[1].addComponent(new VelocityComponent(2, 2));
entities[2].addComponent(new PositionComponent(50, 60));
entities[2].addComponent(new HealthComponent(100));
const result = querySystem.queryAll(PositionComponent, VelocityComponent);
expect(result.entities.length).toBe(2);
expect(result.entities).toContain(entities[0]);
expect(result.entities).toContain(entities[1]);
// 验证Archetype优化是否工作 - 简化验证,重点是查询结果正确
const stats = querySystem.getStats();
// Archetype可能存在也可能不存在重点是查询结果正确
expect(stats.optimizationStats.archetypeSystem.length).toBeGreaterThanOrEqual(0);
});
test('Archetype应该优化查询性能', () => {
const entityCount = 1000;
const testEntities: Entity[] = [];
// 创建大量具有相同组件组合的实体
for (let i = 0; i < entityCount; i++) {
const entity = new Entity(`PerfEntity_${i}`, i + 100);
testEntities.push(entity);
}
// 先添加组件
for (const entity of testEntities) {
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
}
// 将实体添加到查询系统
querySystem.setEntities([...entities, ...testEntities]);
const startTime = performance.now();
const result = querySystem.queryAll(PositionComponent, VelocityComponent);
const endTime = performance.now();
expect(result.entities.length).toBe(entityCount);
const duration = endTime - startTime;
expect(duration).toBeLessThan(50); // 应该在50ms内完成
console.log(`Archetype优化查询${entityCount}个实体耗时: ${duration.toFixed(2)}ms`);
});
});
describe('位掩码优化', () => {
test('位掩码应该正确识别组件组合', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[0].addComponent(new VelocityComponent(1, 1));
entities[0].addComponent(new HealthComponent(100));
entities[1].addComponent(new PositionComponent(30, 40));
entities[1].addComponent(new VelocityComponent(2, 2));
entities[2].addComponent(new PositionComponent(50, 60));
entities[2].addComponent(new HealthComponent(80));
// 查询Position + Velocity组合
const velocityResult = querySystem.queryAll(PositionComponent, VelocityComponent);
expect(velocityResult.entities.length).toBe(2);
expect(velocityResult.entities).toContain(entities[0]);
expect(velocityResult.entities).toContain(entities[1]);
// 查询Position + Health组合
const healthResult = querySystem.queryAll(PositionComponent, HealthComponent);
expect(healthResult.entities.length).toBe(2);
expect(healthResult.entities).toContain(entities[0]);
expect(healthResult.entities).toContain(entities[2]);
});
test('位掩码应该支持高效的组件检查', () => {
const entityCount = 5000;
const testEntities: Entity[] = [];
// 创建大量实体
for (let i = 0; i < entityCount; i++) {
const entity = new Entity(`MaskEntity_${i}`, i + 200);
testEntities.push(entity);
}
// 先随机分配组件
for (let i = 0; i < entityCount; i++) {
const entity = testEntities[i];
entity.addComponent(new PositionComponent(i, i));
if (i % 2 === 0) {
entity.addComponent(new VelocityComponent(1, 1));
}
if (i % 3 === 0) {
entity.addComponent(new HealthComponent(100));
}
if (i % 5 === 0) {
entity.addComponent(new RenderComponent(true));
}
}
// 将实体添加到查询系统
querySystem.setEntities([...entities, ...testEntities]);
const startTime = performance.now();
// 执行复杂查询
const result1 = querySystem.queryAll(PositionComponent, VelocityComponent);
const result2 = querySystem.queryAll(PositionComponent, HealthComponent);
const result3 = querySystem.queryAll(VelocityComponent, HealthComponent);
const result4 = querySystem.queryAll(PositionComponent, VelocityComponent, HealthComponent);
const endTime = performance.now();
expect(result1.entities.length).toBe(entityCount / 2);
expect(result2.entities.length).toBe(Math.floor(entityCount / 3) + 1);
expect(result4.entities.length).toBe(Math.floor(entityCount / 6) + 1);
const duration = endTime - startTime;
expect(duration).toBeLessThan(100); // 复杂查询应该在100ms内完成
console.log(`位掩码优化复杂查询耗时: ${duration.toFixed(2)}ms`);
});
});
describe('脏标记系统', () => {
test('脏标记应该追踪组件变化', () => {
entities[0].addComponent(new PositionComponent(10, 20));
// 第一次查询
const result1 = querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(1);
// 修改组件(模拟脏标记)
const position = entities[0].getComponent(PositionComponent);
if (position) {
position.x = 50;
// 在实际实现中,这会标记实体为脏
}
// 添加新实体以触发重新查询
entities[1].addComponent(new PositionComponent(30, 40));
const result2 = querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(2);
});
test('脏标记应该优化不必要的查询', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
// 多次相同查询应该使用缓存
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
querySystem.queryAll(PositionComponent);
}
const endTime = performance.now();
const duration = endTime - startTime;
// 缓存查询应该非常快
expect(duration).toBeLessThan(10);
console.log(`1000次缓存查询耗时: ${duration.toFixed(2)}ms`);
});
});
describe('查询统计和性能监控', () => {
test('应该能够获取查询统计信息', () => {
querySystem.clearCache(); // 确保测试隔离
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
// 执行一些查询
querySystem.queryAll(PositionComponent);
querySystem.queryAll(VelocityComponent);
querySystem.queryAll(PositionComponent, VelocityComponent);
const stats = querySystem.getStats();
expect(stats.queryStats.totalQueries).toBeGreaterThan(0);
expect(stats.cacheStats.size).toBeGreaterThan(0);
expect(parseFloat(stats.cacheStats.hitRate)).toBeGreaterThanOrEqual(0);
expect(parseFloat(stats.cacheStats.hitRate)).toBeLessThanOrEqual(100);
});
test('缓存命中率应该在重复查询时提高', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
// 第一次查询(缓存未命中)
querySystem.queryAll(PositionComponent);
let stats = querySystem.getStats();
const initialHitRate = parseFloat(stats.cacheStats.hitRate);
// 重复查询(应该命中缓存)
for (let i = 0; i < 10; i++) {
querySystem.queryAll(PositionComponent);
}
stats = querySystem.getStats();
expect(parseFloat(stats.cacheStats.hitRate)).toBeGreaterThan(initialHitRate);
});
test('应该能够清理查询缓存', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
// 创建一些缓存条目
querySystem.queryAll(PositionComponent);
querySystem.queryAll(VelocityComponent);
let stats = querySystem.getStats();
expect(stats.cacheStats.size).toBeGreaterThan(0);
// 清理缓存
querySystem.clearCache();
stats = querySystem.getStats();
expect(stats.cacheStats.size).toBe(0);
});
});
describe('内存管理和优化', () => {
test('大量查询不应该导致内存泄漏', () => {
querySystem.clearCache(); // 确保测试隔离
const entityCount = 1000;
const testEntities: Entity[] = [];
// 创建大量实体
for (let i = 0; i < entityCount; i++) {
const entity = new Entity(`MemEntity_${i}`, i + 300);
entity.addComponent(new PositionComponent(i, i));
if (i % 2 === 0) {
entity.addComponent(new VelocityComponent(1, 1));
}
testEntities.push(entity);
}
// 执行大量不同的查询
const startTime = performance.now();
for (let i = 0; i < 100; i++) {
querySystem.queryAll(PositionComponent);
querySystem.queryAll(VelocityComponent);
querySystem.queryAll(PositionComponent, VelocityComponent);
}
const endTime = performance.now();
const duration = endTime - startTime;
expect(duration).toBeLessThan(500); // 应该在500ms内完成
// 验证缓存大小合理
const stats = querySystem.getStats();
expect(stats.cacheStats.size).toBeLessThan(10); // 不同查询类型应该不多
console.log(`大量查询操作耗时: ${duration.toFixed(2)}ms缓存大小: ${stats.cacheStats.size}`);
});
test('查询结果应该正确管理实体引用', () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new PositionComponent(30, 40));
const result = querySystem.queryAll(PositionComponent);
// 修改查询结果不应该影响原始数据
const originalLength = result.entities.length;
result.entities.push(entities[2]); // 尝试修改结果
const newResult = querySystem.queryAll(PositionComponent);
expect(newResult.entities.length).toBe(originalLength);
});
});
describe('边界情况和错误处理', () => {
test('空实体列表查询应该安全', () => {
expect(() => {
const result = querySystem.queryAll(PositionComponent);
expect(result.entities).toEqual([]);
}).not.toThrow();
});
test('查询不存在的组件类型应该安全', () => {
expect(() => {
const result = querySystem.queryAll(AIComponent, PhysicsComponent);
expect(result.entities).toEqual([]);
}).not.toThrow();
});
test('查询已销毁实体的组件应该安全处理', () => {
entities[0].addComponent(new PositionComponent(10, 20));
const result1 = querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(1);
// 销毁实体(通过移除所有组件)
entities[0].removeAllComponents();
const result2 = querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(0);
});
test('并发查询应该安全', async () => {
entities[0].addComponent(new PositionComponent(10, 20));
entities[1].addComponent(new VelocityComponent(1, 1));
entities[2].addComponent(new HealthComponent(100));
// 模拟并发查询
const promises = Array.from({ length: 50 }, (_, i) => {
return Promise.resolve(querySystem.queryAll(PositionComponent));
});
const results = await Promise.all(promises);
// 所有结果应该一致
results.forEach(result => {
expect(result.entities.length).toBe(1);
expect(result.entities[0]).toBe(entities[0]);
});
});
test('极大数量的查询类型应该能正确处理', () => {
const componentTypes = [
PositionComponent,
VelocityComponent,
HealthComponent,
RenderComponent,
AIComponent,
PhysicsComponent
];
// 创建具有不同组件组合的实体
for (let i = 0; i < entities.length; i++) {
for (let j = 0; j < componentTypes.length; j++) {
if (i % (j + 1) === 0) {
const ComponentClass = componentTypes[j];
if (!entities[i].hasComponent(ComponentClass as any)) {
switch (ComponentClass) {
case PositionComponent:
entities[i].addComponent(new PositionComponent(i, i));
break;
case VelocityComponent:
entities[i].addComponent(new VelocityComponent(1, 1));
break;
case HealthComponent:
entities[i].addComponent(new HealthComponent(100));
break;
case RenderComponent:
entities[i].addComponent(new RenderComponent(true));
break;
case AIComponent:
entities[i].addComponent(new AIComponent('patrol'));
break;
case PhysicsComponent:
entities[i].addComponent(new PhysicsComponent(1.0));
break;
}
}
}
}
}
// 测试各种组合查询
expect(() => {
querySystem.queryAll(PositionComponent);
querySystem.queryAll(PositionComponent, VelocityComponent);
querySystem.queryAll(PositionComponent, VelocityComponent, HealthComponent);
querySystem.queryAll(RenderComponent, AIComponent);
querySystem.queryAll(PhysicsComponent, PositionComponent);
}).not.toThrow();
const stats = querySystem.getStats();
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('应该能够构建排除组件的查询', () => {
// 为一些实体添加HealthComponent这样其他实体就不包含这个组件
entities[3].addComponent(new HealthComponent(100));
entities[4].addComponent(new HealthComponent(80));
const result = builder
.without(HealthComponent)
.execute();
// 应该返回没有HealthComponent的8个实体
expect(result.entities.length).toBe(8);
expect(result.entities).not.toContain(entities[3]);
expect(result.entities).not.toContain(entities[4]);
});
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;
// 重建索引以反映标签变化
querySystem.setEntities(entities);
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(11);
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(13);
});
test('应该能够批量添加实体(无重复检查)', () => {
const newEntities = [
new Entity('Unchecked1', 995),
new Entity('Unchecked2', 996)
];
querySystem.addEntitiesUnchecked(newEntities);
const stats = querySystem.getStats();
expect(stats.entityCount).toBe(12);
});
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);
});
});
});

View File

@@ -0,0 +1,252 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/ComponentStorage';
// 测试组件:使用集合类型装饰器
@EnableSoA
class CollectionsComponent extends Component {
// 序列化Map存储
@SerializeMap
public playerStats: Map<string, number> = new Map();
// 序列化Set存储
@SerializeSet
public achievements: Set<string> = new Set();
// 序列化Array存储
@SerializeArray
public inventory: string[] = [];
// 深拷贝对象存储
@DeepCopy
public config: { settings: { volume: number } } = { settings: { volume: 0.5 } };
// 普通对象(引用存储)
public metadata: any = null;
constructor() {
super();
}
}
describe('SoA集合类型装饰器测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('验证Map序列化存储', () => {
console.log('\\n=== 测试Map序列化存储 ===');
const component = new CollectionsComponent();
// 设置Map数据
component.playerStats.set('health', 100);
component.playerStats.set('mana', 50);
component.playerStats.set('experience', 1250);
console.log('原始Map数据:', {
size: component.playerStats.size,
entries: Array.from(component.playerStats.entries())
});
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
console.log('取回Map数据:', {
size: retrieved?.playerStats.size,
entries: Array.from(retrieved?.playerStats.entries() || [])
});
// 验证Map数据完整性
expect(retrieved?.playerStats).toBeInstanceOf(Map);
expect(retrieved?.playerStats.size).toBe(3);
expect(retrieved?.playerStats.get('health')).toBe(100);
expect(retrieved?.playerStats.get('mana')).toBe(50);
expect(retrieved?.playerStats.get('experience')).toBe(1250);
console.log('✅ Map序列化存储验证通过');
});
test('验证Set序列化存储', () => {
console.log('\\n=== 测试Set序列化存储 ===');
const component = new CollectionsComponent();
// 设置Set数据
component.achievements.add('first_kill');
component.achievements.add('level_10');
component.achievements.add('boss_defeated');
console.log('原始Set数据:', {
size: component.achievements.size,
values: Array.from(component.achievements)
});
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
console.log('取回Set数据:', {
size: retrieved?.achievements.size,
values: Array.from(retrieved?.achievements || [])
});
// 验证Set数据完整性
expect(retrieved?.achievements).toBeInstanceOf(Set);
expect(retrieved?.achievements.size).toBe(3);
expect(retrieved?.achievements.has('first_kill')).toBe(true);
expect(retrieved?.achievements.has('level_10')).toBe(true);
expect(retrieved?.achievements.has('boss_defeated')).toBe(true);
console.log('✅ Set序列化存储验证通过');
});
test('验证Array序列化存储', () => {
console.log('\\n=== 测试Array序列化存储 ===');
const component = new CollectionsComponent();
// 设置Array数据
component.inventory.push('sword', 'shield', 'potion');
console.log('原始Array数据:', component.inventory);
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
console.log('取回Array数据:', retrieved?.inventory);
// 验证Array数据完整性
expect(Array.isArray(retrieved?.inventory)).toBe(true);
expect(retrieved?.inventory.length).toBe(3);
expect(retrieved?.inventory).toEqual(['sword', 'shield', 'potion']);
console.log('✅ Array序列化存储验证通过');
});
test('验证深拷贝对象存储', () => {
console.log('\\n=== 测试深拷贝对象存储 ===');
const component = new CollectionsComponent();
const originalConfig = component.config;
// 修改配置
component.config.settings.volume = 0.8;
console.log('原始配置:', component.config);
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
console.log('取回配置:', retrieved?.config);
// 验证深拷贝
expect(retrieved?.config).toEqual(component.config);
expect(retrieved?.config).not.toBe(originalConfig); // 不是同一个引用
expect(retrieved?.config.settings.volume).toBe(0.8);
// 修改原始对象不应该影响取回的对象
component.config.settings.volume = 0.3;
expect(retrieved?.config.settings.volume).toBe(0.8); // 保持不变
console.log('✅ 深拷贝对象存储验证通过');
});
test('对比普通对象存储(引用存储)', () => {
console.log('\\n=== 测试普通对象存储(引用存储)===');
const component = new CollectionsComponent();
const sharedObject = { data: 'shared' };
component.metadata = sharedObject;
console.log('原始metadata:', component.metadata);
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
console.log('取回metadata:', retrieved?.metadata);
// 验证引用存储
expect(retrieved?.metadata).toBe(sharedObject); // 是同一个引用
expect(retrieved?.metadata.data).toBe('shared');
console.log('✅ 普通对象存储验证通过');
});
test('复杂场景:多种类型混合使用', () => {
console.log('\\n=== 测试复杂场景 ===');
const component = new CollectionsComponent();
// 设置复杂数据
component.playerStats.set('level', 25);
component.playerStats.set('gold', 5000);
component.achievements.add('explorer');
component.achievements.add('warrior');
component.inventory.push('legendary_sword', 'magic_potion');
component.config = {
settings: {
volume: 0.75
}
};
component.metadata = { timestamp: Date.now() };
console.log('复杂数据设置完成');
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, CollectionsComponent);
// 全面验证
expect(retrieved?.playerStats.get('level')).toBe(25);
expect(retrieved?.achievements.has('explorer')).toBe(true);
expect(retrieved?.inventory).toContain('legendary_sword');
expect(retrieved?.config.settings.volume).toBe(0.75);
expect(retrieved?.metadata).toBeDefined();
console.log('✅ 复杂场景验证通过');
});
test('性能测试:序列化 vs 深拷贝', () => {
console.log('\\n=== 性能对比测试 ===');
const entityCount = 100;
// 准备测试数据
const startTime = performance.now();
for (let i = 0; i < entityCount; i++) {
const component = new CollectionsComponent();
// 设置数据
component.playerStats.set('id', i);
component.playerStats.set('score', i * 100);
component.achievements.add(`achievement_${i}`);
component.inventory.push(`item_${i}`);
component.config = { settings: { volume: i / entityCount } };
manager.addComponent(i, component);
}
const createTime = performance.now() - startTime;
// 读取测试
const readStartTime = performance.now();
for (let i = 0; i < entityCount; i++) {
const component = manager.getComponent(i, CollectionsComponent);
expect(component?.playerStats.get('id')).toBe(i);
}
const readTime = performance.now() - readStartTime;
console.log(`创建${entityCount}个复杂组件: ${createTime.toFixed(2)}ms`);
console.log(`读取${entityCount}个复杂组件: ${readTime.toFixed(2)}ms`);
console.log(`平均每个组件: ${((createTime + readTime) / entityCount).toFixed(4)}ms`);
console.log('✅ 性能测试完成');
});
});

View File

@@ -0,0 +1,307 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/ComponentStorage';
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
// 综合测试组件,覆盖所有装饰器
@EnableSoA
class ComprehensiveComponent extends Component {
@HighPrecision
public bigIntId: number = BigInt(Number.MAX_SAFE_INTEGER + 1) as any;
@Float64
public preciseValue: number = Math.PI;
@Int32
public intValue: number = -2147483648;
@SerializeMap
public gameMap: Map<string, any> = new Map();
@SerializeSet
public flags: Set<number> = new Set();
@SerializeArray
public items: any[] = [];
@DeepCopy
public nestedConfig: any = { deep: { nested: { value: 42 } } };
// 未装饰的字段
public normalFloat: number = 1.23;
public flag: boolean = true;
public text: string = 'default';
public complexObject: any = null;
constructor() {
super();
}
}
describe('SoA存储综合测试覆盖', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('验证所有装饰器类型的存储和检索', () => {
console.log('\\n=== 综合装饰器测试 ===');
const component = new ComprehensiveComponent();
// 设置复杂数据
component.gameMap.set('player1', { level: 10, gold: 500 });
component.gameMap.set('player2', { level: 15, gold: 1200 });
component.flags.add(1);
component.flags.add(2);
component.flags.add(4);
component.items.push({ type: 'weapon', name: 'sword' });
component.items.push({ type: 'armor', name: 'shield' });
component.nestedConfig.deep.nested.value = 999;
component.complexObject = { reference: 'shared' };
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, ComprehensiveComponent);
// 验证所有类型
expect(retrieved?.bigIntId).toBe(component.bigIntId);
expect(retrieved?.preciseValue).toBeCloseTo(Math.PI, 15);
expect(retrieved?.intValue).toBe(-2147483648);
expect(retrieved?.gameMap).toBeInstanceOf(Map);
expect(retrieved?.gameMap.get('player1')).toEqual({ level: 10, gold: 500 });
expect(retrieved?.flags).toBeInstanceOf(Set);
expect(retrieved?.flags.has(2)).toBe(true);
expect(retrieved?.items).toEqual(component.items);
expect(retrieved?.nestedConfig.deep.nested.value).toBe(999);
// 深拷贝验证
expect(retrieved?.nestedConfig).not.toBe(component.nestedConfig);
console.log('✅ 综合装饰器测试通过');
});
test('测试存储器内存统计和容量管理', () => {
console.log('\\n=== 存储器管理测试 ===');
const storage = manager.getStorage(ComprehensiveComponent) as SoAStorage<ComprehensiveComponent>;
// 添加多个组件
for (let i = 1; i <= 5; i++) {
const component = new ComprehensiveComponent();
component.intValue = i * 100;
component.preciseValue = i * Math.PI;
manager.addComponent(i, component);
}
// 检查统计信息
const stats = storage.getStats();
console.log('存储统计:', {
size: stats.size,
capacity: stats.capacity,
memoryUsage: stats.memoryUsage,
fieldCount: stats.fieldStats.size
});
expect(stats.size).toBe(5);
expect(stats.capacity).toBeGreaterThanOrEqual(5);
expect(stats.memoryUsage).toBeGreaterThan(0);
// 测试压缩
storage.removeComponent(2);
storage.removeComponent(4);
const statsBeforeCompact = storage.getStats();
storage.compact();
const statsAfterCompact = storage.getStats();
expect(statsAfterCompact.size).toBe(3);
console.log('压缩前后对比:', {
before: statsBeforeCompact.size,
after: statsAfterCompact.size
});
console.log('✅ 存储器管理测试通过');
});
test('测试序列化错误处理', () => {
console.log('\\n=== 序列化错误处理测试 ===');
// 创建包含循环引用的对象
const component = new ComprehensiveComponent();
const cyclicObject: any = { name: 'test' };
cyclicObject.self = cyclicObject; // 循环引用
// 这应该不会崩溃,而是优雅处理
component.items.push(cyclicObject);
expect(() => {
manager.addComponent(1, component);
}).not.toThrow();
const retrieved = manager.getComponent(1, ComprehensiveComponent);
expect(retrieved).toBeDefined();
console.log('✅ 序列化错误处理测试通过');
});
test('测试大容量扩展和性能', () => {
console.log('\\n=== 大容量性能测试 ===');
const startTime = performance.now();
const entityCount = 2000;
// 创建大量实体
for (let i = 1; i <= entityCount; i++) {
const component = new ComprehensiveComponent();
component.intValue = i;
component.preciseValue = i * 0.1;
component.gameMap.set(`key${i}`, i);
component.flags.add(i % 10);
component.items.push(`item${i}`);
manager.addComponent(i, component);
}
const createTime = performance.now() - startTime;
// 随机访问测试
const readStartTime = performance.now();
for (let i = 0; i < 100; i++) {
const randomId = Math.floor(Math.random() * entityCount) + 1;
const component = manager.getComponent(randomId, ComprehensiveComponent);
expect(component?.intValue).toBe(randomId);
}
const readTime = performance.now() - readStartTime;
console.log(`创建${entityCount}个组件: ${createTime.toFixed(2)}ms`);
console.log(`随机读取100次: ${readTime.toFixed(2)}ms`);
console.log(`平均创建时间: ${(createTime / entityCount).toFixed(4)}ms/组件`);
// 验证存储统计
const storage = manager.getStorage(ComprehensiveComponent) as SoAStorage<ComprehensiveComponent>;
const stats = storage.getStats();
expect(stats.size).toBe(entityCount);
expect(stats.capacity).toBeGreaterThanOrEqual(entityCount);
console.log('✅ 大容量性能测试通过');
});
test('测试空值和边界处理', () => {
console.log('\\n=== 空值边界测试 ===');
const component = new ComprehensiveComponent();
// 设置各种边界值
component.gameMap.set('null', null);
component.gameMap.set('undefined', undefined);
component.gameMap.set('empty', '');
component.gameMap.set('zero', 0);
component.gameMap.set('false', false);
component.flags.add(0);
component.items.push(null, undefined, '', 0, false);
component.nestedConfig = null;
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, ComprehensiveComponent);
// 验证边界值处理
expect(retrieved?.gameMap.get('null')).toBe(null);
expect(retrieved?.gameMap.get('undefined')).toBe(null); // JSON序列化会将undefined转为null
expect(retrieved?.gameMap.get('empty')).toBe('');
expect(retrieved?.gameMap.get('zero')).toBe(0);
expect(retrieved?.gameMap.get('false')).toBe(false);
expect(retrieved?.flags.has(0)).toBe(true);
expect(retrieved?.items).toEqual([null, null, '', 0, false]); // undefined序列化为null
expect(retrieved?.nestedConfig).toBe(null);
console.log('✅ 空值边界测试通过');
});
test('测试不同TypedArray类型的字段访问', () => {
console.log('\\n=== TypedArray字段测试 ===');
const storage = manager.getStorage(ComprehensiveComponent) as SoAStorage<ComprehensiveComponent>;
// 添加测试数据
const component = new ComprehensiveComponent();
manager.addComponent(1, component);
// 检查不同类型的TypedArray
const preciseArray = storage.getFieldArray('preciseValue');
const intArray = storage.getFieldArray('intValue');
const normalArray = storage.getFieldArray('normalFloat');
const flagArray = storage.getFieldArray('flag');
expect(preciseArray).toBeInstanceOf(Float64Array);
expect(intArray).toBeInstanceOf(Int32Array);
expect(normalArray).toBeInstanceOf(Float32Array);
expect(flagArray).toBeInstanceOf(Float32Array);
// 高精度字段不应该在TypedArray中
const bigIntArray = storage.getFieldArray('bigIntId');
expect(bigIntArray).toBeNull();
console.log('TypedArray类型验证:', {
preciseValue: preciseArray?.constructor.name,
intValue: intArray?.constructor.name,
normalFloat: normalArray?.constructor.name,
flag: flagArray?.constructor.name,
bigIntId: bigIntArray ? 'Found' : 'null (正确)'
});
console.log('✅ TypedArray字段测试通过');
});
test('测试向量化批量操作', () => {
console.log('\\n=== 向量化操作测试 ===');
const storage = manager.getStorage(ComprehensiveComponent) as SoAStorage<ComprehensiveComponent>;
// 添加测试数据
for (let i = 1; i <= 10; i++) {
const component = new ComprehensiveComponent();
component.normalFloat = i;
component.intValue = i * 10;
manager.addComponent(i, component);
}
// 执行向量化操作
let operationExecuted = false;
storage.performVectorizedOperation((fieldArrays, activeIndices) => {
operationExecuted = true;
const normalFloatArray = fieldArrays.get('normalFloat') as Float32Array;
const intArray = fieldArrays.get('intValue') as Int32Array;
expect(normalFloatArray).toBeInstanceOf(Float32Array);
expect(intArray).toBeInstanceOf(Int32Array);
expect(activeIndices.length).toBe(10);
// 批量修改数据
for (let i = 0; i < activeIndices.length; i++) {
const idx = activeIndices[i];
normalFloatArray[idx] *= 2; // 乘以2
intArray[idx] += 5; // 加5
}
});
expect(operationExecuted).toBe(true);
// 验证批量操作结果
const component = manager.getComponent(5, ComprehensiveComponent);
expect(component?.normalFloat).toBe(10); // 5 * 2
expect(component?.intValue).toBe(55); // 50 + 5
console.log('✅ 向量化操作测试通过');
});
});

View File

@@ -0,0 +1,171 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64, Int32 } from '../../../src/ECS/Core/ComponentStorage';
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
// 测试组件:使用不同的数值类型装饰器
@EnableSoA
class DecoratedComponent extends Component {
// 默认Float32Array存储
public normalFloat: number = 3.14;
// 高精度存储(作为复杂对象)
@HighPrecision
public highPrecisionNumber: number = Number.MAX_SAFE_INTEGER;
// Float64Array存储
@Float64
public preciseFloat: number = Math.PI;
// Int32Array存储
@Int32
public integerValue: number = 42;
// 布尔值默认Float32Array
public flag: boolean = true;
// 字符串(专门数组)
public text: string = 'hello';
constructor() {
super();
}
}
describe('SoA数值类型装饰器测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('验证不同装饰器的存储类型', () => {
console.log('\\n=== 测试装饰器存储类型 ===');
const component = new DecoratedComponent();
component.highPrecisionNumber = Number.MAX_SAFE_INTEGER;
component.preciseFloat = Math.PI;
component.integerValue = 999999;
component.normalFloat = 2.718;
console.log('原始数据:', {
normalFloat: component.normalFloat,
highPrecisionNumber: component.highPrecisionNumber,
preciseFloat: component.preciseFloat,
integerValue: component.integerValue,
flag: component.flag,
text: component.text
});
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, DecoratedComponent);
console.log('\\n取回数据:', {
normalFloat: retrieved?.normalFloat,
highPrecisionNumber: retrieved?.highPrecisionNumber,
preciseFloat: retrieved?.preciseFloat,
integerValue: retrieved?.integerValue,
flag: retrieved?.flag,
text: retrieved?.text
});
// 验证精度保持
expect(retrieved?.normalFloat).toBeCloseTo(2.718, 5); // Float32精度
expect(retrieved?.highPrecisionNumber).toBe(Number.MAX_SAFE_INTEGER); // 高精度保持
expect(retrieved?.preciseFloat).toBeCloseTo(Math.PI, 15); // Float64精度
expect(retrieved?.integerValue).toBe(999999); // 整数保持
expect(retrieved?.flag).toBe(true);
expect(retrieved?.text).toBe('hello');
console.log('✅ 所有装饰器类型验证通过');
});
test('验证存储器内部结构', () => {
console.log('\\n=== 测试存储器内部结构 ===');
const component = new DecoratedComponent();
manager.addComponent(1, component);
const storage = manager.getStorage(DecoratedComponent) as SoAStorage<DecoratedComponent>;
// 检查TypedArray字段
const normalFloatArray = storage.getFieldArray('normalFloat');
const preciseFloatArray = storage.getFieldArray('preciseFloat');
const integerArray = storage.getFieldArray('integerValue');
const flagArray = storage.getFieldArray('flag');
console.log('存储类型:', {
normalFloat: normalFloatArray?.constructor.name,
preciseFloat: preciseFloatArray?.constructor.name,
integerValue: integerArray?.constructor.name,
flag: flagArray?.constructor.name
});
// 验证存储类型
expect(normalFloatArray).toBeInstanceOf(Float32Array);
expect(preciseFloatArray).toBeInstanceOf(Float64Array);
expect(integerArray).toBeInstanceOf(Int32Array);
expect(flagArray).toBeInstanceOf(Float32Array);
// 高精度字段不应该在TypedArray中
const highPrecisionArray = storage.getFieldArray('highPrecisionNumber');
expect(highPrecisionArray).toBeNull();
console.log('✅ 存储器内部结构验证通过');
});
test('测试边界值精度', () => {
console.log('\\n=== 测试边界值精度 ===');
const component = new DecoratedComponent();
// 测试极限值
component.highPrecisionNumber = Number.MAX_SAFE_INTEGER;
component.preciseFloat = Number.MIN_VALUE;
component.normalFloat = 16777217; // 超出Float32精度
component.integerValue = -2147483648; // Int32最小值
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, DecoratedComponent);
console.log('边界值测试结果:', {
highPrecision: retrieved?.highPrecisionNumber === Number.MAX_SAFE_INTEGER,
preciseFloat: retrieved?.preciseFloat === Number.MIN_VALUE,
normalFloat: retrieved?.normalFloat, // 可能有精度损失
integerValue: retrieved?.integerValue === -2147483648
});
// 验证高精度保持
expect(retrieved?.highPrecisionNumber).toBe(Number.MAX_SAFE_INTEGER);
expect(retrieved?.preciseFloat).toBe(Number.MIN_VALUE);
expect(retrieved?.integerValue).toBe(-2147483648);
console.log('✅ 边界值精度测试通过');
});
test('性能对比:装饰器 vs 自动检测', () => {
console.log('\\n=== 性能对比测试 ===');
const entityCount = 1000;
// 使用装饰器的组件
const startTime = performance.now();
for (let i = 0; i < entityCount; i++) {
const component = new DecoratedComponent();
component.highPrecisionNumber = Number.MAX_SAFE_INTEGER;
component.preciseFloat = Math.PI * i;
component.integerValue = i;
manager.addComponent(i, component);
}
const decoratorTime = performance.now() - startTime;
console.log(`装饰器方式: ${decoratorTime.toFixed(2)}ms`);
console.log(`平均每个组件: ${(decoratorTime / entityCount).toFixed(4)}ms`);
// 验证数据完整性
const sample = manager.getComponent(500, DecoratedComponent);
expect(sample?.highPrecisionNumber).toBe(Number.MAX_SAFE_INTEGER);
expect(sample?.integerValue).toBe(500);
console.log('✅ 性能测试完成,数据完整性验证通过');
});
});

View File

@@ -0,0 +1,128 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA } from '../../../src/ECS/Core/ComponentStorage';
// 模拟复杂对象如cocos的node节点
class MockNode {
public name: string;
public active: boolean;
constructor(name: string) {
this.name = name;
this.active = true;
}
public toString() {
return `Node(${this.name})`;
}
}
// 包含复杂属性的组件
@EnableSoA
class ProblematicComponent extends Component {
public x: number = 0;
public y: number = 0;
public node: MockNode | null = null;
public callback: Function | null = null;
public data: any = null;
constructor() {
super();
this.node = new MockNode('test');
this.callback = () => console.log('test');
this.data = { complex: 'object' };
}
}
// 安全的数值组件
@EnableSoA
class SafeComponent extends Component {
public x: number = 0;
public y: number = 0;
public active: boolean = true;
}
describe('SoA边界情况和复杂属性测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('包含复杂对象的组件会有什么问题', () => {
console.log('\\n=== 测试复杂对象处理 ===');
// 创建包含复杂属性的组件
const originalComponent = new ProblematicComponent();
console.log('原始组件:', {
x: originalComponent.x,
y: originalComponent.y,
node: originalComponent.node?.name,
callback: typeof originalComponent.callback,
data: originalComponent.data
});
// 添加到SoA存储
manager.addComponent(1, originalComponent);
// 获取组件看看发生了什么
const retrievedComponent = manager.getComponent(1, ProblematicComponent);
console.log('取回的组件:', {
x: retrievedComponent?.x,
y: retrievedComponent?.y,
node: retrievedComponent?.node,
callback: retrievedComponent?.callback,
data: retrievedComponent?.data
});
// 验证数据完整性
expect(retrievedComponent?.x).toBe(0);
expect(retrievedComponent?.y).toBe(0);
// 复杂对象的问题
console.log('\\n⚠ 问题发现:');
console.log('- node对象:', retrievedComponent?.node);
console.log('- callback函数:', retrievedComponent?.callback);
console.log('- data对象:', retrievedComponent?.data);
// 复杂属性现在应该正确保存
expect(retrievedComponent?.node?.name).toBe('test'); // 应该保持原始值
expect(retrievedComponent?.callback).toBe(originalComponent.callback); // 应该是同一个函数
expect(retrievedComponent?.data).toEqual({ complex: 'object' }); // 应该保持原始数据
console.log('✅ 修复成功:复杂对象现在能正确处理!');
});
test('纯数值组件工作正常', () => {
console.log('\\n=== 测试纯数值组件 ===');
const safeComponent = new SafeComponent();
safeComponent.x = 100;
safeComponent.y = 200;
safeComponent.active = false;
manager.addComponent(1, safeComponent);
const retrieved = manager.getComponent(1, SafeComponent);
console.log('纯数值组件正常工作:', {
x: retrieved?.x,
y: retrieved?.y,
active: retrieved?.active
});
expect(retrieved?.x).toBe(100);
expect(retrieved?.y).toBe(200);
expect(retrieved?.active).toBe(false);
});
test('SoA是否能检测到不适合的组件类型', () => {
console.log('\\n=== 测试类型检测 ===');
// 当前实现会静默忽略复杂字段
// 这是一个潜在的问题!
const storage = manager.getStorage(ProblematicComponent);
console.log('存储类型:', storage.constructor.name);
// SoA存储应该能警告或拒绝不适合的组件
expect(storage.constructor.name).toBe('SoAStorage');
});
});

View File

@@ -0,0 +1,158 @@
import { Component } from '../../../src/ECS/Component';
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64 } from '../../../src/ECS/Core/ComponentStorage';
// 包含所有基础类型的组件
@EnableSoA
class AllTypesComponent extends Component {
// 数值类型
public intNumber: number = 42;
public floatNumber: number = 3.14;
public zeroNumber: number = 0;
// 布尔类型
public trueBoolean: boolean = true;
public falseBoolean: boolean = false;
// 字符串类型
public emptyString: string = '';
public normalString: string = 'hello';
public longString: string = 'this is a long string with spaces and 123 numbers!';
// 其他基础类型
public nullValue: null = null;
public undefinedValue: undefined = undefined;
// 复杂类型
public arrayValue: number[] = [1, 2, 3];
public objectValue: { name: string } = { name: 'test' };
constructor() {
super();
}
}
// 边界测试专用组件
@EnableSoA
class BoundaryTestComponent extends Component {
// 高精度大整数
@HighPrecision
public maxInt: number = 0;
// 高精度小浮点数
@Float64
public minFloat: number = 0;
// 普通数值
public normalNumber: number = 0;
// 字符串测试
public testString: string = '';
public longString: string = '';
constructor() {
super();
}
}
describe('SoA所有数据类型处理测试', () => {
let manager: ComponentStorageManager;
beforeEach(() => {
manager = new ComponentStorageManager();
});
test('验证所有基础类型的处理', () => {
console.log('\\n=== 测试所有数据类型 ===');
// 创建包含各种类型的组件
const originalComponent = new AllTypesComponent();
originalComponent.normalString = 'modified string';
originalComponent.longString = '测试中文字符串 with emoji 🎉';
originalComponent.intNumber = 999;
originalComponent.floatNumber = 2.718;
originalComponent.trueBoolean = false;
originalComponent.falseBoolean = true;
console.log('原始组件数据:', {
intNumber: originalComponent.intNumber,
floatNumber: originalComponent.floatNumber,
trueBoolean: originalComponent.trueBoolean,
falseBoolean: originalComponent.falseBoolean,
emptyString: `"${originalComponent.emptyString}"`,
normalString: `"${originalComponent.normalString}"`,
longString: `"${originalComponent.longString}"`,
arrayValue: originalComponent.arrayValue,
objectValue: originalComponent.objectValue
});
// 存储到SoA
manager.addComponent(1, originalComponent);
// 获取并验证
const retrievedComponent = manager.getComponent(1, AllTypesComponent);
console.log('\\n取回的组件数据:', {
intNumber: retrievedComponent?.intNumber,
floatNumber: retrievedComponent?.floatNumber,
trueBoolean: retrievedComponent?.trueBoolean,
falseBoolean: retrievedComponent?.falseBoolean,
emptyString: `"${retrievedComponent?.emptyString}"`,
normalString: `"${retrievedComponent?.normalString}"`,
longString: `"${retrievedComponent?.longString}"`,
arrayValue: retrievedComponent?.arrayValue,
objectValue: retrievedComponent?.objectValue
});
// 验证数值类型
expect(retrievedComponent?.intNumber).toBe(999);
expect(retrievedComponent?.floatNumber).toBeCloseTo(2.718);
// 验证布尔类型
expect(retrievedComponent?.trueBoolean).toBe(false);
expect(retrievedComponent?.falseBoolean).toBe(true);
// 验证字符串类型
expect(retrievedComponent?.emptyString).toBe('');
expect(retrievedComponent?.normalString).toBe('modified string');
expect(retrievedComponent?.longString).toBe('测试中文字符串 with emoji 🎉');
// 验证复杂类型
expect(retrievedComponent?.arrayValue).toEqual([1, 2, 3]);
expect(retrievedComponent?.objectValue).toEqual({ name: 'test' });
console.log('\\n✅ 所有类型验证完成');
});
test('边界情况测试', () => {
console.log('\\n=== 边界情况测试 ===');
const component = new BoundaryTestComponent();
// 特殊数值
component.maxInt = Number.MAX_SAFE_INTEGER;
component.minFloat = Number.MIN_VALUE;
component.normalNumber = -0;
// 特殊字符串
component.testString = '\\n\\t\\r"\'\\\\'; // 转义字符
component.longString = 'a'.repeat(1000); // 长字符串
manager.addComponent(1, component);
const retrieved = manager.getComponent(1, BoundaryTestComponent);
console.log('边界情况结果:', {
maxInt: retrieved?.maxInt,
minFloat: retrieved?.minFloat,
negativeZero: retrieved?.normalNumber,
escapeStr: retrieved?.testString,
longStr: retrieved?.longString?.length
});
expect(retrieved?.maxInt).toBe(Number.MAX_SAFE_INTEGER);
expect(retrieved?.minFloat).toBe(Number.MIN_VALUE);
expect(retrieved?.testString).toBe('\\n\\t\\r"\'\\\\');
expect(retrieved?.longString).toBe('a'.repeat(1000));
console.log('✅ 边界情况测试通过');
});
});

View File

@@ -0,0 +1,500 @@
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
// 测试组件
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
this.x = (args[0] as number) ?? 0;
this.y = (args[1] as number) ?? 0;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
this.vx = (args[0] as number) ?? 0;
this.vy = (args[1] as number) ?? 0;
}
}
class HealthComponent extends Component {
public health: number;
constructor(...args: unknown[]) {
super();
this.health = (args[0] as number) ?? 100;
}
}
class TagComponent extends Component {
public tag: string;
constructor(...args: unknown[]) {
super();
this.tag = (args[0] as string) ?? '';
}
}
// 测试系统
class MovementSystem extends EntitySystem {
public processedEntities: Entity[] = [];
public initializeCalled = false;
public onAddedEntities: Entity[] = [];
public onRemovedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
public override initialize(): void {
this.initializeCalled = true;
super.initialize();
}
protected override onAdded(entity: Entity): void {
this.onAddedEntities.push(entity);
}
protected override onRemoved(entity: Entity): void {
this.onRemovedEntities.push(entity);
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
const velocity = entity.getComponent(VelocityComponent)!;
position.x += velocity.vx;
position.y += velocity.vy;
}
}
}
class HealthSystem extends EntitySystem {
public processedEntities: Entity[] = [];
public initializeCalled = false;
public onAddedEntities: Entity[] = [];
public onRemovedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(HealthComponent));
}
public override initialize(): void {
this.initializeCalled = true;
super.initialize();
}
protected override onAdded(entity: Entity): void {
this.onAddedEntities.push(entity);
}
protected override onRemoved(entity: Entity): void {
this.onRemovedEntities.push(entity);
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
for (const entity of entities) {
const health = entity.getComponent(HealthComponent)!;
if (health.health <= 0) {
entity.enabled = false;
}
}
}
}
class MultiComponentSystem extends EntitySystem {
public processedEntities: Entity[] = [];
public initializeCalled = false;
constructor() {
super(Matcher.empty().all(PositionComponent, HealthComponent, TagComponent));
}
public override initialize(): void {
this.initializeCalled = true;
super.initialize();
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
}
}
describe('ECS系统初始化时序问题深度测试', () => {
let scene: Scene;
beforeEach(() => {
scene = new Scene();
scene.name = "InitializeTestScene";
});
describe('基础时序问题测试', () => {
test('先添加实体再添加系统 - 系统应该正确初始化', () => {
// 创建实体并添加组件
const player = scene.createEntity("Player");
player.addComponent(new PositionComponent(10, 20));
player.addComponent(new VelocityComponent(1, 1));
player.addComponent(new HealthComponent(100));
const enemy = scene.createEntity("Enemy");
enemy.addComponent(new PositionComponent(50, 60));
enemy.addComponent(new VelocityComponent(-1, 0));
enemy.addComponent(new HealthComponent(80));
// 验证实体已创建
expect(scene.entities.count).toBe(2);
// 添加系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证initialize方法被调用
expect(movementSystem.initializeCalled).toBe(true);
expect(healthSystem.initializeCalled).toBe(true);
// 验证系统正确识别已存在的实体
expect(movementSystem.entities.length).toBe(2);
expect(healthSystem.entities.length).toBe(2);
// 验证onAdded回调被正确调用
expect(movementSystem.onAddedEntities.length).toBe(2);
expect(movementSystem.onAddedEntities).toContain(player);
expect(movementSystem.onAddedEntities).toContain(enemy);
// 运行更新确认处理
scene.update();
expect(movementSystem.processedEntities.length).toBe(2);
expect(healthSystem.processedEntities.length).toBe(2);
// 检查移动逻辑是否生效
const playerPos = player.getComponent(PositionComponent)!;
expect(playerPos.x).toBe(11);
expect(playerPos.y).toBe(21);
});
test('先添加系统再添加实体 - 正常工作', () => {
// 先添加系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证initialize被调用但没有发现实体
expect(movementSystem.initializeCalled).toBe(true);
expect(healthSystem.initializeCalled).toBe(true);
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
// 创建实体
const player = scene.createEntity("Player");
player.addComponent(new PositionComponent(10, 20));
player.addComponent(new VelocityComponent(1, 1));
player.addComponent(new HealthComponent(100));
// 系统应该自动识别新实体
expect(movementSystem.entities.length).toBe(1);
expect(healthSystem.entities.length).toBe(1);
expect(movementSystem.onAddedEntities.length).toBe(1);
});
});
describe('复杂场景的时序测试', () => {
test('部分匹配实体的初始化', () => {
// 创建不同类型的实体
const fullEntity = scene.createEntity("FullEntity");
fullEntity.addComponent(new PositionComponent(0, 0));
fullEntity.addComponent(new VelocityComponent(1, 1));
fullEntity.addComponent(new HealthComponent(100));
const partialEntity1 = scene.createEntity("PartialEntity1");
partialEntity1.addComponent(new PositionComponent(10, 10));
partialEntity1.addComponent(new HealthComponent(50));
// 缺少VelocityComponent
const partialEntity2 = scene.createEntity("PartialEntity2");
partialEntity2.addComponent(new PositionComponent(20, 20));
partialEntity2.addComponent(new VelocityComponent(2, 2));
// 缺少HealthComponent
// 添加系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证选择性匹配
expect(movementSystem.entities).toContain(fullEntity);
expect(movementSystem.entities).not.toContain(partialEntity1);
expect(movementSystem.entities).toContain(partialEntity2);
expect(movementSystem.entities.length).toBe(2);
expect(healthSystem.entities).toContain(fullEntity);
expect(healthSystem.entities).toContain(partialEntity1);
expect(healthSystem.entities).not.toContain(partialEntity2);
expect(healthSystem.entities.length).toBe(2);
});
test('多组件要求系统的初始化', () => {
// 创建具有不同组件组合的实体
const entity1 = scene.createEntity("Entity1");
entity1.addComponent(new PositionComponent(0, 0));
entity1.addComponent(new HealthComponent(100));
entity1.addComponent(new TagComponent("player"));
const entity2 = scene.createEntity("Entity2");
entity2.addComponent(new PositionComponent(10, 10));
entity2.addComponent(new HealthComponent(80));
// 缺少TagComponent
const entity3 = scene.createEntity("Entity3");
entity3.addComponent(new PositionComponent(20, 20));
entity3.addComponent(new TagComponent("enemy"));
// 缺少HealthComponent
// 添加要求三个组件的系统
const multiSystem = new MultiComponentSystem();
scene.addEntityProcessor(multiSystem);
// 只有entity1应该匹配
expect(multiSystem.entities.length).toBe(1);
expect(multiSystem.entities).toContain(entity1);
expect(multiSystem.entities).not.toContain(entity2);
expect(multiSystem.entities).not.toContain(entity3);
});
test('批量实体创建后的系统初始化', () => {
// 批量创建实体
const entities = scene.createEntities(10, "BatchEntity");
// 为所有实体添加组件
entities.forEach((entity, index) => {
entity.addComponent(new PositionComponent(index * 10, index * 10));
entity.addComponent(new VelocityComponent(index, index));
if (index % 2 === 0) {
entity.addComponent(new HealthComponent(100 - index * 10));
}
});
// 添加系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证系统正确处理批量实体
expect(movementSystem.entities.length).toBe(10); // 所有实体都有Position+Velocity
expect(healthSystem.entities.length).toBe(5); // 只有偶数索引的实体有Health
// 验证onAdded回调被正确调用
expect(movementSystem.onAddedEntities.length).toBe(10);
expect(healthSystem.onAddedEntities.length).toBe(5);
});
});
describe('动态组件修改后的系统响应', () => {
test('运行时添加组件 - 系统自动响应', () => {
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
// 创建只有位置组件的实体
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
// 系统不应该匹配
expect(movementSystem.entities.length).toBe(0);
// 添加速度组件
entity.addComponent(new VelocityComponent(5, 5));
// 系统应该立即识别
expect(movementSystem.entities.length).toBe(1);
expect(movementSystem.entities).toContain(entity);
expect(movementSystem.onAddedEntities).toContain(entity);
});
test('运行时移除组件 - 系统自动响应', () => {
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
// 创建完整的可移动实体
const entity = scene.createEntity("MovableEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(5, 5));
// 系统应该识别
expect(movementSystem.entities.length).toBe(1);
// 移除速度组件
const velocityComponent = entity.getComponent(VelocityComponent);
if (velocityComponent) {
entity.removeComponent(velocityComponent);
}
// 系统应该移除实体
expect(movementSystem.entities.length).toBe(0);
expect(movementSystem.onRemovedEntities).toContain(entity);
});
test('复杂的组件添加移除序列', () => {
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
const entity = scene.createEntity("ComplexEntity");
// 初始状态:无组件
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
// 添加位置组件
entity.addComponent(new PositionComponent(0, 0));
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
// 添加健康组件
entity.addComponent(new HealthComponent(100));
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(1);
// 添加速度组件
entity.addComponent(new VelocityComponent(1, 1));
expect(movementSystem.entities.length).toBe(1);
expect(healthSystem.entities.length).toBe(1);
// 移除健康组件
const healthComponent = entity.getComponent(HealthComponent);
if (healthComponent) {
entity.removeComponent(healthComponent);
}
expect(movementSystem.entities.length).toBe(1);
expect(healthSystem.entities.length).toBe(0);
// 移除位置组件
const positionComponent = entity.getComponent(PositionComponent);
if (positionComponent) {
entity.removeComponent(positionComponent);
}
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
});
});
describe('系统重复添加和移除测试', () => {
test('重复添加同一个系统 - 应该忽略', () => {
const movementSystem = new MovementSystem();
// 第一次添加
scene.addEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(1);
expect(movementSystem.initializeCalled).toBe(true);
// 重置标志
movementSystem.initializeCalled = false;
// 第二次添加同一个系统
scene.addEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(1); // 没有增加
expect(movementSystem.initializeCalled).toBe(false); // initialize不应该再次调用
});
test('添加后移除再添加 - 应该重新初始化', () => {
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
const movementSystem = new MovementSystem();
// 第一次添加
scene.addEntityProcessor(movementSystem);
expect(movementSystem.entities.length).toBe(1);
expect(movementSystem.initializeCalled).toBe(true);
// 移除系统
scene.removeEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(0);
// 重置状态
movementSystem.initializeCalled = false;
movementSystem.onAddedEntities = [];
// 重新添加
scene.addEntityProcessor(movementSystem);
expect(movementSystem.entities.length).toBe(1);
expect(movementSystem.initializeCalled).toBe(true);
expect(movementSystem.onAddedEntities.length).toBe(1);
});
});
describe('空场景和空系统的边界情况', () => {
test('空场景添加系统 - 不应该出错', () => {
const movementSystem = new MovementSystem();
expect(() => {
scene.addEntityProcessor(movementSystem);
}).not.toThrow();
expect(movementSystem.initializeCalled).toBe(true);
expect(movementSystem.entities.length).toBe(0);
});
test('有实体但没有匹配组件 - 系统应该为空', () => {
// 创建只有健康组件的实体
const entity = scene.createEntity("HealthOnlyEntity");
entity.addComponent(new HealthComponent(100));
// 添加移动系统需要Position+Velocity
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
expect(movementSystem.entities.length).toBe(0);
expect(movementSystem.onAddedEntities.length).toBe(0);
});
test('实体被禁用 - 系统仍应包含但不处理', () => {
const entity = scene.createEntity("DisabledEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
expect(movementSystem.entities.length).toBe(1);
// 禁用实体
entity.enabled = false;
// 系统仍然包含实体,但处理时应该跳过
expect(movementSystem.entities.length).toBe(1);
scene.update();
// 处理逻辑中应该检查enabled状态
// 由于实体被禁用,位置不应该改变(这取决于系统实现)
});
});
afterEach(() => {
scene.destroyAllEntities();
const processors = [...scene.entityProcessors.processors];
processors.forEach(processor => scene.removeEntityProcessor(processor));
});
});

View File

@@ -0,0 +1,140 @@
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
class TestComponent extends Component {
public value: number = 0;
constructor(...args: unknown[]) {
super();
const [value = 0] = args as [number?];
this.value = value;
}
}
class TrackingSystem extends EntitySystem {
public initializeCallCount = 0;
public onChangedCallCount = 0;
public trackedEntities: Entity[] = [];
public override initialize(): void {
// 必须先调用父类的initialize来检查防重复逻辑
const wasInitialized = (this as any)._initialized;
super.initialize();
// 只有在真正执行初始化时才增加计数和处理实体
if (!wasInitialized) {
this.initializeCallCount++;
// 处理所有现有实体
if (this.scene) {
for (const entity of this.scene.entities.buffer) {
this.onChanged(entity);
}
}
}
}
public onChanged(entity: Entity): void {
this.onChangedCallCount++;
if (this.isInterestedEntity(entity)) {
if (!this.trackedEntities.includes(entity)) {
this.trackedEntities.push(entity);
}
} else {
const index = this.trackedEntities.indexOf(entity);
if (index !== -1) {
this.trackedEntities.splice(index, 1);
}
}
}
public isInterestedEntity(entity: Entity): boolean {
return entity.hasComponent(TestComponent);
}
}
describe('系统多次初始化问题测试', () => {
let scene: Scene;
let system: TrackingSystem;
beforeEach(() => {
ComponentTypeManager.instance.reset();
scene = new Scene();
system = new TrackingSystem();
});
test('系统被多次添加到场景 - 应该防止重复初始化', () => {
const entity = scene.createEntity('TestEntity');
entity.addComponent(new TestComponent(10));
// 第一次添加系统
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(1);
expect(system.trackedEntities.length).toBe(1);
expect(system.onChangedCallCount).toBe(1);
// 再次添加同一个系统 - 应该被忽略
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(1); // 不应该增加
expect(system.trackedEntities.length).toBe(1); // 实体不应该重复
expect(system.onChangedCallCount).toBe(1); // onChanged不应该重复调用
});
test('手动多次调用initialize - 应该防止重复处理', () => {
const entity = scene.createEntity('TestEntity');
entity.addComponent(new TestComponent(10));
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(1);
expect(system.trackedEntities.length).toBe(1);
expect(system.onChangedCallCount).toBe(1);
// 手动再次调用initialize - 应该被防止
system.initialize();
expect(system.initializeCallCount).toBe(1); // 不应该增加
expect(system.onChangedCallCount).toBe(1); // onChanged不应该重复调用
expect(system.trackedEntities.length).toBe(1);
});
test('系统被移除后重新添加 - 应该重新初始化', () => {
const entity = scene.createEntity('TestEntity');
entity.addComponent(new TestComponent(10));
// 添加系统
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(1);
expect(system.trackedEntities.length).toBe(1);
// 移除系统
scene.removeEntityProcessor(system);
// 重新添加系统 - 应该重新初始化
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(2); // 应该重新初始化
expect(system.trackedEntities.length).toBe(1);
});
test('多个实体的重复初始化应该被防止', () => {
// 创建多个实体
const entities = [];
for (let i = 0; i < 5; i++) {
const entity = scene.createEntity(`Entity${i}`);
entity.addComponent(new TestComponent(i));
entities.push(entity);
}
scene.addEntityProcessor(system);
expect(system.initializeCallCount).toBe(1);
expect(system.trackedEntities.length).toBe(5);
expect(system.onChangedCallCount).toBe(5);
// 手动再次初始化 - 应该被防止
system.initialize();
expect(system.initializeCallCount).toBe(1); // 不应该增加
expect(system.onChangedCallCount).toBe(5); // 不应该重复处理
expect(system.trackedEntities.length).toBe(5);
});
});

View File

@@ -0,0 +1,375 @@
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
// 测试组件
class PositionComponent extends Component {
public x: number = 0;
public y: number = 0;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class HealthComponent extends Component {
public health: number = 100;
constructor(...args: unknown[]) {
super();
const [health = 100] = args as [number?];
this.health = health;
}
}
// 测试系统
class MovementSystem extends EntitySystem {
public processedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
// 简单的移动逻辑
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
const velocity = entity.getComponent(VelocityComponent)!;
position.x += velocity.vx;
position.y += velocity.vy;
}
}
}
class HealthSystem extends EntitySystem {
public processedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(HealthComponent));
}
protected override process(entities: Entity[]): void {
this.processedEntities = [...entities];
// 简单的健康检查逻辑
for (const entity of entities) {
const health = entity.getComponent(HealthComponent)!;
if (health.health <= 0) {
// 标记为死亡,但不在这里销毁实体
entity.enabled = false;
}
}
}
}
describe('ECS系统时序问题测试', () => {
let scene: Scene;
beforeEach(() => {
scene = new Scene();
scene.name = "TimingTestScene";
});
describe('实体添加时序问题', () => {
test(' 先添加实体再添加系统 - 暴露时序问题', () => {
// 第一步:先创建并添加实体
const player = scene.createEntity("Player");
player.addComponent(new PositionComponent(10, 20));
player.addComponent(new VelocityComponent(1, 1));
player.addComponent(new HealthComponent(100));
const enemy = scene.createEntity("Enemy");
enemy.addComponent(new PositionComponent(50, 60));
enemy.addComponent(new VelocityComponent(-1, 0));
enemy.addComponent(new HealthComponent(80));
// 验证实体已创建
expect(scene.entities.count).toBe(2);
// 第二步:然后添加系统(这里可能出现时序问题)
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证系统已添加
expect(scene.entityProcessors.count).toBe(2);
// 关键测试:检查系统是否正确识别了已存在的实体
console.log("MovementSystem匹配的实体数量:", movementSystem.entities.length);
console.log("HealthSystem匹配的实体数量:", healthSystem.entities.length);
console.log("Player组件:", player.components.map(c => c.constructor.name));
console.log("Enemy组件:", enemy.components.map(c => c.constructor.name));
// 预期结果移动系统应该匹配到2个实体都有Position+Velocity
expect(movementSystem.entities.length).toBe(2);
// 预期结果健康系统应该匹配到2个实体都有Health
expect(healthSystem.entities.length).toBe(2);
// 运行一次更新看看
scene.update();
// 检查系统是否处理了实体
expect(movementSystem.processedEntities.length).toBe(2);
expect(healthSystem.processedEntities.length).toBe(2);
// 检查移动逻辑是否生效
const playerPos = player.getComponent(PositionComponent)!;
expect(playerPos.x).toBe(11); // 10 + 1
expect(playerPos.y).toBe(21); // 20 + 1
});
test(' 先添加系统再添加实体 - 正常工作的情况', () => {
// 第一步:先添加系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 验证系统已添加但没有实体
expect(scene.entityProcessors.count).toBe(2);
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
// 第二步:然后创建并添加实体
const player = scene.createEntity("Player");
player.addComponent(new PositionComponent(10, 20));
player.addComponent(new VelocityComponent(1, 1));
player.addComponent(new HealthComponent(100));
const enemy = scene.createEntity("Enemy");
enemy.addComponent(new PositionComponent(50, 60));
enemy.addComponent(new VelocityComponent(-1, 0));
enemy.addComponent(new HealthComponent(80));
// 验证实体已创建
expect(scene.entities.count).toBe(2);
// 关键测试:检查系统是否正确识别了新添加的实体
console.log("MovementSystem匹配的实体数量:", movementSystem.entities.length);
console.log("HealthSystem匹配的实体数量:", healthSystem.entities.length);
// 预期结果:系统应该自动匹配到新实体
expect(movementSystem.entities.length).toBe(2);
expect(healthSystem.entities.length).toBe(2);
// 运行一次更新
scene.update();
// 检查系统是否处理了实体
expect(movementSystem.processedEntities.length).toBe(2);
expect(healthSystem.processedEntities.length).toBe(2);
});
});
describe('组件动态修改时序问题', () => {
test(' 运行时动态添加组件 - 检查系统响应', () => {
// 先设置好系统
const movementSystem = new MovementSystem();
const healthSystem = new HealthSystem();
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(healthSystem);
// 创建只有位置组件的实体
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
// 初始状态:只有健康系统不匹配,移动系统不匹配
expect(movementSystem.entities.length).toBe(0);
expect(healthSystem.entities.length).toBe(0);
// 动态添加速度组件
entity.addComponent(new VelocityComponent(5, 5));
// 关键测试:移动系统是否立即识别到这个实体
console.log("添加VelocityComponent后MovementSystem实体数:", movementSystem.entities.length);
expect(movementSystem.entities.length).toBe(1);
// 动态添加健康组件
entity.addComponent(new HealthComponent(50));
// 关键测试:健康系统是否立即识别到这个实体
console.log("添加HealthComponent后HealthSystem实体数:", healthSystem.entities.length);
expect(healthSystem.entities.length).toBe(1);
// 运行更新确认处理
scene.update();
expect(movementSystem.processedEntities.length).toBe(1);
expect(healthSystem.processedEntities.length).toBe(1);
});
test(' 运行时动态移除组件 - 检查系统响应', () => {
// 先设置好系统
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
// 创建完整的可移动实体
const entity = scene.createEntity("MovableEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(5, 5));
// 确认系统识别了实体
expect(movementSystem.entities.length).toBe(1);
// 动态移除速度组件
const velocityComponent = entity.getComponent(VelocityComponent);
if (velocityComponent) {
entity.removeComponent(velocityComponent);
}
// 关键测试:移动系统是否立即移除了这个实体
console.log("移除VelocityComponent后MovementSystem实体数:", movementSystem.entities.length);
expect(movementSystem.entities.length).toBe(0);
// 重新添加速度组件
entity.addComponent(new VelocityComponent(3, 3));
// 关键测试:移动系统是否重新识别到这个实体
console.log("重新添加VelocityComponent后MovementSystem实体数:", movementSystem.entities.length);
expect(movementSystem.entities.length).toBe(1);
});
});
describe('系统初始化时序问题', () => {
test(' 系统initialize方法调用时机', () => {
// 创建实体
const entity1 = scene.createEntity("Entity1");
entity1.addComponent(new PositionComponent(10, 10));
entity1.addComponent(new VelocityComponent(1, 1));
const entity2 = scene.createEntity("Entity2");
entity2.addComponent(new PositionComponent(20, 20));
entity2.addComponent(new VelocityComponent(2, 2));
// 创建系统但先不添加到场景
const movementSystem = new MovementSystem();
// 检查系统初始状态
expect(movementSystem.entities.length).toBe(0);
// 添加系统到场景
scene.addEntityProcessor(movementSystem);
// 关键测试:系统是否在添加时正确初始化并发现已存在的实体
console.log("系统添加后发现的实体数:", movementSystem.entities.length);
// 这个测试会暴露initialize方法是否被正确调用
expect(movementSystem.entities.length).toBe(2);
});
test(' 批量实体操作的时序问题', () => {
const movementSystem = new MovementSystem();
scene.addEntityProcessor(movementSystem);
// 批量创建实体
const entities = scene.createEntities(5, "BatchEntity");
// 为所有实体添加组件
entities.forEach((entity, index) => {
entity.addComponent(new PositionComponent(index * 10, index * 10));
entity.addComponent(new VelocityComponent(index, index));
});
// 关键测试:系统是否识别了所有批量创建的实体
console.log("批量操作后系统实体数:", movementSystem.entities.length);
expect(movementSystem.entities.length).toBe(5);
// 运行更新确认处理
scene.update();
expect(movementSystem.processedEntities.length).toBe(5);
});
});
describe('事件装饰器时序问题', () => {
// 模拟使用事件装饰器的系统
class EventDecoratedSystem extends EntitySystem {
public receivedEvents: any[] = [];
constructor() {
super(Matcher.empty().all(PositionComponent));
// 模拟装饰器初始化
this.initEventListeners();
}
// 模拟 @EventListener('entity:moved') 装饰器
private initEventListeners() {
// 这里应该通过装饰器自动完成
scene.eventSystem?.on('entity:moved', (data) => {
this.onEntityMoved(data);
});
}
public onEntityMoved(data: any) {
this.receivedEvents.push(data);
}
protected override process(entities: Entity[]): void {
// 处理实体移动并发射事件
for (const entity of entities) {
const pos = entity.getComponent(PositionComponent)!;
// 模拟移动
pos.x += 1;
// 发射移动事件
scene.eventSystem?.emit('entity:moved', {
entityId: entity.id,
position: { x: pos.x, y: pos.y }
});
}
}
}
test(' 事件装饰器系统的初始化时序', () => {
// 先创建实体
const entity = scene.createEntity("EventTestEntity");
entity.addComponent(new PositionComponent(0, 0));
// 然后添加使用事件装饰器的系统
const eventSystem = new EventDecoratedSystem();
scene.addEntityProcessor(eventSystem);
// 验证系统正确识别了实体
expect(eventSystem.entities.length).toBe(1);
// 运行更新,应该触发事件
scene.update();
// 关键测试:事件装饰器是否正确工作
console.log("接收到的事件数量:", eventSystem.receivedEvents.length);
expect(eventSystem.receivedEvents.length).toBe(1);
// 验证事件数据
const event = eventSystem.receivedEvents[0];
expect(event.entityId).toBe(entity.id);
expect(event.position.x).toBe(1); // 移动后的位置
});
});
afterEach(() => {
// 清理场景
scene.destroyAllEntities();
// 手动清理系统
const processors = [...scene.entityProcessors.processors];
processors.forEach(processor => scene.removeEntityProcessor(processor));
});
});

View File

@@ -0,0 +1,259 @@
import { Entity } from '../../src/ECS/Entity';
import { Component } from '../../src/ECS/Component';
// 测试组件类
class PerfTestComponent1 extends Component {
public value: number = 1;
}
class PerfTestComponent2 extends Component {
public value: number = 2;
}
class PerfTestComponent3 extends Component {
public value: number = 3;
}
class PerfTestComponent4 extends Component {
public value: number = 4;
}
class PerfTestComponent5 extends Component {
public value: number = 5;
}
class PerfTestComponent6 extends Component {
public value: number = 6;
}
class PerfTestComponent7 extends Component {
public value: number = 7;
}
class PerfTestComponent8 extends Component {
public value: number = 8;
}
describe('Entity - 性能测试', () => {
describe('典型游戏实体性能测试', () => {
test('3-5个组件的实体性能测试', () => {
const entity = new Entity('TypicalEntity', 1);
// 添加典型游戏实体的组件数量3-5个
entity.addComponent(new PerfTestComponent1());
entity.addComponent(new PerfTestComponent2());
entity.addComponent(new PerfTestComponent3());
entity.addComponent(new PerfTestComponent4());
entity.addComponent(new PerfTestComponent5());
const iterations = 10000;
const startTime = performance.now();
// 模拟典型的组件访问模式
for (let i = 0; i < iterations; i++) {
entity.getComponent(PerfTestComponent1);
entity.getComponent(PerfTestComponent3);
entity.getComponent(PerfTestComponent5);
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`典型实体(5组件) ${iterations * 3}次组件获取耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(150);
});
test('内存使用优化验证', () => {
const entities: Entity[] = [];
const entityCount = 1000;
// 创建大量实体,每个实体有少量组件
for (let i = 0; i < entityCount; i++) {
const entity = new Entity(`Entity_${i}`, i);
entity.addComponent(new PerfTestComponent1());
entity.addComponent(new PerfTestComponent2());
entity.addComponent(new PerfTestComponent3());
entities.push(entity);
}
// 测试批量组件访问性能
const startTime = performance.now();
for (const entity of entities) {
entity.getComponent(PerfTestComponent1);
entity.getComponent(PerfTestComponent2);
entity.getComponent(PerfTestComponent3);
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`${entityCount}个实体每个3个组件总计${entityCount * 3}次组件获取耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(100);
});
test('组件添加和移除性能测试', () => {
const entity = new Entity('TestEntity', 1);
const iterations = 1000;
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
// 添加组件
const comp1 = entity.addComponent(new PerfTestComponent1());
const comp2 = entity.addComponent(new PerfTestComponent2());
const comp3 = entity.addComponent(new PerfTestComponent3());
// 获取组件
entity.getComponent(PerfTestComponent1);
entity.getComponent(PerfTestComponent2);
entity.getComponent(PerfTestComponent3);
// 移除组件
entity.removeComponent(comp1);
entity.removeComponent(comp2);
entity.removeComponent(comp3);
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`${iterations}次组件添加-获取-移除循环耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(70);
});
});
describe('极端情况性能测试', () => {
test('单个组件高频访问性能', () => {
const entity = new Entity('SingleComponentEntity', 1);
entity.addComponent(new PerfTestComponent1());
const iterations = 50000;
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
entity.getComponent(PerfTestComponent1);
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`单组件${iterations}次高频访问耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(150);
});
test('多组件实体性能测试', () => {
const entity = new Entity('MultiComponentEntity', 1);
// 添加8个组件比典型情况多
entity.addComponent(new PerfTestComponent1());
entity.addComponent(new PerfTestComponent2());
entity.addComponent(new PerfTestComponent3());
entity.addComponent(new PerfTestComponent4());
entity.addComponent(new PerfTestComponent5());
entity.addComponent(new PerfTestComponent6());
entity.addComponent(new PerfTestComponent7());
entity.addComponent(new PerfTestComponent8());
const iterations = 5000;
const startTime = performance.now();
// 随机访问不同组件
for (let i = 0; i < iterations; i++) {
entity.getComponent(PerfTestComponent1);
entity.getComponent(PerfTestComponent4);
entity.getComponent(PerfTestComponent7);
entity.getComponent(PerfTestComponent2);
entity.getComponent(PerfTestComponent8);
entity.getComponent(PerfTestComponent3);
entity.getComponent(PerfTestComponent6);
entity.getComponent(PerfTestComponent5);
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`多组件实体(8组件) ${iterations * 8}次随机访问耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(200);
});
test('hasComponent性能测试', () => {
const entity = new Entity('HasComponentTestEntity', 1);
entity.addComponent(new PerfTestComponent1());
entity.addComponent(new PerfTestComponent3());
entity.addComponent(new PerfTestComponent5());
const iterations = 25000; // 减少迭代次数以适应CI环境
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
entity.hasComponent(PerfTestComponent1); // 存在
entity.hasComponent(PerfTestComponent2); // 不存在
entity.hasComponent(PerfTestComponent3); // 存在
entity.hasComponent(PerfTestComponent4); // 不存在
entity.hasComponent(PerfTestComponent5); // 存在
}
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`${iterations * 5}次hasComponent检查耗时: ${duration.toFixed(2)}ms`);
expect(duration).toBeLessThan(310);
});
});
describe('内存效率测试', () => {
test('大量实体内存使用测试', () => {
const entities: Entity[] = [];
const entityCount = 5000;
const startTime = performance.now();
// 创建大量实体,模拟真实游戏场景
for (let i = 0; i < entityCount; i++) {
const entity = new Entity(`Entity_${i}`, i);
// 每个实体随机添加2-6个组件
const componentCount = 2 + (i % 5);
if (componentCount >= 1) entity.addComponent(new PerfTestComponent1());
if (componentCount >= 2) entity.addComponent(new PerfTestComponent2());
if (componentCount >= 3) entity.addComponent(new PerfTestComponent3());
if (componentCount >= 4) entity.addComponent(new PerfTestComponent4());
if (componentCount >= 5) entity.addComponent(new PerfTestComponent5());
if (componentCount >= 6) entity.addComponent(new PerfTestComponent6());
entities.push(entity);
}
const creationTime = performance.now() - startTime;
// 测试访问性能
const accessStartTime = performance.now();
for (const entity of entities) {
entity.getComponent(PerfTestComponent1);
if (entity.hasComponent(PerfTestComponent3)) {
entity.getComponent(PerfTestComponent3);
}
if (entity.hasComponent(PerfTestComponent5)) {
entity.getComponent(PerfTestComponent5);
}
}
const accessTime = performance.now() - accessStartTime;
console.log(`创建${entityCount}个实体耗时: ${creationTime.toFixed(2)}ms`);
console.log(`访问${entityCount}个实体的组件耗时: ${accessTime.toFixed(2)}ms`);
expect(creationTime).toBeLessThan(150);
expect(accessTime).toBeLessThan(100);
});
});
});

View File

@@ -0,0 +1,272 @@
import { Entity } from '../../src/ECS/Entity';
import { Component } from '../../src/ECS/Component';
// 测试组件类
class TestPositionComponent extends Component {
public x: number = 0;
public y: number = 0;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class TestHealthComponent extends Component {
public health: number = 100;
constructor(...args: unknown[]) {
super();
const [health = 100] = args as [number?];
this.health = health;
}
}
class TestVelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class TestRenderComponent extends Component {
public visible: boolean = true;
constructor(...args: unknown[]) {
super();
const [visible = true] = args as [boolean?];
this.visible = visible;
}
}
describe('Entity - 组件缓存优化测试', () => {
let entity: Entity;
beforeEach(() => {
// 创建新的实体
entity = new Entity('TestEntity', 1);
});
describe('基本功能测试', () => {
test('应该能够创建实体', () => {
expect(entity.name).toBe('TestEntity');
expect(entity.id).toBe(1);
expect(entity.components.length).toBe(0);
});
test('应该能够添加组件', () => {
const position = new TestPositionComponent(10, 20);
const addedComponent = entity.addComponent(position);
expect(addedComponent).toBe(position);
expect(entity.components.length).toBe(1);
expect(entity.components[0]).toBe(position);
expect(position.entity).toBe(entity);
});
test('应该能够获取组件', () => {
const position = new TestPositionComponent(10, 20);
entity.addComponent(position);
const retrieved = entity.getComponent(TestPositionComponent);
expect(retrieved).toBe(position);
expect(retrieved?.x).toBe(10);
expect(retrieved?.y).toBe(20);
});
test('应该能够检查组件存在性', () => {
const position = new TestPositionComponent(10, 20);
entity.addComponent(position);
expect(entity.hasComponent(TestPositionComponent)).toBe(true);
expect(entity.hasComponent(TestHealthComponent)).toBe(false);
});
test('应该能够移除组件', () => {
const position = new TestPositionComponent(10, 20);
entity.addComponent(position);
entity.removeComponent(position);
expect(entity.components.length).toBe(0);
expect(entity.hasComponent(TestPositionComponent)).toBe(false);
expect(position.entity).toBeNull();
});
});
describe('多组件管理测试', () => {
test('应该能够管理多个不同类型的组件', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
const velocity = new TestVelocityComponent(5, -3);
entity.addComponent(position);
entity.addComponent(health);
entity.addComponent(velocity);
expect(entity.components.length).toBe(3);
expect(entity.hasComponent(TestPositionComponent)).toBe(true);
expect(entity.hasComponent(TestHealthComponent)).toBe(true);
expect(entity.hasComponent(TestVelocityComponent)).toBe(true);
});
test('应该能够正确获取多个组件', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
const velocity = new TestVelocityComponent(5, -3);
entity.addComponent(position);
entity.addComponent(health);
entity.addComponent(velocity);
const retrievedPosition = entity.getComponent(TestPositionComponent);
const retrievedHealth = entity.getComponent(TestHealthComponent);
const retrievedVelocity = entity.getComponent(TestVelocityComponent);
expect(retrievedPosition).toBe(position);
expect(retrievedHealth).toBe(health);
expect(retrievedVelocity).toBe(velocity);
});
test('应该能够批量添加组件', () => {
const components = [
new TestPositionComponent(10, 20),
new TestHealthComponent(150),
new TestVelocityComponent(5, -3)
];
const addedComponents = entity.addComponents(components);
expect(addedComponents.length).toBe(3);
expect(entity.components.length).toBe(3);
expect(addedComponents[0]).toBe(components[0]);
expect(addedComponents[1]).toBe(components[1]);
expect(addedComponents[2]).toBe(components[2]);
});
test('应该能够移除所有组件', () => {
entity.addComponent(new TestPositionComponent(10, 20));
entity.addComponent(new TestHealthComponent(150));
entity.addComponent(new TestVelocityComponent(5, -3));
entity.removeAllComponents();
expect(entity.components.length).toBe(0);
expect(entity.hasComponent(TestPositionComponent)).toBe(false);
expect(entity.hasComponent(TestHealthComponent)).toBe(false);
expect(entity.hasComponent(TestVelocityComponent)).toBe(false);
});
});
describe('性能优化验证', () => {
test('位掩码应该正确工作', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
entity.addComponent(position);
entity.addComponent(health);
// 位掩码应该反映组件的存在
expect(entity.hasComponent(TestPositionComponent)).toBe(true);
expect(entity.hasComponent(TestHealthComponent)).toBe(true);
expect(entity.hasComponent(TestVelocityComponent)).toBe(false);
});
test('索引映射应该正确维护', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
const velocity = new TestVelocityComponent(5, -3);
entity.addComponent(position);
entity.addComponent(health);
entity.addComponent(velocity);
// 获取组件应该通过索引映射快速完成
const retrievedPosition = entity.getComponent(TestPositionComponent);
const retrievedHealth = entity.getComponent(TestHealthComponent);
const retrievedVelocity = entity.getComponent(TestVelocityComponent);
expect(retrievedPosition).toBe(position);
expect(retrievedHealth).toBe(health);
expect(retrievedVelocity).toBe(velocity);
});
test('组件获取性能应该良好', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
const velocity = new TestVelocityComponent(5, -3);
const render = new TestRenderComponent(true);
entity.addComponent(position);
entity.addComponent(health);
entity.addComponent(velocity);
entity.addComponent(render);
// 测试大量获取操作的性能
const iterations = 1000;
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
entity.getComponent(TestPositionComponent);
entity.getComponent(TestHealthComponent);
entity.getComponent(TestVelocityComponent);
entity.getComponent(TestRenderComponent);
}
const endTime = performance.now();
const duration = endTime - startTime;
// 1000次 * 4个组件 = 4000次获取操作应该在合理时间内完成
expect(duration).toBeLessThan(100); // 应该在100ms内完成
});
});
describe('边界情况测试', () => {
test('获取不存在的组件应该返回null', () => {
const result = entity.getComponent(TestPositionComponent);
expect(result).toBeNull();
});
test('不应该允许添加重复类型的组件', () => {
const position1 = new TestPositionComponent(10, 20);
const position2 = new TestPositionComponent(30, 40);
entity.addComponent(position1);
expect(() => {
entity.addComponent(position2);
}).toThrow();
});
test('移除不存在的组件应该安全处理', () => {
const position = new TestPositionComponent(10, 20);
expect(() => {
entity.removeComponent(position);
}).not.toThrow();
});
test('调试信息应该正确反映实体状态', () => {
const position = new TestPositionComponent(10, 20);
const health = new TestHealthComponent(150);
entity.addComponent(position);
entity.addComponent(health);
const debugInfo = entity.getDebugInfo();
expect(debugInfo.name).toBe('TestEntity');
expect(debugInfo.id).toBe(1);
expect(debugInfo.componentCount).toBe(2);
expect(debugInfo.componentTypes).toContain('TestPositionComponent');
expect(debugInfo.componentTypes).toContain('TestHealthComponent');
expect(debugInfo.indexMappingSize).toBe(2);
});
});
});

View File

@@ -0,0 +1,572 @@
import { Scene } from '../../src/ECS/Scene';
import { Entity } from '../../src/ECS/Entity';
import { Component } from '../../src/ECS/Component';
import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
import { Matcher } from '../../src/ECS/Utils/Matcher';
// 测试组件
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public vx: number;
public vy: number;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class HealthComponent extends Component {
public health: number;
constructor(...args: unknown[]) {
super();
const [health = 100] = args as [number?];
this.health = health;
}
}
class RenderComponent extends Component {
public visible: boolean;
constructor(...args: unknown[]) {
super();
const [visible = true] = args as [boolean?];
this.visible = visible;
}
}
// 测试系统
class MovementSystem extends EntitySystem {
public processCallCount = 0;
public lastProcessedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
this.lastProcessedEntities = [...entities];
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
if (position && velocity) {
position.x += velocity.vx;
position.y += velocity.vy;
}
}
}
}
class RenderSystem extends EntitySystem {
public processCallCount = 0;
public lastProcessedEntities: Entity[] = [];
constructor() {
super(Matcher.empty().all(PositionComponent, RenderComponent));
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
this.lastProcessedEntities = [...entities];
}
}
// 测试场景
class TestScene extends Scene {
public initializeCalled = false;
public beginCalled = false;
public endCalled = false;
public updateCallCount = 0;
public override initialize(): void {
this.initializeCalled = true;
super.initialize();
}
public override begin(): void {
this.beginCalled = true;
super.begin();
}
public override end(): void {
this.endCalled = true;
super.end();
}
public override update(): void {
this.updateCallCount++;
super.update();
}
}
describe('Scene - 场景管理系统测试', () => {
let scene: Scene;
beforeEach(() => {
scene = new Scene();
});
describe('基本功能测试', () => {
test('应该能够创建场景', () => {
expect(scene).toBeDefined();
expect(scene).toBeInstanceOf(Scene);
expect(scene.name).toBe("");
expect(scene.entities).toBeDefined();
expect(scene.entityProcessors).toBeDefined();
expect(scene.identifierPool).toBeDefined();
});
test('应该能够设置场景名称', () => {
scene.name = "TestScene";
expect(scene.name).toBe("TestScene");
});
test('场景应该有正确的初始状态', () => {
expect(scene.entities.count).toBe(0);
expect(scene.entityProcessors.count).toBe(0);
});
});
describe('实体管理', () => {
test('应该能够创建实体', () => {
const entity = scene.createEntity("TestEntity");
expect(entity).toBeDefined();
expect(entity.name).toBe("TestEntity");
expect(entity.id).toBeGreaterThan(0);
expect(scene.entities.count).toBe(1);
});
test('应该能够批量创建实体', () => {
const entities = scene.createEntities(5, "Entity");
expect(entities.length).toBe(5);
expect(scene.entities.count).toBe(5);
for (let i = 0; i < entities.length; i++) {
expect(entities[i].name).toBe(`Entity_${i}`);
expect(entities[i].id).toBeGreaterThan(0);
}
});
test('应该能够通过ID查找实体', () => {
const entity = scene.createEntity("TestEntity");
const found = scene.findEntityById(entity.id);
expect(found).toBe(entity);
});
test('查找不存在的实体应该返回null', () => {
const found = scene.findEntityById(999999);
expect(found).toBeNull();
});
test('应该能够销毁实体', () => {
const entity = scene.createEntity("TestEntity");
const entityId = entity.id;
scene.entities.remove(entity);
expect(scene.entities.count).toBe(0);
expect(scene.findEntityById(entityId)).toBeNull();
});
test('应该能够通过ID销毁实体', () => {
const entity = scene.createEntity("TestEntity");
const entityId = entity.id;
const entityToRemove = scene.findEntityById(entityId)!;
scene.entities.remove(entityToRemove);
expect(scene.entities.count).toBe(0);
expect(scene.findEntityById(entityId)).toBeNull();
});
test('销毁不存在的实体应该安全处理', () => {
expect(() => {
// Scene doesn't have destroyEntity method
expect(scene.findEntityById(999999)).toBeNull();
}).not.toThrow();
});
test('应该能够销毁所有实体', () => {
scene.createEntities(10, "Entity");
expect(scene.entities.count).toBe(10);
scene.destroyAllEntities();
expect(scene.entities.count).toBe(0);
});
});
describe('实体系统管理', () => {
let movementSystem: MovementSystem;
let renderSystem: RenderSystem;
beforeEach(() => {
movementSystem = new MovementSystem();
renderSystem = new RenderSystem();
});
test('应该能够添加实体系统', () => {
scene.addEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(1);
expect(movementSystem.scene).toBe(scene);
});
test('应该能够移除实体系统', () => {
scene.addEntityProcessor(movementSystem);
scene.removeEntityProcessor(movementSystem);
expect(scene.entityProcessors.count).toBe(0);
expect(movementSystem.scene).toBeNull();
});
test('移除不存在的系统应该安全处理', () => {
expect(() => {
scene.removeEntityProcessor(movementSystem);
}).not.toThrow();
});
test('应该能够管理多个实体系统', () => {
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(renderSystem);
expect(scene.entityProcessors.count).toBe(2);
});
test('系统应该按更新顺序执行', () => {
movementSystem.updateOrder = 1;
renderSystem.updateOrder = 0;
scene.addEntityProcessor(movementSystem);
scene.addEntityProcessor(renderSystem);
// 创建测试实体
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
entity.addComponent(new RenderComponent(true));
scene.update();
// RenderSystem应该先执行updateOrder = 0
// MovementSystem应该后执行updateOrder = 1
expect(renderSystem.processCallCount).toBe(1);
expect(movementSystem.processCallCount).toBe(1);
});
});
describe('组件查询系统', () => {
beforeEach(() => {
// 创建测试实体
const entity1 = scene.createEntity("Entity1");
entity1.addComponent(new PositionComponent(10, 20));
entity1.addComponent(new VelocityComponent(1, 0));
const entity2 = scene.createEntity("Entity2");
entity2.addComponent(new PositionComponent(30, 40));
entity2.addComponent(new HealthComponent(80));
const entity3 = scene.createEntity("Entity3");
entity3.addComponent(new VelocityComponent(0, 1));
entity3.addComponent(new HealthComponent(120));
});
test('应该能够查询具有特定组件的实体', () => {
const result = scene.querySystem.queryAll(PositionComponent);
expect(result.entities.length).toBe(2);
expect(result.entities[0].name).toBe("Entity1");
expect(result.entities[1].name).toBe("Entity2");
});
test('应该能够查询具有多个组件的实体', () => {
const result = scene.querySystem.queryAll(PositionComponent, VelocityComponent);
expect(result.entities.length).toBe(1);
expect(result.entities[0].name).toBe("Entity1");
});
test('查询不存在的组件应该返回空结果', () => {
const result = scene.querySystem.queryAll(RenderComponent);
expect(result.entities.length).toBe(0);
});
test('查询系统应该支持缓存', () => {
// 第一次查询
const result1 = scene.querySystem.queryAll(PositionComponent);
// 第二次查询(应该使用缓存)
const result2 = scene.querySystem.queryAll(PositionComponent);
// 实体数组应该相同,并且第二次查询应该来自缓存
expect(result1.entities).toEqual(result2.entities);
expect(result2.fromCache).toBe(true);
});
test('组件变化应该更新查询缓存', () => {
const result1 = scene.querySystem.queryAll(PositionComponent);
expect(result1.entities.length).toBe(2);
// 添加新实体
const entity4 = scene.createEntity("Entity4");
entity4.addComponent(new PositionComponent(50, 60));
const result2 = scene.querySystem.queryAll(PositionComponent);
expect(result2.entities.length).toBe(3);
});
});
describe('事件系统', () => {
test('场景应该有事件系统', () => {
expect(scene.eventSystem).toBeDefined();
});
test('应该能够监听实体事件', () => {
let entityCreatedEvent: any = null;
scene.eventSystem.on('entity:created', (data: any) => {
entityCreatedEvent = data;
});
const entity = scene.createEntity("TestEntity");
expect(entityCreatedEvent).toBeDefined();
expect(entityCreatedEvent.entityName).toBe("TestEntity");
});
test('应该能够监听组件事件', () => {
let componentAddedEvent: any = null;
scene.eventSystem.on('component:added', (data: any) => {
componentAddedEvent = data;
});
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(10, 20));
expect(componentAddedEvent).toBeDefined();
expect(componentAddedEvent.componentType).toBe('PositionComponent');
});
});
describe('场景生命周期管理', () => {
let testScene: TestScene;
beforeEach(() => {
testScene = new TestScene();
});
test('应该能够初始化场景', () => {
testScene.initialize();
expect(testScene.initializeCalled).toBe(true);
});
test('应该能够开始场景', () => {
testScene.begin();
expect(testScene.beginCalled).toBe(true);
});
test('应该能够结束场景', () => {
testScene.end();
expect(testScene.endCalled).toBe(true);
});
test('应该能够更新场景', () => {
const movementSystem = new MovementSystem();
testScene.addEntityProcessor(movementSystem);
// 创建测试实体
const entity = testScene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
entity.addComponent(new VelocityComponent(1, 1));
testScene.update();
expect(testScene.updateCallCount).toBe(1);
expect(movementSystem.processCallCount).toBe(1);
// 验证移动系统是否正确处理了实体
const position = entity.getComponent(PositionComponent);
expect(position?.x).toBe(1);
expect(position?.y).toBe(1);
});
});
describe('统计和调试信息', () => {
test('应该能够获取场景统计信息', () => {
// 创建一些实体和系统
scene.createEntities(5, "Entity");
scene.addEntityProcessor(new MovementSystem());
scene.addEntityProcessor(new RenderSystem());
const stats = scene.getStats();
expect(stats.entityCount).toBe(5);
expect(stats.processorCount).toBe(2);
});
test('空场景的统计信息应该正确', () => {
const stats = scene.getStats();
expect(stats.entityCount).toBe(0);
expect(stats.processorCount).toBe(0);
});
test('查询系统应该有统计信息', () => {
// 执行一些查询以产生统计数据
scene.querySystem.queryAll(PositionComponent);
scene.querySystem.queryAll(VelocityComponent);
const stats = scene.querySystem.getStats();
expect(stats.queryStats.totalQueries).toBeGreaterThan(0);
expect(parseFloat(stats.cacheStats.hitRate)).toBeGreaterThanOrEqual(0);
});
});
describe('内存管理和性能', () => {
test('销毁大量实体应该正确清理内存', () => {
const entityCount = 1000;
const entities = scene.createEntities(entityCount, "Entity");
// 为每个实体添加组件
entities.forEach(entity => {
entity.addComponent(new PositionComponent(Math.random() * 100, Math.random() * 100));
entity.addComponent(new VelocityComponent(Math.random() * 5, Math.random() * 5));
});
expect(scene.entities.count).toBe(entityCount);
// 销毁所有实体
scene.destroyAllEntities();
expect(scene.entities.count).toBe(0);
// 查询应该返回空结果
const positionResult = scene.querySystem.queryAll(PositionComponent);
const velocityResult = scene.querySystem.queryAll(VelocityComponent);
expect(positionResult.entities.length).toBe(0);
expect(velocityResult.entities.length).toBe(0);
});
test('大量实体的创建和查询性能应该可接受', () => {
const entityCount = 5000;
const startTime = performance.now();
// 创建大量实体
const entities = scene.createEntities(entityCount, "Entity");
// 为每个实体添加组件
entities.forEach((entity, index) => {
entity.addComponent(new PositionComponent(index, index));
if (index % 2 === 0) {
entity.addComponent(new VelocityComponent(1, 1));
}
if (index % 3 === 0) {
entity.addComponent(new HealthComponent(100));
}
});
const creationTime = performance.now() - startTime;
// 测试查询性能
const queryStartTime = performance.now();
const positionResult = scene.querySystem.queryAll(PositionComponent);
const velocityResult = scene.querySystem.queryAll(VelocityComponent);
const healthResult = scene.querySystem.queryAll(HealthComponent);
const queryTime = performance.now() - queryStartTime;
expect(positionResult.entities.length).toBe(entityCount);
expect(velocityResult.entities.length).toBe(entityCount / 2);
expect(healthResult.entities.length).toBe(Math.floor(entityCount / 3) + 1);
// 性能断言(这些值可能需要根据实际环境调整)
expect(creationTime).toBeLessThan(2000); // 创建应该在2秒内完成
expect(queryTime).toBeLessThan(100); // 查询应该在100ms内完成
console.log(`创建${entityCount}个实体耗时: ${creationTime.toFixed(2)}ms`);
console.log(`查询操作耗时: ${queryTime.toFixed(2)}ms`);
});
});
describe('错误处理和边界情况', () => {
test('重复添加同一个系统应该安全处理', () => {
const system = new MovementSystem();
scene.addEntityProcessor(system);
scene.addEntityProcessor(system); // 重复添加
expect(scene.entityProcessors.count).toBe(1);
});
test('系统处理过程中的异常应该被正确处理', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
class ErrorSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected override process(entities: Entity[]): void {
throw new Error("Test system error");
}
}
const errorSystem = new ErrorSystem();
scene.addEntityProcessor(errorSystem);
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent(0, 0));
// 更新不应该抛出异常
expect(() => {
scene.update();
}).not.toThrow();
consoleSpy.mockRestore();
});
test('空场景的更新应该安全', () => {
expect(() => {
scene.update();
}).not.toThrow();
});
test('对已销毁实体的操作应该安全处理', () => {
const entity = scene.createEntity("TestEntity");
scene.entities.remove(entity);
// 对已销毁实体的操作应该安全
expect(() => {
entity.addComponent(new PositionComponent(0, 0));
}).not.toThrow();
});
});
});

View File

@@ -0,0 +1,383 @@
import { PassiveSystem } from '../../../src/ECS/Systems/PassiveSystem';
import { IntervalSystem } from '../../../src/ECS/Systems/IntervalSystem';
import { ProcessingSystem } from '../../../src/ECS/Systems/ProcessingSystem';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage';
import { Time } from '../../../src/Utils/Time';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
// 测试组件
class TestComponent extends Component {
public value: number = 0;
constructor(...args: unknown[]) {
super();
const [value = 0] = args as [number?];
this.value = value;
}
}
class AnotherComponent extends Component {
public name: string = 'test';
constructor(...args: unknown[]) {
super();
const [name = 'test'] = args as [string?];
this.name = name;
}
}
// 具体的被动系统实现
class ConcretePassiveSystem extends PassiveSystem {
public processCallCount = 0;
constructor() {
super(Matcher.all(TestComponent));
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
// 被动系统的process方法会被调用但不做任何处理
super.process(entities);
}
}
// 具体的间隔系统实现
class ConcreteIntervalSystem extends IntervalSystem {
public processCallCount = 0;
public lastDelta = 0;
constructor(interval: number) {
super(interval, Matcher.all(TestComponent));
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
this.lastDelta = this.getIntervalDelta();
}
}
// 具体的处理系统实现
class ConcreteProcessingSystem extends ProcessingSystem {
public processSystemCallCount = 0;
public processCallCount = 0;
constructor() {
super(Matcher.all(TestComponent));
}
public processSystem(): void {
this.processSystemCallCount++;
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
super.process(entities);
}
}
describe('System Types - 系统类型测试', () => {
let entity: Entity;
beforeEach(() => {
entity = new Entity('TestEntity', 1);
// 重置时间系统
Time.update(0.016);
// 注册测试组件类型
ComponentRegistry.register(TestComponent);
ComponentRegistry.register(AnotherComponent);
});
describe('PassiveSystem - 被动系统', () => {
let passiveSystem: ConcretePassiveSystem;
beforeEach(() => {
passiveSystem = new ConcretePassiveSystem();
});
test('应该能够创建被动系统', () => {
expect(passiveSystem).toBeInstanceOf(PassiveSystem);
expect(passiveSystem).toBeInstanceOf(ConcretePassiveSystem);
});
test('process方法不应该做任何处理', () => {
const entities = [entity];
const initialProcessCount = passiveSystem.processCallCount;
passiveSystem.update();
// 虽然process被调用了但被动系统不做任何实际处理
expect(passiveSystem.processCallCount).toBe(initialProcessCount + 1);
});
test('应该能够动态查询匹配的实体', () => {
// 现在使用动态查询不需要手动add/remove
// 先检查没有匹配的实体
expect(passiveSystem.entities.length).toBe(0);
// 添加匹配的组件后,系统应该能查询到实体
entity.addComponent(new TestComponent(100));
// 需要设置场景和QuerySystem才能进行动态查询
// 这里我们只测试entities getter的存在性
expect(passiveSystem.entities).toBeDefined();
});
});
describe('IntervalSystem - 间隔系统', () => {
let intervalSystem: ConcreteIntervalSystem;
const testInterval = 0.1; // 100ms
beforeEach(() => {
intervalSystem = new ConcreteIntervalSystem(testInterval);
});
test('应该能够创建间隔系统', () => {
expect(intervalSystem).toBeInstanceOf(IntervalSystem);
expect(intervalSystem).toBeInstanceOf(ConcreteIntervalSystem);
});
test('在间隔时间内不应该处理', () => {
const initialProcessCount = intervalSystem.processCallCount;
// 模拟时间更新,但不足以触发间隔
Time.update(testInterval / 2);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(initialProcessCount);
});
test('达到间隔时间时应该处理', () => {
const initialProcessCount = intervalSystem.processCallCount;
// 模拟时间更新,刚好达到间隔
Time.update(testInterval);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(initialProcessCount + 1);
});
test('超过间隔时间应该处理并记录余数', () => {
const initialProcessCount = intervalSystem.processCallCount;
const overTime = testInterval + 0.02; // 超过20ms
Time.update(overTime);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(initialProcessCount + 1);
expect(intervalSystem.lastDelta).toBe(testInterval + 0.02);
});
test('多次累积应该正确触发', () => {
let processCount = intervalSystem.processCallCount;
// 多次小的时间增量
for (let i = 0; i < 10; i++) {
Time.update(testInterval / 5);
intervalSystem.update();
}
// 10 * (interval/5) = 2 * interval应该触发2次
expect(intervalSystem.processCallCount).toBeGreaterThanOrEqual(processCount + 1);
});
test('getIntervalDelta应该返回正确的增量', () => {
const overTime = testInterval + 0.03;
Time.update(overTime);
intervalSystem.update();
expect(intervalSystem.lastDelta).toBe(testInterval + 0.03);
});
test('重置后应该重新开始计时', () => {
// 第一次触发
Time.update(testInterval);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(1);
// 再次触发需要等待完整间隔
Time.update(testInterval / 2);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(1);
Time.update(testInterval / 2);
intervalSystem.update();
expect(intervalSystem.processCallCount).toBe(2);
});
});
describe('ProcessingSystem - 处理系统', () => {
let processingSystem: ConcreteProcessingSystem;
beforeEach(() => {
processingSystem = new ConcreteProcessingSystem();
});
test('应该能够创建处理系统', () => {
expect(processingSystem).toBeInstanceOf(ProcessingSystem);
expect(processingSystem).toBeInstanceOf(ConcreteProcessingSystem);
});
test('process方法应该调用processSystem', () => {
const initialProcessSystemCount = processingSystem.processSystemCallCount;
const initialProcessCount = processingSystem.processCallCount;
processingSystem.update();
expect(processingSystem.processCallCount).toBe(initialProcessCount + 1);
expect(processingSystem.processSystemCallCount).toBe(initialProcessSystemCount + 1);
});
test('每次更新都应该调用processSystem', () => {
const initialCount = processingSystem.processSystemCallCount;
processingSystem.update();
processingSystem.update();
processingSystem.update();
expect(processingSystem.processSystemCallCount).toBe(initialCount + 3);
});
test('应该能够动态查询多个实体', () => {
// 现在使用动态查询不需要手动add
// 测试系统的基本功能
const initialCount = processingSystem.processSystemCallCount;
processingSystem.update();
// processSystem应该被调用不管有多少实体
expect(processingSystem.processSystemCallCount).toBe(initialCount + 1);
// 测试entities getter的存在性
expect(processingSystem.entities).toBeDefined();
expect(Array.isArray(processingSystem.entities)).toBe(true);
});
});
describe('系统集成测试', () => {
test('不同类型的系统应该都继承自EntitySystem', () => {
const passive = new ConcretePassiveSystem();
const interval = new ConcreteIntervalSystem(0.1);
const processing = new ConcreteProcessingSystem();
expect(passive.matcher).toBeDefined();
expect(interval.matcher).toBeDefined();
expect(processing.matcher).toBeDefined();
expect(passive.entities).toBeDefined();
expect(interval.entities).toBeDefined();
expect(processing.entities).toBeDefined();
expect(passive.systemName).toBeDefined();
expect(interval.systemName).toBeDefined();
expect(processing.systemName).toBeDefined();
});
test('系统应该能够正确匹配实体', () => {
const passive = new ConcretePassiveSystem();
const interval = new ConcreteIntervalSystem(0.1);
const processing = new ConcreteProcessingSystem();
const matchingEntity = new Entity('Matching', 1);
matchingEntity.addComponent(new TestComponent(100));
const nonMatchingEntity = new Entity('NonMatching', 2);
nonMatchingEntity.addComponent(new AnotherComponent('test'));
// 所有系统都应该匹配TestComponent
// 直接检查实体是否有需要的组件
expect(matchingEntity.hasComponent(TestComponent)).toBe(true);
expect(nonMatchingEntity.hasComponent(TestComponent)).toBe(false);
expect(nonMatchingEntity.hasComponent(AnotherComponent)).toBe(true);
});
});
describe('Matcher高级查询功能测试', () => {
test('应该能使用新的静态方法创建匹配器', () => {
// 测试新的静态方法
const byTagMatcher = Matcher.byTag(100);
const byNameMatcher = Matcher.byName('Player');
const byComponentMatcher = Matcher.byComponent(TestComponent);
expect(byTagMatcher.getCondition().tag).toBe(100);
expect(byNameMatcher.getCondition().name).toBe('Player');
expect(byComponentMatcher.getCondition().component).toBe(TestComponent);
});
test('应该支持链式组合查询', () => {
const complexMatcher = Matcher.all(TestComponent)
.withTag(100)
.withName('Player')
.none(AnotherComponent);
const condition = complexMatcher.getCondition();
expect(condition.all).toContain(TestComponent);
expect(condition.tag).toBe(100);
expect(condition.name).toBe('Player');
expect(condition.none).toContain(AnotherComponent);
});
test('应该能够移除特定条件', () => {
const matcher = Matcher.byTag(100)
.withName('Player')
.withComponent(TestComponent);
// 移除条件
matcher.withoutTag().withoutName();
const condition = matcher.getCondition();
expect(condition.tag).toBeUndefined();
expect(condition.name).toBeUndefined();
expect(condition.component).toBe(TestComponent);
});
test('应该能够正确重置所有条件', () => {
const matcher = Matcher.all(TestComponent)
.withTag(100)
.withName('Player')
.any(AnotherComponent);
matcher.reset();
expect(matcher.isEmpty()).toBe(true);
expect(matcher.getCondition().all.length).toBe(0);
expect(matcher.getCondition().any.length).toBe(0);
expect(matcher.getCondition().tag).toBeUndefined();
expect(matcher.getCondition().name).toBeUndefined();
});
test('应该能够正确克隆匹配器', () => {
const original = Matcher.all(TestComponent)
.withTag(100)
.withName('Player');
const cloned = original.clone();
expect(cloned.getCondition().all).toEqual(original.getCondition().all);
expect(cloned.getCondition().tag).toBe(original.getCondition().tag);
expect(cloned.getCondition().name).toBe(original.getCondition().name);
// 修改克隆的不应该影响原始的
cloned.withTag(200);
expect(original.getCondition().tag).toBe(100);
expect(cloned.getCondition().tag).toBe(200);
});
test('应该能够生成正确的字符串表示', () => {
const complexMatcher = Matcher.all(TestComponent)
.withTag(100)
.withName('Player')
.none(AnotherComponent);
const str = complexMatcher.toString();
expect(str).toContain('all(TestComponent)');
expect(str).toContain('tag(100)');
expect(str).toContain('name(Player)');
expect(str).toContain('none(AnotherComponent)');
});
});
});

View File

@@ -0,0 +1,338 @@
import {
BigIntFactory,
IBigIntLike,
EnvironmentInfo
} from '../../../src/ECS/Utils/BigIntCompatibility';
describe('BigInt兼容性测试', () => {
describe('BigIntFactory环境检测', () => {
it('应该能够检测BigInt支持情况', () => {
const isSupported = BigIntFactory.isNativeSupported();
expect(typeof isSupported).toBe('boolean');
});
it('应该返回环境信息', () => {
const envInfo = BigIntFactory.getEnvironmentInfo();
expect(envInfo).toBeDefined();
expect(typeof envInfo.supportsBigInt).toBe('boolean');
expect(typeof envInfo.environment).toBe('string');
expect(typeof envInfo.jsEngine).toBe('string');
});
it('环境信息应该包含合理的字段', () => {
const envInfo = BigIntFactory.getEnvironmentInfo();
expect(envInfo.environment).not.toBe('');
expect(envInfo.jsEngine).not.toBe('');
});
});
describe('BigIntFactory基本创建', () => {
it('应该能够创建零值', () => {
const zero = BigIntFactory.zero();
expect(zero.isZero()).toBe(true);
expect(zero.toString()).toBe('0');
});
it('应该能够创建1值', () => {
const one = BigIntFactory.one();
expect(one.isZero()).toBe(false);
expect(one.toString()).toBe('1');
});
it('应该能够从数值创建', () => {
const value = BigIntFactory.create(42);
expect(value.toString()).toBe('42');
expect(value.valueOf()).toBe(42);
});
it('应该能够从字符串创建', () => {
const value = BigIntFactory.create('123');
expect(value.toString()).toBe('123');
});
it('应该能够从原生BigInt创建如果支持', () => {
if (BigIntFactory.isNativeSupported()) {
const value = BigIntFactory.create(BigInt(456));
expect(value.toString()).toBe('456');
}
});
});
describe('IBigIntLike基本操作', () => {
let value1: IBigIntLike;
let value2: IBigIntLike;
beforeEach(() => {
value1 = BigIntFactory.create(5); // 101 in binary
value2 = BigIntFactory.create(3); // 011 in binary
});
it('should支持字符串转换', () => {
expect(value1.toString()).toBe('5');
expect(value2.toString()).toBe('3');
});
it('应该支持十六进制转换', () => {
const value = BigIntFactory.create(255);
expect(value.toString(16)).toBe('FF');
});
it('应该支持二进制转换', () => {
expect(value1.toString(2)).toBe('101');
expect(value2.toString(2)).toBe('11');
});
it('应该支持相等比较', () => {
const value1Copy = BigIntFactory.create(5);
expect(value1.equals(value1Copy)).toBe(true);
expect(value1.equals(value2)).toBe(false);
});
it('应该支持零值检查', () => {
const zero = BigIntFactory.zero();
expect(zero.isZero()).toBe(true);
expect(value1.isZero()).toBe(false);
});
it('应该支持克隆操作', () => {
const cloned = value1.clone();
expect(cloned.equals(value1)).toBe(true);
expect(cloned).not.toBe(value1); // 不同的对象引用
});
});
describe('位运算操作', () => {
let value1: IBigIntLike; // 5 = 101
let value2: IBigIntLike; // 3 = 011
beforeEach(() => {
value1 = BigIntFactory.create(5);
value2 = BigIntFactory.create(3);
});
it('AND运算应该正确', () => {
const result = value1.and(value2);
expect(result.toString()).toBe('1'); // 101 & 011 = 001
});
it('OR运算应该正确', () => {
const result = value1.or(value2);
expect(result.toString()).toBe('7'); // 101 | 011 = 111
});
it('XOR运算应该正确', () => {
const result = value1.xor(value2);
expect(result.toString()).toBe('6'); // 101 ^ 011 = 110
});
it('NOT运算应该正确8位限制', () => {
const value = BigIntFactory.create(5); // 00000101
const result = value.not(8);
expect(result.toString()).toBe('250'); // 11111010 = 250
});
it('左移位运算应该正确', () => {
const result = value1.shiftLeft(2);
expect(result.toString()).toBe('20'); // 101 << 2 = 10100 = 20
});
it('右移位运算应该正确', () => {
const result = value1.shiftRight(1);
expect(result.toString()).toBe('2'); // 101 >> 1 = 10 = 2
});
it('移位0位应该返回相同值', () => {
const result = value1.shiftLeft(0);
expect(result.equals(value1)).toBe(true);
});
it('右移超过位数应该返回0', () => {
const result = value1.shiftRight(10);
expect(result.isZero()).toBe(true);
});
});
describe('复杂位运算场景', () => {
it('应该正确处理大数值位运算', () => {
const large1 = BigIntFactory.create(0xFFFFFFFF); // 32位全1
const large2 = BigIntFactory.create(0x12345678);
const andResult = large1.and(large2);
expect(andResult.toString(16)).toBe('12345678');
const orResult = large1.or(large2);
expect(orResult.toString(16)).toBe('FFFFFFFF');
});
it('应该正确处理连续位运算', () => {
let result = BigIntFactory.create(1);
// 构建 111111 (6个1)
for (let i = 1; i < 6; i++) {
const shifted = BigIntFactory.one().shiftLeft(i);
result = result.or(shifted);
}
expect(result.toString()).toBe('63'); // 111111 = 63
expect(result.toString(2)).toBe('111111');
});
it('应该正确处理掩码操作', () => {
const value = BigIntFactory.create(0b10110101); // 181
const mask = BigIntFactory.create(0b00001111); // 15, 低4位掩码
const masked = value.and(mask);
expect(masked.toString()).toBe('5'); // 0101 = 5
});
});
describe('字符串解析功能', () => {
it('应该支持从二进制字符串创建', () => {
const value = BigIntFactory.fromBinaryString('10101');
expect(value.toString()).toBe('21');
});
it('应该支持从十六进制字符串创建', () => {
const value1 = BigIntFactory.fromHexString('0xFF');
const value2 = BigIntFactory.fromHexString('FF');
expect(value1.toString()).toBe('255');
expect(value2.toString()).toBe('255');
expect(value1.equals(value2)).toBe(true);
});
it('应该正确处理大的十六进制值', () => {
const value = BigIntFactory.fromHexString('0x12345678');
expect(value.toString()).toBe('305419896');
});
it('应该正确处理长二进制字符串', () => {
const binaryStr = '11111111111111111111111111111111'; // 32个1
const value = BigIntFactory.fromBinaryString(binaryStr);
expect(value.toString()).toBe('4294967295'); // 2^32 - 1
});
});
describe('边界情况和错误处理', () => {
it('应该正确处理零值的所有操作', () => {
const zero = BigIntFactory.zero();
const one = BigIntFactory.one();
expect(zero.and(one).isZero()).toBe(true);
expect(zero.or(one).equals(one)).toBe(true);
expect(zero.xor(one).equals(one)).toBe(true);
expect(zero.shiftLeft(5).isZero()).toBe(true);
expect(zero.shiftRight(5).isZero()).toBe(true);
});
it('应该正确处理1值的位运算', () => {
const one = BigIntFactory.one();
const zero = BigIntFactory.zero();
expect(one.and(zero).isZero()).toBe(true);
expect(one.or(zero).equals(one)).toBe(true);
expect(one.xor(zero).equals(one)).toBe(true);
});
it('应该处理不支持的字符串进制', () => {
const value = BigIntFactory.create(255);
expect(() => value.toString(8)).toThrow();
});
it('NOT运算应该正确处理不同的位数限制', () => {
const value = BigIntFactory.one(); // 1
const not8 = value.not(8);
expect(not8.toString()).toBe('254'); // 11111110 = 254
const not16 = value.not(16);
expect(not16.toString()).toBe('65534'); // 1111111111111110 = 65534
});
});
describe('性能和兼容性测试', () => {
it('两种实现应该产生相同的运算结果', () => {
// 测试各种运算在两种模式下的一致性
const testCases = [
{ a: 0, b: 0 },
{ a: 1, b: 1 },
{ a: 5, b: 3 },
{ a: 255, b: 128 },
{ a: 65535, b: 32768 }
];
testCases.forEach(({ a, b }) => {
const val1 = BigIntFactory.create(a);
const val2 = BigIntFactory.create(b);
// 基本运算
const and = val1.and(val2);
const or = val1.or(val2);
const xor = val1.xor(val2);
// 验证运算结果的一致性
expect(and.toString()).toBe((a & b).toString());
expect(or.toString()).toBe((a | b).toString());
expect(xor.toString()).toBe((a ^ b).toString());
});
});
it('大量运算应该保持高性能', () => {
const startTime = performance.now();
let result = BigIntFactory.zero();
for (let i = 0; i < 1000; i++) {
const value = BigIntFactory.create(i);
result = result.or(value.shiftLeft(i % 32));
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(1000); // 应该在1秒内完成
expect(result.isZero()).toBe(false);
});
it('应该支持ECS框架中常见的位掩码操作', () => {
// 模拟组件位掩码
const componentMasks: IBigIntLike[] = [];
// 创建64个组件的位掩码
for (let i = 0; i < 64; i++) {
componentMasks.push(BigIntFactory.one().shiftLeft(i));
}
// 组合多个组件掩码
let combinedMask = BigIntFactory.zero();
for (let i = 0; i < 10; i++) {
combinedMask = combinedMask.or(componentMasks[i * 2]);
}
// 检查是否包含特定组件
for (let i = 0; i < 10; i++) {
const hasComponent = !combinedMask.and(componentMasks[i * 2]).isZero();
expect(hasComponent).toBe(true);
const hasOtherComponent = !combinedMask.and(componentMasks[i * 2 + 1]).isZero();
expect(hasOtherComponent).toBe(false);
}
});
});
describe('与Core类集成测试', () => {
// 这里我们不直接导入Core来避免循环依赖而是测试工厂方法
it('BigIntFactory应该能够为Core提供环境信息', () => {
const envInfo = BigIntFactory.getEnvironmentInfo();
// 验证所有必需的字段都存在
expect(envInfo.supportsBigInt).toBeDefined();
expect(envInfo.environment).toBeDefined();
expect(envInfo.jsEngine).toBeDefined();
});
it('应该提供详细的环境检测信息', () => {
const envInfo = BigIntFactory.getEnvironmentInfo();
// 环境信息应该有意义
expect(envInfo.environment).not.toBe('Unknown');
});
});
});

View File

@@ -0,0 +1,553 @@
import { Bits } from '../../../src/ECS/Utils/Bits';
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
describe('Bits - 高性能位操作类测试', () => {
let bits: Bits;
beforeEach(() => {
bits = new Bits();
});
describe('基本构造和初始化', () => {
it('应该能够创建空的Bits对象', () => {
expect(bits).toBeDefined();
expect(bits.isEmpty()).toBe(true);
expect(bits.getValue().isZero()).toBe(true);
});
it('应该能够使用初始值创建Bits对象', () => {
const bitsWithValue = new Bits(BigIntFactory.create(5)); // 二进制: 101
expect(bitsWithValue.getValue().toString()).toBe('5');
expect(bitsWithValue.isEmpty()).toBe(false);
expect(bitsWithValue.get(0)).toBe(true); // 第0位
expect(bitsWithValue.get(1)).toBe(false); // 第1位
expect(bitsWithValue.get(2)).toBe(true); // 第2位
});
it('默认构造函数应该创建值为0的对象', () => {
const defaultBits = new Bits();
expect(defaultBits.getValue().isZero()).toBe(true);
});
});
describe('位设置和清除操作', () => {
it('应该能够设置指定位置的位', () => {
bits.set(0);
expect(bits.get(0)).toBe(true);
expect(bits.getValue().toString()).toBe('1');
bits.set(3);
expect(bits.get(3)).toBe(true);
expect(bits.getValue().toString()).toBe('9'); // 1001 in binary
});
it('应该能够清除指定位置的位', () => {
bits.set(0);
bits.set(1);
bits.set(2);
expect(bits.getValue().toString()).toBe('7'); // 111 in binary
bits.clear(1);
expect(bits.get(1)).toBe(false);
expect(bits.getValue().toString()).toBe('5'); // 101 in binary
});
it('重复设置同一位应该保持不变', () => {
bits.set(0);
const value1 = bits.getValue();
bits.set(0);
const value2 = bits.getValue();
expect(value1.equals(value2)).toBe(true);
});
it('清除未设置的位应该安全', () => {
bits.clear(5);
expect(bits.getValue().isZero()).toBe(true);
});
it('设置负索引应该抛出错误', () => {
expect(() => {
bits.set(-1);
}).toThrow('Bit index cannot be negative');
});
it('清除负索引应该抛出错误', () => {
expect(() => {
bits.clear(-1);
}).toThrow('Bit index cannot be negative');
});
});
describe('位获取操作', () => {
beforeEach(() => {
bits.set(0);
bits.set(2);
bits.set(4);
});
it('应该能够正确获取设置的位', () => {
expect(bits.get(0)).toBe(true);
expect(bits.get(2)).toBe(true);
expect(bits.get(4)).toBe(true);
});
it('应该能够正确获取未设置的位', () => {
expect(bits.get(1)).toBe(false);
expect(bits.get(3)).toBe(false);
expect(bits.get(5)).toBe(false);
});
it('获取负索引应该返回false', () => {
expect(bits.get(-1)).toBe(false);
expect(bits.get(-10)).toBe(false);
});
it('获取超大索引应该正确处理', () => {
expect(bits.get(1000)).toBe(false);
});
});
describe('位运算操作', () => {
let otherBits: Bits;
beforeEach(() => {
bits.set(0);
bits.set(2);
bits.set(4); // 10101 in binary = 21
otherBits = new Bits();
otherBits.set(1);
otherBits.set(2);
otherBits.set(3); // 1110 in binary = 14
});
it('AND运算应该正确', () => {
const result = bits.and(otherBits);
expect(result.getValue().toString()).toBe('4'); // 10101 & 01110 = 00100 = 4
expect(result.get(2)).toBe(true);
expect(result.get(0)).toBe(false);
expect(result.get(1)).toBe(false);
});
it('OR运算应该正确', () => {
const result = bits.or(otherBits);
expect(result.getValue().toString()).toBe('31'); // 10101 | 01110 = 11111 = 31
expect(result.get(0)).toBe(true);
expect(result.get(1)).toBe(true);
expect(result.get(2)).toBe(true);
expect(result.get(3)).toBe(true);
expect(result.get(4)).toBe(true);
});
it('XOR运算应该正确', () => {
const result = bits.xor(otherBits);
expect(result.getValue().toString()).toBe('27'); // 10101 ^ 01110 = 11011 = 27
expect(result.get(0)).toBe(true);
expect(result.get(1)).toBe(true);
expect(result.get(2)).toBe(false); // 相同位XOR为0
expect(result.get(3)).toBe(true);
expect(result.get(4)).toBe(true);
});
it('NOT运算应该正确', () => {
const simpleBits = new Bits(BigIntFactory.create(5)); // 101 in binary
const result = simpleBits.not(8); // 限制为8位
expect(result.getValue().toString()).toBe('250'); // ~00000101 = 11111010 = 250 (8位)
});
it('NOT运算默认64位应该正确', () => {
const simpleBits = new Bits(BigIntFactory.create(1));
const result = simpleBits.not();
const expected = BigIntFactory.one().shiftLeft(64).valueOf() - 2; // 64位全1减去最低位
expect(result.getValue().valueOf()).toBe(expected);
});
});
describe('包含性检查', () => {
let otherBits: Bits;
beforeEach(() => {
bits.set(0);
bits.set(2);
bits.set(4); // 10101
otherBits = new Bits();
});
it('containsAll应该正确检查包含所有位', () => {
otherBits.set(0);
otherBits.set(2); // 101
expect(bits.containsAll(otherBits)).toBe(true);
otherBits.set(1); // 111
expect(bits.containsAll(otherBits)).toBe(false);
});
it('intersects应该正确检查交集', () => {
otherBits.set(1);
otherBits.set(3); // 1010
expect(bits.intersects(otherBits)).toBe(false);
otherBits.set(0); // 1011
expect(bits.intersects(otherBits)).toBe(true);
});
it('excludes应该正确检查互斥', () => {
otherBits.set(1);
otherBits.set(3); // 1010
expect(bits.excludes(otherBits)).toBe(true);
otherBits.set(0); // 1011
expect(bits.excludes(otherBits)).toBe(false);
});
it('空Bits对象的包含性检查', () => {
const emptyBits = new Bits();
expect(bits.containsAll(emptyBits)).toBe(true);
expect(bits.intersects(emptyBits)).toBe(false);
expect(bits.excludes(emptyBits)).toBe(true);
});
});
describe('状态检查和计数', () => {
it('isEmpty应该正确检查空状态', () => {
expect(bits.isEmpty()).toBe(true);
bits.set(0);
expect(bits.isEmpty()).toBe(false);
bits.clear(0);
expect(bits.isEmpty()).toBe(true);
});
it('cardinality应该正确计算设置的位数量', () => {
expect(bits.cardinality()).toBe(0);
bits.set(0);
expect(bits.cardinality()).toBe(1);
bits.set(2);
bits.set(4);
expect(bits.cardinality()).toBe(3);
bits.clear(2);
expect(bits.cardinality()).toBe(2);
});
it('大数值的cardinality应该正确', () => {
// 设置很多位
for (let i = 0; i < 100; i += 2) {
bits.set(i);
}
expect(bits.cardinality()).toBe(50);
});
});
describe('清空和重置操作', () => {
beforeEach(() => {
bits.set(0);
bits.set(1);
bits.set(2);
});
it('clearAll应该清空所有位', () => {
expect(bits.isEmpty()).toBe(false);
bits.clearAll();
expect(bits.isEmpty()).toBe(true);
expect(bits.getValue().isZero()).toBe(true);
});
it('clearAll后应该能重新设置位', () => {
bits.clearAll();
bits.set(5);
expect(bits.get(5)).toBe(true);
expect(bits.cardinality()).toBe(1);
});
});
describe('复制和克隆操作', () => {
beforeEach(() => {
bits.set(1);
bits.set(3);
bits.set(5);
});
it('copyFrom应该正确复制另一个Bits对象', () => {
const newBits = new Bits();
newBits.copyFrom(bits);
expect(newBits.getValue().equals(bits.getValue())).toBe(true);
expect(newBits.equals(bits)).toBe(true);
});
it('clone应该创建相同的副本', () => {
const clonedBits = bits.clone();
expect(clonedBits.getValue().equals(bits.getValue())).toBe(true);
expect(clonedBits.equals(bits)).toBe(true);
expect(clonedBits).not.toBe(bits); // 应该是不同的对象
});
it('修改克隆对象不应该影响原对象', () => {
const clonedBits = bits.clone();
clonedBits.set(7);
expect(bits.get(7)).toBe(false);
expect(clonedBits.get(7)).toBe(true);
});
});
describe('值操作', () => {
it('getValue和setValue应该正确工作', () => {
bits.setValue(42);
expect(bits.getValue().toString()).toBe('42');
});
it('setValue应该正确反映在位操作中', () => {
bits.setValue(5); // 101 in binary
expect(bits.get(0)).toBe(true);
expect(bits.get(1)).toBe(false);
expect(bits.get(2)).toBe(true);
});
it('setValue为0应该清空所有位', () => {
bits.set(1);
bits.set(2);
bits.setValue(0);
expect(bits.isEmpty()).toBe(true);
});
});
describe('字符串表示和解析', () => {
beforeEach(() => {
bits.set(0);
bits.set(2);
bits.set(4); // 10101 = 21
});
it('toString应该返回可读的位表示', () => {
const str = bits.toString();
expect(str).toBe('Bits[0, 2, 4]');
});
it('空Bits的toString应该正确', () => {
const emptyBits = new Bits();
expect(emptyBits.toString()).toBe('Bits[]');
});
it('toBinaryString应该返回正确的二进制表示', () => {
const binaryStr = bits.toBinaryString(8);
expect(binaryStr).toBe('00010101');
});
it('toBinaryString应该正确处理空格分隔', () => {
const binaryStr = bits.toBinaryString(16);
expect(binaryStr).toBe('00000000 00010101');
});
it('toHexString应该返回正确的十六进制表示', () => {
const hexStr = bits.toHexString();
expect(hexStr).toBe('0x15'); // 21 in hex
});
it('fromBinaryString应该正确解析', () => {
const parsedBits = Bits.fromBinaryString('10101');
expect(parsedBits.getValue().toString()).toBe('21');
expect(parsedBits.equals(bits)).toBe(true);
});
it('fromBinaryString应该处理带空格的字符串', () => {
const parsedBits = Bits.fromBinaryString('0001 0101');
expect(parsedBits.getValue().toString()).toBe('21');
});
it('fromHexString应该正确解析', () => {
const parsedBits = Bits.fromHexString('0x15');
expect(parsedBits.getValue().toString()).toBe('21');
expect(parsedBits.equals(bits)).toBe(true);
});
it('fromHexString应该处理不带0x前缀的字符串', () => {
const parsedBits = Bits.fromHexString('15');
expect(parsedBits.getValue().toString()).toBe('21');
});
});
describe('比较操作', () => {
let otherBits: Bits;
beforeEach(() => {
bits.set(0);
bits.set(2);
otherBits = new Bits();
});
it('equals应该正确比较相等的Bits', () => {
otherBits.set(0);
otherBits.set(2);
expect(bits.equals(otherBits)).toBe(true);
});
it('equals应该正确比较不相等的Bits', () => {
otherBits.set(0);
otherBits.set(1);
expect(bits.equals(otherBits)).toBe(false);
});
it('空Bits对象应该相等', () => {
const emptyBits1 = new Bits();
const emptyBits2 = new Bits();
expect(emptyBits1.equals(emptyBits2)).toBe(true);
});
});
describe('索引查找操作', () => {
it('getHighestBitIndex应该返回最高设置位的索引', () => {
bits.set(0);
bits.set(5);
bits.set(10);
expect(bits.getHighestBitIndex()).toBe(10);
});
it('getLowestBitIndex应该返回最低设置位的索引', () => {
bits.set(3);
bits.set(7);
bits.set(1);
expect(bits.getLowestBitIndex()).toBe(1);
});
it('空Bits的索引查找应该返回-1', () => {
expect(bits.getHighestBitIndex()).toBe(-1);
expect(bits.getLowestBitIndex()).toBe(-1);
});
it('只有一个位设置时索引查找应该正确', () => {
bits.set(5);
expect(bits.getHighestBitIndex()).toBe(5);
expect(bits.getLowestBitIndex()).toBe(5);
});
});
describe('大数值处理', () => {
it('应该能够处理超过64位的数值', () => {
bits.set(100);
expect(bits.get(100)).toBe(true);
expect(bits.cardinality()).toBe(1);
});
it('应该能够处理非常大的位索引', () => {
bits.set(1000);
bits.set(2000);
expect(bits.get(1000)).toBe(true);
expect(bits.get(2000)).toBe(true);
expect(bits.cardinality()).toBe(2);
});
it('大数值的位运算应该正确', () => {
const largeBits1 = new Bits();
const largeBits2 = new Bits();
largeBits1.set(100);
largeBits1.set(200);
largeBits2.set(100);
largeBits2.set(150);
const result = largeBits1.and(largeBits2);
expect(result.get(100)).toBe(true);
expect(result.get(150)).toBe(false);
expect(result.get(200)).toBe(false);
});
});
describe('性能测试', () => {
it('大量位设置操作应该高效', () => {
const startTime = performance.now();
for (let i = 0; i < 10000; i++) {
bits.set(i);
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(1000); // 应该在1秒内完成
expect(bits.cardinality()).toBe(10000);
});
it('大量位查询操作应该高效', () => {
// 先设置一些位
for (let i = 0; i < 1000; i += 2) {
bits.set(i);
}
const startTime = performance.now();
let trueCount = 0;
for (let i = 0; i < 10000; i++) {
if (bits.get(i)) {
trueCount++;
}
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
expect(trueCount).toBe(500); // 500个偶数位
});
it('位运算操作应该高效', () => {
const otherBits = new Bits();
// 设置一些位
for (let i = 0; i < 1000; i++) {
bits.set(i * 2);
otherBits.set(i * 2 + 1);
}
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
const result = bits.or(otherBits);
}
const endTime = performance.now();
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
});
});
describe('边界情况和错误处理', () => {
it('应该处理0值的各种操作', () => {
const zeroBits = new Bits(BigIntFactory.zero());
expect(zeroBits.isEmpty()).toBe(true);
expect(zeroBits.cardinality()).toBe(0);
expect(zeroBits.getHighestBitIndex()).toBe(-1);
expect(zeroBits.getLowestBitIndex()).toBe(-1);
});
it('应该处理最大BigInt值', () => {
const maxBits = new Bits(BigIntFactory.create(Number.MAX_SAFE_INTEGER));
expect(maxBits.isEmpty()).toBe(false);
expect(maxBits.cardinality()).toBeGreaterThan(0);
});
it('位操作的结果应该是新对象', () => {
bits.set(0);
const otherBits = new Bits();
otherBits.set(1);
const result = bits.or(otherBits);
expect(result).not.toBe(bits);
expect(result).not.toBe(otherBits);
});
it('连续的设置和清除操作应该正确', () => {
for (let i = 0; i < 100; i++) {
bits.set(i);
expect(bits.get(i)).toBe(true);
bits.clear(i);
expect(bits.get(i)).toBe(false);
}
expect(bits.isEmpty()).toBe(true);
});
});
});

View File

@@ -0,0 +1,414 @@
/**
* IdentifierPool 世代式ID池测试
*
* 测试实体ID的分配、回收、验证和世代版本控制功能
*/
import { IdentifierPool } from '../../../src/ECS/Utils/IdentifierPool';
import { TestUtils } from '../../setup';
describe('IdentifierPool 世代式ID池测试', () => {
let pool: IdentifierPool;
beforeEach(() => {
pool = new IdentifierPool();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
// 测试基本功能
describe('基本功能测试', () => {
test('应该能创建IdentifierPool实例', () => {
expect(pool).toBeDefined();
expect(pool).toBeInstanceOf(IdentifierPool);
});
test('应该能分配连续的ID', () => {
const id1 = pool.checkOut();
const id2 = pool.checkOut();
const id3 = pool.checkOut();
expect(id1).toBe(65536); // 世代1索引0
expect(id2).toBe(65537); // 世代1索引1
expect(id3).toBe(65538); // 世代1索引2
});
test('应该能验证有效的ID', () => {
const id = pool.checkOut();
expect(pool.isValid(id)).toBe(true);
});
test('应该能获取统计信息', () => {
const id1 = pool.checkOut();
const id2 = pool.checkOut();
const stats = pool.getStats();
expect(stats.totalAllocated).toBe(2);
expect(stats.currentActive).toBe(2);
expect(stats.currentlyFree).toBe(0);
expect(stats.pendingRecycle).toBe(0);
expect(stats.maxPossibleEntities).toBe(65536); // 2^16
expect(stats.averageGeneration).toBe(1);
expect(stats.memoryUsage).toBeGreaterThan(0);
});
});
// 测试回收功能
describe('ID回收功能测试', () => {
test('应该能回收有效的ID', () => {
const id = pool.checkOut();
const result = pool.checkIn(id);
expect(result).toBe(true);
const stats = pool.getStats();
expect(stats.pendingRecycle).toBe(1);
expect(stats.currentActive).toBe(0);
});
test('应该拒绝回收无效的ID', () => {
const invalidId = 999999;
const result = pool.checkIn(invalidId);
expect(result).toBe(false);
const stats = pool.getStats();
expect(stats.pendingRecycle).toBe(0);
});
test('应该拒绝重复回收同一个ID', () => {
const id = pool.checkOut();
const firstResult = pool.checkIn(id);
const secondResult = pool.checkIn(id);
expect(firstResult).toBe(true);
expect(secondResult).toBe(false);
});
});
// 测试延迟回收
describe('延迟回收机制测试', () => {
test('应该支持延迟回收', () => {
const pool = new IdentifierPool(100); // 100ms延迟
const id = pool.checkOut();
pool.checkIn(id);
// 立即检查ID应该还在延迟队列中
let stats = pool.getStats();
expect(stats.pendingRecycle).toBe(1);
expect(stats.currentlyFree).toBe(0);
// 模拟时间前进150ms
jest.advanceTimersByTime(150);
// 触发延迟回收处理通过分配新ID
pool.checkOut();
// 现在ID应该被真正回收了
stats = pool.getStats();
expect(stats.pendingRecycle).toBe(0);
expect(stats.currentlyFree).toBe(0); // 因为被重新分配了
});
test('延迟时间内ID应该仍然有效', () => {
const pool = new IdentifierPool(100);
const id = pool.checkOut();
pool.checkIn(id);
// 在延迟时间内ID应该仍然有效
expect(pool.isValid(id)).toBe(true);
// 模拟时间前进150ms并触发处理
jest.advanceTimersByTime(150);
pool.checkOut(); // 触发延迟回收处理
// 现在ID应该无效了世代已递增
expect(pool.isValid(id)).toBe(false);
});
test('应该支持强制延迟回收处理', () => {
const id = pool.checkOut();
pool.checkIn(id);
// 在延迟时间内强制处理
pool.forceProcessDelayedRecycle();
// ID应该立即变为无效
expect(pool.isValid(id)).toBe(false);
const stats = pool.getStats();
expect(stats.pendingRecycle).toBe(0);
expect(stats.currentlyFree).toBe(1);
});
});
// 测试世代版本控制
describe('世代版本控制测试', () => {
test('回收后的ID应该增加世代版本', () => {
const pool = new IdentifierPool(0); // 无延迟,立即回收
const originalId = pool.checkOut();
pool.checkIn(originalId);
// 分配新ID触发回收处理
const newId = pool.checkOut();
// 原ID应该无效
expect(pool.isValid(originalId)).toBe(false);
// 新ID应该有不同的世代版本
expect(newId).not.toBe(originalId);
expect(newId).toBe(131072); // 世代2索引0
});
test('应该能重用回收的索引', () => {
const pool = new IdentifierPool(0);
const id1 = pool.checkOut(); // 索引0
const id2 = pool.checkOut(); // 索引1
pool.checkIn(id1);
const id3 = pool.checkOut(); // 应该重用索引0但世代递增
expect(id3 & 0xFFFF).toBe(0); // 索引部分应该是0
expect(id3 >> 16).toBe(2); // 世代应该是2
});
test('世代版本溢出应该重置为1', () => {
const pool = new IdentifierPool(0);
// 手动设置一个即将溢出的世代
const id = pool.checkOut();
// 通过反射访问私有成员来模拟溢出情况
const generations = (pool as any)._generations;
generations.set(0, 65535); // 设置为最大值
pool.checkIn(id);
const newId = pool.checkOut();
// 世代应该重置为1而不是0
expect(newId >> 16).toBe(1);
});
});
// 测试错误处理
describe('错误处理测试', () => {
test('超过最大索引数应该抛出错误', () => {
// 创建一个模拟的池,直接设置到达到限制
const pool = new IdentifierPool();
// 通过反射设置到达到限制65536会触发错误
(pool as any)._nextAvailableIndex = 65536;
expect(() => {
pool.checkOut();
}).toThrow('实体索引已达到框架设计限制');
});
test('应该能处理边界值', () => {
const pool = new IdentifierPool();
const id = pool.checkOut();
expect(id).toBe(65536); // 世代1索引0
// 回收并重新分配
pool.checkIn(id);
jest.advanceTimersByTime(200);
const newId = pool.checkOut();
expect(newId).toBe(131072); // 世代2索引0
});
});
// 测试动态扩展
describe('动态内存扩展测试', () => {
test('应该能动态扩展内存', () => {
const pool = new IdentifierPool(0, 10); // 小的扩展块用于测试
// 分配超过初始块大小的ID
const ids: number[] = [];
for (let i = 0; i < 25; i++) {
ids.push(pool.checkOut());
}
expect(ids.length).toBe(25);
// 验证所有ID都是唯一的
const uniqueIds = new Set(ids);
expect(uniqueIds.size).toBe(25);
// 检查内存扩展统计
const stats = pool.getStats();
expect(stats.memoryExpansions).toBeGreaterThan(1);
expect(stats.generationStorageSize).toBeGreaterThanOrEqual(25);
});
test('内存扩展应该按块进行', () => {
const blockSize = 5;
const pool = new IdentifierPool(0, blockSize);
// 分配第一个块
for (let i = 0; i < blockSize; i++) {
pool.checkOut();
}
let stats = pool.getStats();
const initialExpansions = stats.memoryExpansions;
// 分配一个会触发新块的ID
pool.checkOut();
stats = pool.getStats();
expect(stats.memoryExpansions).toBe(initialExpansions + 1);
});
});
// 测试性能和内存
describe('性能和内存测试', () => {
test('应该能处理大量ID分配', () => {
const count = 10000; // 增加测试规模
const ids: number[] = [];
const startTime = performance.now();
for (let i = 0; i < count; i++) {
ids.push(pool.checkOut());
}
const endTime = performance.now();
const duration = endTime - startTime;
expect(ids.length).toBe(count);
expect(duration).toBeLessThan(1000); // 10k个ID应该在1秒内完成
// 验证所有ID都是唯一的
const uniqueIds = new Set(ids);
expect(uniqueIds.size).toBe(count);
});
test('应该能处理大量回收操作', () => {
const count = 5000; // 增加测试规模
const ids: number[] = [];
// 分配ID
for (let i = 0; i < count; i++) {
ids.push(pool.checkOut());
}
// 回收ID
const startTime = performance.now();
for (const id of ids) {
pool.checkIn(id);
}
const endTime = performance.now();
const duration = endTime - startTime;
expect(duration).toBeLessThan(500); // 5k个回收应该在500ms内完成
const stats = pool.getStats();
expect(stats.pendingRecycle).toBe(count);
expect(stats.currentActive).toBe(0);
});
test('内存使用应该是合理的', () => {
const stats = pool.getStats();
const initialMemory = stats.memoryUsage;
// 分配大量ID
for (let i = 0; i < 5000; i++) {
pool.checkOut();
}
const newStats = pool.getStats();
const memoryIncrease = newStats.memoryUsage - initialMemory;
// 内存增长应该是合理的(动态分配应该更高效)
expect(memoryIncrease).toBeLessThan(5000 * 50); // 每个ID少于50字节
});
});
// 测试并发安全性(模拟)
describe('并发安全性测试', () => {
test('应该能处理并发分配', async () => {
const promises: Promise<number>[] = [];
// 模拟并发分配
for (let i = 0; i < 1000; i++) {
promises.push(Promise.resolve(pool.checkOut()));
}
const ids = await Promise.all(promises);
// 所有ID应该是唯一的
const uniqueIds = new Set(ids);
expect(uniqueIds.size).toBe(1000);
});
test('应该能处理并发回收', async () => {
const ids: number[] = [];
// 先分配一些ID
for (let i = 0; i < 500; i++) {
ids.push(pool.checkOut());
}
// 模拟并发回收
const promises = ids.map(id => Promise.resolve(pool.checkIn(id)));
const results = await Promise.all(promises);
// 所有回收操作都应该成功
expect(results.every(result => result === true)).toBe(true);
});
});
// 测试统计信息
describe('统计信息测试', () => {
test('统计信息应该准确反映池状态', () => {
// 分配一些ID
const ids = [pool.checkOut(), pool.checkOut(), pool.checkOut()];
let stats = pool.getStats();
expect(stats.totalAllocated).toBe(3);
expect(stats.currentActive).toBe(3);
expect(stats.currentlyFree).toBe(0);
expect(stats.pendingRecycle).toBe(0);
// 回收一个ID
pool.checkIn(ids[0]);
stats = pool.getStats();
expect(stats.totalRecycled).toBe(1);
expect(stats.currentActive).toBe(2);
expect(stats.pendingRecycle).toBe(1);
// 强制处理延迟回收
pool.forceProcessDelayedRecycle();
stats = pool.getStats();
expect(stats.pendingRecycle).toBe(0);
expect(stats.currentlyFree).toBe(1);
});
test('应该正确计算平均世代版本', () => {
const pool = new IdentifierPool(0); // 无延迟
// 分配、回收、再分配来增加世代
const id1 = pool.checkOut();
pool.checkIn(id1);
const id2 = pool.checkOut(); // 这会触发世代递增
const stats = pool.getStats();
expect(stats.averageGeneration).toBeGreaterThan(1);
});
});
});

View File

@@ -0,0 +1,288 @@
/**
* Matcher完整测试套件
* 测试新的Matcher条件构建功能和QuerySystem集成
*/
import { Scene } from '../../../src/ECS/Scene';
import { Entity } from '../../../src/ECS/Entity';
import { Component } from '../../../src/ECS/Component';
import { Matcher } from '../../../src/ECS/Utils/Matcher';
// 测试组件
class Position extends Component {
public x: number = 0;
public y: number = 0;
constructor(...args: unknown[]) {
super();
const [x = 0, y = 0] = args as [number?, number?];
this.x = x;
this.y = y;
}
}
class Velocity extends Component {
public vx: number = 0;
public vy: number = 0;
constructor(...args: unknown[]) {
super();
const [vx = 0, vy = 0] = args as [number?, number?];
this.vx = vx;
this.vy = vy;
}
}
class Health extends Component {
public hp: number = 100;
constructor(...args: unknown[]) {
super();
const [hp = 100] = args as [number?];
this.hp = hp;
}
}
class Dead extends Component {}
describe('Matcher测试套件', () => {
let scene: Scene;
let entities: Entity[];
beforeEach(() => {
scene = new Scene();
scene.begin();
// 创建测试实体
entities = [];
// 实体1: 移动的活体
const entity1 = scene.createEntity('MovingAlive');
entity1.addComponent(new Position(10, 20));
entity1.addComponent(new Velocity(1, 0));
entity1.addComponent(new Health(100));
entities.push(entity1);
// 实体2: 静止的活体
const entity2 = scene.createEntity('StillAlive');
entity2.addComponent(new Position(30, 40));
entity2.addComponent(new Health(50));
entities.push(entity2);
// 实体3: 移动的死体
const entity3 = scene.createEntity('MovingDead');
entity3.addComponent(new Position(50, 60));
entity3.addComponent(new Velocity(0, 1));
entity3.addComponent(new Dead());
entities.push(entity3);
// 实体4: 静止的死体
const entity4 = scene.createEntity('StillDead');
entity4.addComponent(new Position(70, 80));
entity4.addComponent(new Dead());
entities.push(entity4);
});
afterEach(() => {
scene.end();
});
describe('Matcher条件构建测试', () => {
test('Matcher.all()应该创建正确的查询条件', () => {
const matcher = Matcher.all(Position, Health);
const condition = matcher.getCondition();
expect(condition.all).toContain(Position);
expect(condition.all).toContain(Health);
expect(condition.all.length).toBe(2);
});
test('Matcher.any()应该创建正确的查询条件', () => {
const matcher = Matcher.any(Health, Dead);
const condition = matcher.getCondition();
expect(condition.any).toContain(Health);
expect(condition.any).toContain(Dead);
expect(condition.any.length).toBe(2);
});
test('Matcher.none()应该创建正确的查询条件', () => {
const matcher = Matcher.none(Dead);
const condition = matcher.getCondition();
expect(condition.none).toContain(Dead);
expect(condition.none.length).toBe(1);
});
test('链式调用应该正确工作', () => {
const matcher = Matcher.all(Position)
.any(Health, Velocity)
.none(Dead);
const condition = matcher.getCondition();
expect(condition.all).toContain(Position);
expect(condition.any).toContain(Health);
expect(condition.any).toContain(Velocity);
expect(condition.none).toContain(Dead);
});
test('byComponent()应该创建单组件查询条件', () => {
const matcher = Matcher.byComponent(Position);
const condition = matcher.getCondition();
expect(condition.component).toBe(Position);
});
test('byTag()应该创建标签查询条件', () => {
const matcher = Matcher.byTag(123);
const condition = matcher.getCondition();
expect(condition.tag).toBe(123);
});
test('byName()应该创建名称查询条件', () => {
const matcher = Matcher.byName('TestEntity');
const condition = matcher.getCondition();
expect(condition.name).toBe('TestEntity');
});
});
describe('QuerySystem集成测试', () => {
test('使用QuerySystem的queryAll()查询所有匹配实体', () => {
const result = scene.querySystem.queryAll(Position, Health);
expect(result.entities.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']);
});
test('使用QuerySystem的queryAny()查询任一匹配实体', () => {
const result = scene.querySystem.queryAny(Health, Dead);
expect(result.entities.length).toBe(4); // 所有实体都有Health或Dead
});
test('使用QuerySystem的queryNone()查询排除实体', () => {
const result = scene.querySystem.queryNone(Dead);
const aliveEntities = result.entities.filter(e => e.hasComponent(Position));
expect(aliveEntities.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']);
});
test('QuerySystem查询性能统计', () => {
scene.querySystem.queryAll(Position, Velocity);
const stats = scene.querySystem.getStats();
expect(stats.queryStats.totalQueries).toBeGreaterThan(0);
expect(stats.queryStats.cacheHits).toBeGreaterThanOrEqual(0);
});
});
describe('实际使用场景测试', () => {
test('游戏系统中的移动实体查询', () => {
// 查询所有可移动的实体(有位置和速度的)
const movableEntities = scene.querySystem.queryAll(Position, Velocity);
expect(movableEntities.entities.length).toBe(2); // MovingAlive, MovingDead
movableEntities.entities.forEach(entity => {
const pos = entity.getComponent(Position)!;
const vel = entity.getComponent(Velocity)!;
expect(pos).toBeDefined();
expect(vel).toBeDefined();
});
});
test('游戏系统中的活体实体查询', () => {
// 查询所有活体实体(有血量,没有死亡标记的)
const aliveEntitiesAll = scene.querySystem.queryAll(Health);
const deadEntitiesAll = scene.querySystem.queryAll(Dead);
expect(aliveEntitiesAll.entities.length).toBe(2); // MovingAlive, StillAlive
expect(deadEntitiesAll.entities.length).toBe(2); // MovingDead, StillDead
});
test('复杂查询:查找活着的移动实体', () => {
// 首先获取所有有位置和速度的实体
const movableEntities = scene.querySystem.queryAll(Position, Velocity);
// 然后过滤出活着的(有血量的)
const aliveMovableEntities = movableEntities.entities.filter(entity =>
entity.hasComponent(Health)
);
expect(aliveMovableEntities.length).toBe(1); // 只有MovingAlive
expect(aliveMovableEntities[0].name).toBe('MovingAlive');
});
test('复合查询条件应用', () => {
// 使用Matcher建立复杂条件然后用QuerySystem执行
const matcher = Matcher.all(Position).any(Health, Dead);
const condition = matcher.getCondition();
// 这里演示如何用条件实际执行需要QuerySystem支持复合条件
expect(condition.all).toContain(Position);
expect(condition.any).toContain(Health);
expect(condition.any).toContain(Dead);
});
});
describe('性能测试', () => {
test('大量简单查询的性能', () => {
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
scene.querySystem.queryAll(Position);
}
const executionTime = performance.now() - startTime;
expect(executionTime).toBeLessThan(100); // 应该在100ms内完成
});
test('复杂查询的性能', () => {
const startTime = performance.now();
for (let i = 0; i < 100; i++) {
scene.querySystem.queryAll(Position, Health);
scene.querySystem.queryAny(Health, Dead);
scene.querySystem.queryNone(Dead);
}
const executionTime = performance.now() - startTime;
expect(executionTime).toBeLessThan(50);
});
test('不存在组件的查询性能', () => {
class NonExistentComponent extends Component {
constructor(...args: unknown[]) {
super();
}
}
const result = scene.querySystem.queryAll(NonExistentComponent);
expect(result.entities.length).toBe(0);
});
});
describe('边界情况测试', () => {
test('空查询应该返回所有实体', () => {
const result = scene.querySystem.queryAll();
expect(result.entities.length).toBe(entities.length);
});
test('查询不存在的组件应该返回空结果', () => {
class NonExistentComponent extends Component {
constructor(...args: unknown[]) {
super();
}
}
const result = scene.querySystem.queryAll(NonExistentComponent);
expect(result.entities.length).toBe(0);
});
test('Matcher条件构建的边界情况', () => {
const emptyMatcher = Matcher.complex();
const condition = emptyMatcher.getCondition();
expect(condition.all.length).toBe(0);
expect(condition.any.length).toBe(0);
expect(condition.none.length).toBe(0);
});
});
});