Files
esengine/packages/core/tests/ECS/WorldManager.test.ts
YHH eac660b1a0 refactor(core): 统一参数命名 - worldId/sceneId 改为 worldName/sceneName (#225)
* refactor(core): 统一参数命名 - worldId/sceneId 改为 worldName/sceneName

* test(core): 更新测试用例以匹配新的错误消息

* refactor(core): 提高代码覆盖率 - 添加参数验证和测试
2025-11-15 00:20:17 +08:00

716 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { WorldManager, IWorldManagerConfig } from '../../src/ECS/WorldManager';
import { IWorldConfig, World } from '../../src/ECS/World';
import { Component } from '../../src/ECS/Component';
// 测试用组件
class TestComponent extends Component {
public value: number = 0;
constructor(value: number = 0) {
super();
this.value = value;
}
}
// 测试用全局系统
class TestGlobalSystem {
public readonly name = 'TestGlobalSystem';
public updateCount: number = 0;
public initialize(): void {
// 初始化
}
public update(): void {
this.updateCount++;
}
public reset(): void {
this.updateCount = 0;
}
public destroy(): void {
// 销毁
}
}
describe('WorldManager', () => {
let worldManager: WorldManager;
beforeEach(() => {
// WorldManager不再是单例直接创建新实例
worldManager = new WorldManager();
});
afterEach(() => {
// 清理所有World
if (worldManager) {
const worldIds = worldManager.getWorldIds();
worldIds.forEach((id) => {
worldManager.removeWorld(id);
});
// 清理定时器
worldManager.destroy();
}
});
describe('实例化', () => {
test('可以创建多个独立的WorldManager实例', () => {
const manager1 = new WorldManager();
const manager2 = new WorldManager();
expect(manager1).not.toBe(manager2);
manager1.createWorld('world1');
expect(manager2.getWorld('world1')).toBeNull();
// 清理
manager1.destroy();
manager2.destroy();
});
test('使用配置创建实例应该正确', () => {
const config: IWorldManagerConfig = {
maxWorlds: 10,
autoCleanup: true,
debug: false
};
const instance = new WorldManager(config);
expect(instance).toBeDefined();
expect(instance.worldCount).toBe(0);
// 清理
instance.destroy();
});
});
describe('World管理', () => {
test('创建World应该成功', () => {
const world = worldManager.createWorld('test-world');
expect(world).toBeDefined();
expect(world.name).toBe('test-world');
expect(worldManager.getWorld('test-world')).toBeDefined();
expect(worldManager.getWorldIds()).toContain('test-world');
});
test('创建World时传入配置应该正确', () => {
const worldConfig: IWorldConfig = {
name: 'ConfiguredWorld',
debug: true,
maxScenes: 5,
autoCleanup: false
};
const world = worldManager.createWorld('configured-world', worldConfig);
expect(world.name).toBe('configured-world');
});
test('空的World name应该抛出错误', () => {
expect(() => {
worldManager.createWorld('');
}).toThrow('World name不能为空');
expect(() => {
worldManager.createWorld(' ');
}).toThrow('World name不能为空');
});
test('重复的World ID应该抛出错误', () => {
worldManager.createWorld('duplicate-world');
expect(() => {
worldManager.createWorld('duplicate-world');
}).toThrow("World name 'duplicate-world' 已存在");
});
test('超出最大World数量应该抛出错误', () => {
const limitedManager = new WorldManager({ maxWorlds: 2 });
limitedManager.createWorld('world1');
limitedManager.createWorld('world2');
expect(() => {
limitedManager.createWorld('world3');
}).toThrow('已达到最大World数量限制: 2');
// 清理
limitedManager.destroy();
});
test('获取World应该正确', () => {
const world = worldManager.createWorld('get-world');
const retrievedWorld = worldManager.getWorld('get-world');
expect(retrievedWorld).toBe(world);
});
test('获取不存在的World应该返回null', () => {
const world = worldManager.getWorld('non-existent');
expect(world).toBeNull();
});
test('检查World存在性应该正确', () => {
expect(worldManager.getWorld('non-existent')).toBeNull();
worldManager.createWorld('exists');
expect(worldManager.getWorld('exists')).toBeDefined();
});
test('销毁World应该正确清理', () => {
const world = worldManager.createWorld('destroy-world');
world.start();
const destroyed = worldManager.removeWorld('destroy-world');
expect(destroyed).toBe(true);
expect(worldManager.getWorld('destroy-world')).toBeNull();
});
test('销毁不存在的World应该返回false', () => {
const destroyed = worldManager.removeWorld('non-existent');
expect(destroyed).toBe(false);
});
test('获取所有World ID应该正确', () => {
worldManager.createWorld('world1');
worldManager.createWorld('world2');
worldManager.createWorld('world3');
const worldIds = worldManager.getWorldIds();
expect(worldIds).toHaveLength(3);
expect(worldIds).toContain('world1');
expect(worldIds).toContain('world2');
expect(worldIds).toContain('world3');
});
});
describe('活跃World管理', () => {
test('启动World应该加入活跃列表', () => {
const world = worldManager.createWorld('active-world');
worldManager.setWorldActive('active-world', true);
const activeWorlds = worldManager.getActiveWorlds();
expect(activeWorlds).toHaveLength(1);
expect(activeWorlds[0]).toBe(world);
});
test('停止World应该从活跃列表移除', () => {
worldManager.createWorld('inactive-world');
worldManager.setWorldActive('inactive-world', true);
worldManager.setWorldActive('inactive-world', false);
const activeWorlds = worldManager.getActiveWorlds();
expect(activeWorlds).toHaveLength(0);
});
test('销毁激活的World应该从活跃列表移除', () => {
worldManager.createWorld('destroy-active');
worldManager.setWorldActive('destroy-active', true);
worldManager.removeWorld('destroy-active');
const activeWorlds = worldManager.getActiveWorlds();
expect(activeWorlds).toHaveLength(0);
});
test('多个World的激活状态应该独立管理', () => {
const world1 = worldManager.createWorld('world1');
const world2 = worldManager.createWorld('world2');
const world3 = worldManager.createWorld('world3');
worldManager.setWorldActive('world1', true);
worldManager.setWorldActive('world3', true);
// world2 保持未启动
const activeWorlds = worldManager.getActiveWorlds();
expect(activeWorlds).toHaveLength(2);
expect(activeWorlds).toContain(world1);
expect(activeWorlds).toContain(world3);
expect(activeWorlds).not.toContain(world2);
});
});
describe('统计和监控', () => {
test('获取WorldManager状态应该正确', () => {
worldManager.createWorld('status-world1');
worldManager.createWorld('status-world2');
worldManager.setWorldActive('status-world2', true);
const status = worldManager.getStats();
expect(status.totalWorlds).toBe(2);
expect(status.activeWorlds).toBe(1);
expect(status.config.maxWorlds).toBeGreaterThan(0);
expect(status.memoryUsage).toBeGreaterThanOrEqual(0);
expect(status.isRunning).toBeDefined();
});
test('获取所有World统计应该包含详细信息', () => {
const world1 = worldManager.createWorld('stats-world1');
worldManager.createWorld('stats-world2');
// 为world1添加一些内容
const scene1 = world1.createScene('scene1');
scene1.createEntity('entity1');
worldManager.setWorldActive('stats-world1', true);
// world2保持空
const allStats = worldManager.getDetailedStatus().worlds;
expect(allStats).toHaveLength(2);
const world1Stats = allStats.find((stat) => stat.id === 'stats-world1');
const world2Stats = allStats.find((stat) => stat.id === 'stats-world2');
expect(world1Stats).toBeDefined();
expect(world2Stats).toBeDefined();
expect(world1Stats?.isActive).toBe(true);
expect(world2Stats?.isActive).toBe(false);
});
test('空WorldManager的统计应该正确', () => {
const status = worldManager.getStats();
const allStats = worldManager.getDetailedStatus().worlds;
expect(status.totalWorlds).toBe(0);
expect(status.activeWorlds).toBe(0);
expect(allStats).toHaveLength(0);
});
});
describe('清理功能', () => {
test('清理空闲World应该移除符合条件的World', () => {
// 创建一个空的World
worldManager.createWorld('empty-world');
// 创建一个有内容的World
const fullWorld = worldManager.createWorld('full-world');
const scene = fullWorld.createScene('scene');
scene.createEntity('entity');
fullWorld.start();
// 执行清理
const cleanedCount = worldManager.cleanup();
// 由于清理逻辑可能基于时间或其他条件,这里主要测试不会出错
expect(cleanedCount).toBeGreaterThanOrEqual(0);
expect(() => worldManager.cleanup()).not.toThrow();
});
test('应该在指定帧数后自动执行清理', () => {
jest.useFakeTimers();
const manager = new WorldManager({
autoCleanup: true,
cleanupFrameInterval: 10 // 10 帧后清理
});
manager.createWorld('test');
// 模拟一个空的老 World (超过 10 分钟)
jest.advanceTimersByTime(11 * 60 * 1000);
// 执行 9 帧,不应该清理
for (let i = 0; i < 9; i++) {
manager.updateAll();
}
expect(manager.getWorld('test')).not.toBeNull();
// 第 10 帧,应该清理
manager.updateAll();
expect(manager.getWorld('test')).toBeNull();
jest.useRealTimers();
manager.destroy();
});
test('autoCleanup=false 时不应该自动清理', () => {
jest.useFakeTimers();
const manager = new WorldManager({
autoCleanup: false
});
manager.createWorld('test');
jest.advanceTimersByTime(20 * 60 * 1000);
// 执行多帧
for (let i = 0; i < 2000; i++) {
manager.updateAll();
}
// 不应该清理
expect(manager.getWorld('test')).not.toBeNull();
jest.useRealTimers();
manager.destroy();
});
test('清理后计数器应该重置', () => {
jest.useFakeTimers();
const manager = new WorldManager({
autoCleanup: true,
cleanupFrameInterval: 10
});
manager.createWorld('world1');
jest.advanceTimersByTime(11 * 60 * 1000);
// 第 10 帧触发清理
for (let i = 0; i < 10; i++) {
manager.updateAll();
}
// world1 被清理
expect(manager.getWorld('world1')).toBeNull();
// 创建新 World
manager.createWorld('world2');
jest.advanceTimersByTime(11 * 60 * 1000);
// 又要等 10 帧才能清理 (计数器已重置)
for (let i = 0; i < 9; i++) {
manager.updateAll();
}
expect(manager.getWorld('world2')).not.toBeNull();
manager.updateAll();
expect(manager.getWorld('world2')).toBeNull();
jest.useRealTimers();
manager.destroy();
});
test('应该使用配置的 cleanupFrameInterval', () => {
const manager = new WorldManager({
autoCleanup: true,
cleanupFrameInterval: 5
});
expect(manager.config.cleanupFrameInterval).toBe(5);
manager.destroy();
});
});
describe('World更新协调', () => {
test('更新所有活跃World应该正确', () => {
const world1 = worldManager.createWorld('update-world1');
const world2 = worldManager.createWorld('update-world2');
worldManager.createWorld('update-world3');
// 添加一些内容到World中
const scene1 = world1.createScene('scene1');
const scene2 = world2.createScene('scene2');
scene1.createEntity('entity1');
scene2.createEntity('entity2');
// 启动部分World
worldManager.setWorldActive('update-world1', true);
worldManager.setWorldActive('update-world2', true);
// world3保持未启动
// 手动调用更新通常由Core.update()调用)
const activeWorlds = worldManager.getActiveWorlds();
expect(() => {
activeWorlds.forEach((world) => {
world.updateGlobalSystems();
world.updateScenes();
});
}).not.toThrow();
expect(activeWorlds).toHaveLength(2);
});
test('updateAll 应该只更新活跃的 World', () => {
const world1 = worldManager.createWorld('world1');
const world2 = worldManager.createWorld('world2');
const system1 = new TestGlobalSystem();
const system2 = new TestGlobalSystem();
world1.addGlobalSystem(system1);
world2.addGlobalSystem(system2);
worldManager.setWorldActive('world1', true);
// world2 保持未激活
worldManager.updateAll();
expect(system1.updateCount).toBe(1); // world1 被更新
expect(system2.updateCount).toBe(0); // world2 未被更新
});
test('isRunning=false 时 updateAll 不应该更新任何 World', () => {
const world = worldManager.createWorld('world');
const system = new TestGlobalSystem();
world.addGlobalSystem(system);
worldManager.setWorldActive('world', true);
worldManager.stopAll(); // isRunning = false
worldManager.updateAll();
expect(system.updateCount).toBe(0);
});
test('updateAll 应该正确处理多个活跃和非活跃 World', () => {
const worlds: World[] = [];
const systems: TestGlobalSystem[] = [];
// 创建 5 个 World
for (let i = 0; i < 5; i++) {
const world = worldManager.createWorld(`world${i}`);
const system = new TestGlobalSystem();
world.addGlobalSystem(system);
worlds.push(world);
systems.push(system);
}
// 激活第 0, 2, 4 个
worldManager.setWorldActive('world0', true);
worldManager.setWorldActive('world2', true);
worldManager.setWorldActive('world4', true);
worldManager.updateAll();
expect(systems[0].updateCount).toBe(1); // 活跃
expect(systems[1].updateCount).toBe(0); // 非活跃
expect(systems[2].updateCount).toBe(1); // 活跃
expect(systems[3].updateCount).toBe(0); // 非活跃
expect(systems[4].updateCount).toBe(1); // 活跃
});
});
describe('边界情况和错误处理', () => {
test('World ID为空字符串应该抛出错误', () => {
expect(() => {
worldManager.createWorld('');
}).toThrow();
});
test('World ID为null或undefined应该抛出错误', () => {
expect(() => {
worldManager.createWorld(null as unknown as string);
}).toThrow();
expect(() => {
worldManager.createWorld(undefined as unknown as string);
}).toThrow();
});
test('极限情况下的大量World管理', () => {
const worldCount = 50;
const worldIds: string[] = [];
// 创建大量World
for (let i = 0; i < worldCount; i++) {
const worldId = `mass-world-${i}`;
worldIds.push(worldId);
expect(() => {
worldManager.createWorld(worldId);
}).not.toThrow();
}
expect(worldManager.getWorldIds()).toHaveLength(worldCount);
// 启动一半的World
for (let i = 0; i < worldCount / 2; i++) {
worldManager.setWorldActive(worldIds[i], true);
}
expect(worldManager.getActiveWorlds()).toHaveLength(worldCount / 2);
// 批量清理
worldIds.forEach((id) => {
expect(() => {
worldManager.removeWorld(id);
}).not.toThrow();
});
expect(worldManager.getWorldIds()).toHaveLength(0);
});
test('销毁后获取World应该返回null', () => {
worldManager.createWorld('temp-world');
worldManager.removeWorld('temp-world');
expect(worldManager.getWorld('temp-world')).toBeNull();
});
});
describe('内存管理', () => {
test('销毁所有World后内存应该被释放', () => {
// 创建多个World并添加内容
for (let i = 0; i < 10; i++) {
const world = worldManager.createWorld(`memory-world-${i}`);
const scene = world.createScene('scene');
// 添加一些实体和系统
for (let j = 0; j < 5; j++) {
const entity = scene.createEntity(`entity-${j}`);
entity.addComponent(new TestComponent(j));
}
world.addGlobalSystem(new TestGlobalSystem());
worldManager.setWorldActive(`memory-world-${i}`, true);
}
const beforeCleanup = worldManager.getStats();
expect(beforeCleanup.totalWorlds).toBe(10);
expect(beforeCleanup.activeWorlds).toBe(10);
// 清理所有World
const worldIds = worldManager.getWorldIds();
worldIds.forEach((id) => {
worldManager.removeWorld(id);
});
const afterCleanup = worldManager.getStats();
expect(afterCleanup.totalWorlds).toBe(0);
expect(afterCleanup.activeWorlds).toBe(0);
});
});
describe('配置验证', () => {
test('无效的maxWorlds配置应该按传入值使用', () => {
const invalidConfig: IWorldManagerConfig = {
maxWorlds: -1,
autoCleanup: true,
debug: true
};
const manager = new WorldManager(invalidConfig);
expect(manager.getStats().config.maxWorlds).toBe(-1);
manager.destroy();
});
test('配置应该正确应用于新实例', () => {
const config: IWorldManagerConfig = {
maxWorlds: 3,
autoCleanup: true,
debug: true
};
const manager = new WorldManager(config);
// 创建到限制数量的World
manager.createWorld('world1');
manager.createWorld('world2');
manager.createWorld('world3');
// 第四个应该失败
expect(() => {
manager.createWorld('world4');
}).toThrow();
// 清理
manager.destroy();
});
});
describe('isWorldActive()', () => {
test('应该正确返回 World 激活状态', () => {
worldManager.createWorld('test');
// 初始未激活
expect(worldManager.isWorldActive('test')).toBe(false);
// 激活后
worldManager.setWorldActive('test', true);
expect(worldManager.isWorldActive('test')).toBe(true);
// 停用后
worldManager.setWorldActive('test', false);
expect(worldManager.isWorldActive('test')).toBe(false);
});
test('不存在的 World 应该返回 false', () => {
expect(worldManager.isWorldActive('non-existent')).toBe(false);
});
test('应该与 world.isActive 保持一致', () => {
const world = worldManager.createWorld('test');
expect(worldManager.isWorldActive('test')).toBe(world.isActive);
worldManager.setWorldActive('test', true);
expect(worldManager.isWorldActive('test')).toBe(world.isActive);
worldManager.setWorldActive('test', false);
expect(worldManager.isWorldActive('test')).toBe(world.isActive);
});
});
describe('activeWorldCount', () => {
test('应该正确返回激活的 World 数量', () => {
expect(worldManager.activeWorldCount).toBe(0);
worldManager.createWorld('world1');
worldManager.createWorld('world2');
worldManager.createWorld('world3');
expect(worldManager.activeWorldCount).toBe(0);
worldManager.setWorldActive('world1', true);
expect(worldManager.activeWorldCount).toBe(1);
worldManager.setWorldActive('world2', true);
expect(worldManager.activeWorldCount).toBe(2);
worldManager.setWorldActive('world1', false);
expect(worldManager.activeWorldCount).toBe(1);
});
test('应该与 getActiveWorlds().length 一致', () => {
worldManager.createWorld('world1');
worldManager.createWorld('world2');
worldManager.setWorldActive('world1', true);
expect(worldManager.activeWorldCount).toBe(worldManager.getActiveWorlds().length);
});
});
describe('getAllWorlds()', () => {
test('应该返回所有 World', () => {
const world1 = worldManager.createWorld('world1');
const world2 = worldManager.createWorld('world2');
const world3 = worldManager.createWorld('world3');
const allWorlds = worldManager.getAllWorlds();
expect(allWorlds).toHaveLength(3);
expect(allWorlds).toContain(world1);
expect(allWorlds).toContain(world2);
expect(allWorlds).toContain(world3);
});
test('空 WorldManager 应该返回空数组', () => {
const allWorlds = worldManager.getAllWorlds();
expect(allWorlds).toHaveLength(0);
});
test('返回的数组修改不应该影响内部状态', () => {
worldManager.createWorld('world1');
const allWorlds = worldManager.getAllWorlds();
allWorlds.push({} as unknown as World);
expect(worldManager.getAllWorlds()).toHaveLength(1);
});
});
});