对ecs目录进行更多的ci测试

This commit is contained in:
YHH
2025-07-28 17:38:18 +08:00
parent dd8f3714ed
commit 608f5030b2
3 changed files with 1275 additions and 0 deletions

277
tests/ECS/Component.test.ts Normal file
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,675 @@
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 {
constructor(public value: number = 0) {
super();
}
}
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class VelocityComponent extends Component {
constructor(public vx: number = 0, public vy: number = 0) {
super();
}
}
// 测试系统
class 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,323 @@
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 { Matcher } from '../../../src/ECS/Utils/Matcher';
import { Time } from '../../../src/Utils/Time';
// 测试组件
class TestComponent extends Component {
constructor(public value: number = 0) {
super();
}
}
class AnotherComponent extends Component {
constructor(public name: string = 'test') {
super();
}
}
// 具体的被动系统实现
class ConcretePassiveSystem extends PassiveSystem {
public processCallCount = 0;
public changeCallCount = 0;
constructor() {
super(Matcher.empty().all(TestComponent));
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
// 被动系统的process方法会被调用但不做任何处理
super.process(entities);
}
public override onChanged(entity: Entity): void {
this.changeCallCount++;
super.onChanged(entity);
}
}
// 具体的间隔系统实现
class ConcreteIntervalSystem extends IntervalSystem {
public processCallCount = 0;
public lastDelta = 0;
constructor(interval: number) {
super(Matcher.empty().all(TestComponent), interval);
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
this.lastDelta = this.getIntervalDelta();
}
}
// 具体的处理系统实现
class ConcreteProcessingSystem extends ProcessingSystem {
public processSystemCallCount = 0;
public processCallCount = 0;
public changeCallCount = 0;
constructor() {
super(Matcher.empty().all(TestComponent));
}
public processSystem(): void {
this.processSystemCallCount++;
}
protected override process(entities: Entity[]): void {
this.processCallCount++;
super.process(entities);
}
public override onChanged(entity: Entity): void {
this.changeCallCount++;
super.onChanged(entity);
}
}
describe('System Types - 系统类型测试', () => {
let entity: Entity;
beforeEach(() => {
entity = new Entity('TestEntity', 1);
// 重置时间系统
Time.update(0.016);
});
describe('PassiveSystem - 被动系统', () => {
let passiveSystem: ConcretePassiveSystem;
beforeEach(() => {
passiveSystem = new ConcretePassiveSystem();
});
test('应该能够创建被动系统', () => {
expect(passiveSystem).toBeInstanceOf(PassiveSystem);
expect(passiveSystem).toBeInstanceOf(ConcretePassiveSystem);
});
test('onChanged方法不应该做任何操作', () => {
const initialChangeCount = passiveSystem.changeCallCount;
passiveSystem.onChanged(entity);
// 计数会增加但实际上基类的onChanged不做任何操作
expect(passiveSystem.changeCallCount).toBe(initialChangeCount + 1);
});
test('process方法不应该做任何处理', () => {
const entities = [entity];
const initialProcessCount = passiveSystem.processCallCount;
passiveSystem.update();
// 虽然process被调用了但被动系统不做任何实际处理
expect(passiveSystem.processCallCount).toBe(initialProcessCount + 1);
});
test('应该能够正常添加和移除实体', () => {
entity.addComponent(new TestComponent(100));
passiveSystem.add(entity);
expect(passiveSystem.entities.length).toBe(1);
passiveSystem.remove(entity);
expect(passiveSystem.entities.length).toBe(0);
});
test('实体变化时应该调用onChanged', () => {
entity.addComponent(new TestComponent(100));
passiveSystem.add(entity);
const initialCount = passiveSystem.changeCallCount;
passiveSystem.onChanged(entity);
expect(passiveSystem.changeCallCount).toBe(initialCount + 1);
});
});
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('onChanged方法不应该做任何操作', () => {
const initialChangeCount = processingSystem.changeCallCount;
processingSystem.onChanged(entity);
expect(processingSystem.changeCallCount).toBe(initialChangeCount + 1);
});
test('每次更新都应该调用processSystem', () => {
const initialCount = processingSystem.processSystemCallCount;
processingSystem.update();
processingSystem.update();
processingSystem.update();
expect(processingSystem.processSystemCallCount).toBe(initialCount + 3);
});
test('应该能够处理多个实体', () => {
const entity1 = new Entity('Entity1', 1);
const entity2 = new Entity('Entity2', 2);
entity1.addComponent(new TestComponent(100));
entity2.addComponent(new TestComponent(200));
processingSystem.add(entity1);
processingSystem.add(entity2);
expect(processingSystem.entities.length).toBe(2);
const initialCount = processingSystem.processSystemCallCount;
processingSystem.update();
// processSystem应该被调用不管有多少实体
expect(processingSystem.processSystemCallCount).toBe(initialCount + 1);
});
});
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();
});
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(passive.matcher.isInterestedEntity(matchingEntity)).toBe(true);
expect(interval.matcher.isInterestedEntity(matchingEntity)).toBe(true);
expect(processing.matcher.isInterestedEntity(matchingEntity)).toBe(true);
expect(passive.matcher.isInterestedEntity(nonMatchingEntity)).toBe(false);
expect(interval.matcher.isInterestedEntity(nonMatchingEntity)).toBe(false);
expect(processing.matcher.isInterestedEntity(nonMatchingEntity)).toBe(false);
});
});
});