优化IdentifierPool - 世代式ID池管理器
This commit is contained in:
414
tests/ECS/Utils/IdentifierPool.test.ts
Normal file
414
tests/ECS/Utils/IdentifierPool.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
220
tests/integration/IdentifierPool.integration.test.ts
Normal file
220
tests/integration/IdentifierPool.integration.test.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* IdentifierPool 集成测试
|
||||
*
|
||||
* 测试新的世代式IdentifierPool与现有ECS系统的兼容性
|
||||
*/
|
||||
import { IdentifierPool } from '../../src/ECS/Utils/IdentifierPool';
|
||||
import { Scene } from '../../src/ECS/Scene';
|
||||
import { Entity } from '../../src/ECS/Entity';
|
||||
|
||||
describe('IdentifierPool 集成测试', () => {
|
||||
let scene: Scene;
|
||||
|
||||
beforeEach(() => {
|
||||
scene = new Scene();
|
||||
});
|
||||
|
||||
describe('与Scene系统集成', () => {
|
||||
test('Scene应该能使用新的IdentifierPool', () => {
|
||||
// Scene内部使用IdentifierPool
|
||||
expect(scene.identifierPool).toBeInstanceOf(IdentifierPool);
|
||||
});
|
||||
|
||||
test('Scene应该能正常创建实体', () => {
|
||||
const entity1 = scene.createEntity('TestEntity1');
|
||||
const entity2 = scene.createEntity('TestEntity2');
|
||||
const entity3 = scene.createEntity('TestEntity3');
|
||||
|
||||
expect(entity1).toBeInstanceOf(Entity);
|
||||
expect(entity2).toBeInstanceOf(Entity);
|
||||
expect(entity3).toBeInstanceOf(Entity);
|
||||
|
||||
// ID应该是唯一的
|
||||
expect(entity1.id).not.toBe(entity2.id);
|
||||
expect(entity2.id).not.toBe(entity3.id);
|
||||
expect(entity1.id).not.toBe(entity3.id);
|
||||
|
||||
// 验证新的ID格式(世代式)
|
||||
expect(entity1.id).toBeGreaterThan(65535); // 应该包含世代信息
|
||||
expect(entity2.id).toBeGreaterThan(65535);
|
||||
expect(entity3.id).toBeGreaterThan(65535);
|
||||
});
|
||||
|
||||
test('实体销毁应该能正确回收ID', () => {
|
||||
const entity = scene.createEntity('ToDestroy');
|
||||
const originalId = entity.id;
|
||||
|
||||
// 销毁实体
|
||||
entity.destroy();
|
||||
|
||||
// 验证ID池统计
|
||||
const stats = scene.identifierPool.getStats();
|
||||
expect(stats.pendingRecycle).toBeGreaterThan(0);
|
||||
|
||||
// 在当前时间点,ID应该仍然有效(因为有延迟回收)
|
||||
expect(scene.identifierPool.isValid(originalId)).toBe(true);
|
||||
});
|
||||
|
||||
test('大量实体创建和销毁应该正常工作', () => {
|
||||
const entities: Entity[] = [];
|
||||
const count = 100;
|
||||
|
||||
// 创建大量实体
|
||||
for (let i = 0; i < count; i++) {
|
||||
entities.push(scene.createEntity(`Entity_${i}`));
|
||||
}
|
||||
|
||||
// 验证实体数量
|
||||
expect(entities.length).toBe(count);
|
||||
|
||||
// 验证所有ID唯一
|
||||
const ids = entities.map(e => e.id);
|
||||
const uniqueIds = new Set(ids);
|
||||
expect(uniqueIds.size).toBe(count);
|
||||
|
||||
// 销毁一半实体
|
||||
const toDestroy = entities.slice(0, 50);
|
||||
toDestroy.forEach(entity => entity.destroy());
|
||||
|
||||
// 验证ID池状态
|
||||
const stats = scene.identifierPool.getStats();
|
||||
expect(stats.pendingRecycle).toBe(50);
|
||||
expect(stats.totalAllocated).toBe(count);
|
||||
});
|
||||
|
||||
test('批量创建实体应该正常工作', () => {
|
||||
const count = 50;
|
||||
const entities = scene.createEntities(count, 'BatchEntity');
|
||||
|
||||
expect(entities.length).toBe(count);
|
||||
|
||||
// 验证所有实体都有有效ID
|
||||
entities.forEach(entity => {
|
||||
expect(scene.identifierPool.isValid(entity.id)).toBe(true);
|
||||
});
|
||||
|
||||
// 验证ID唯一性
|
||||
const ids = entities.map(e => e.id);
|
||||
const uniqueIds = new Set(ids);
|
||||
expect(uniqueIds.size).toBe(count);
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能和内存验证', () => {
|
||||
test('ID分配性能应该满足要求', () => {
|
||||
const count = 1000;
|
||||
const startTime = performance.now();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
scene.createEntity(`PerfTest_${i}`);
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
// 1000个实体应该在100ms内创建完成
|
||||
expect(duration).toBeLessThan(100);
|
||||
|
||||
// 验证内存使用合理(动态分配应该更高效)
|
||||
const stats = scene.identifierPool.getStats();
|
||||
expect(stats.memoryUsage).toBeLessThan(1000 * 100); // 每个实体少于100字节
|
||||
});
|
||||
|
||||
test('ID回收不应该影响性能', () => {
|
||||
const entities: Entity[] = [];
|
||||
const count = 500;
|
||||
|
||||
// 创建实体
|
||||
for (let i = 0; i < count; i++) {
|
||||
entities.push(scene.createEntity(`RecycleTest_${i}`));
|
||||
}
|
||||
|
||||
// 测试回收性能
|
||||
const startTime = performance.now();
|
||||
|
||||
entities.forEach(entity => entity.destroy());
|
||||
|
||||
const endTime = performance.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
// 回收500个实体应该在50ms内完成
|
||||
expect(duration).toBeLessThan(50);
|
||||
|
||||
const stats = scene.identifierPool.getStats();
|
||||
expect(stats.pendingRecycle).toBe(count);
|
||||
});
|
||||
});
|
||||
|
||||
describe('向后兼容性', () => {
|
||||
test('现有的Entity API应该继续工作', () => {
|
||||
const entity = scene.createEntity('CompatTest');
|
||||
|
||||
// 基本属性应该存在
|
||||
expect(typeof entity.id).toBe('number');
|
||||
expect(typeof entity.name).toBe('string');
|
||||
expect(entity.name).toBe('CompatTest');
|
||||
|
||||
// 基本方法应该工作
|
||||
expect(typeof entity.destroy).toBe('function');
|
||||
expect(typeof entity.addComponent).toBe('function');
|
||||
expect(typeof entity.getComponent).toBe('function');
|
||||
});
|
||||
|
||||
test('Scene的createEntity方法应该继续工作', () => {
|
||||
// 传入名称
|
||||
const entity1 = scene.createEntity('TestEntity');
|
||||
expect(entity1).toBeInstanceOf(Entity);
|
||||
|
||||
const entity2 = scene.createEntity('Named');
|
||||
expect(entity2.name).toBe('Named');
|
||||
|
||||
// 批量创建
|
||||
const entities = scene.createEntities(5);
|
||||
expect(entities.length).toBe(5);
|
||||
});
|
||||
|
||||
test('实体销毁应该继续工作', () => {
|
||||
const entity = scene.createEntity('ToDestroy');
|
||||
const initialCount = scene.entities.count;
|
||||
|
||||
entity.destroy();
|
||||
|
||||
expect(entity.isDestroyed).toBe(true);
|
||||
expect(scene.entities.count).toBe(initialCount - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('世代版本特性验证', () => {
|
||||
test('回收的ID应该不能被误用', () => {
|
||||
const entity = scene.createEntity('GenerationTest');
|
||||
const oldId = entity.id;
|
||||
|
||||
// 销毁实体
|
||||
entity.destroy();
|
||||
|
||||
// 等待延迟回收处理
|
||||
jest.useFakeTimers();
|
||||
jest.advanceTimersByTime(150);
|
||||
|
||||
// 创建新实体触发回收处理
|
||||
const newEntity = scene.createEntity('NewEntity');
|
||||
|
||||
// 旧ID应该无效
|
||||
expect(scene.identifierPool.isValid(oldId)).toBe(false);
|
||||
|
||||
// 新ID应该有效
|
||||
expect(scene.identifierPool.isValid(newEntity.id)).toBe(true);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
test('ID验证应该工作正常', () => {
|
||||
const entity = scene.createEntity('ValidateTest');
|
||||
const validId = entity.id;
|
||||
const invalidId = 999999;
|
||||
|
||||
expect(scene.identifierPool.isValid(validId)).toBe(true);
|
||||
expect(scene.identifierPool.isValid(invalidId)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
72
tests/setup.ts
Normal file
72
tests/setup.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Jest 测试全局设置文件
|
||||
*
|
||||
* 此文件在每个测试文件执行前运行,用于设置全局测试环境
|
||||
*/
|
||||
|
||||
// 设置测试超时时间(毫秒)
|
||||
jest.setTimeout(10000);
|
||||
|
||||
// 模拟控制台方法以减少测试输出噪音
|
||||
const originalConsoleLog = console.log;
|
||||
const originalConsoleWarn = console.warn;
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
// 在测试环境中可以选择性地静默某些日志
|
||||
beforeAll(() => {
|
||||
// 可以在这里设置全局的模拟或配置
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// 清理全局资源
|
||||
});
|
||||
|
||||
// 每个测试前的清理
|
||||
beforeEach(() => {
|
||||
// 清理定时器
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// 恢复所有模拟
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
// 导出测试工具函数
|
||||
export const TestUtils = {
|
||||
/**
|
||||
* 创建测试用的延迟
|
||||
* @param ms 延迟毫秒数
|
||||
*/
|
||||
delay: (ms: number): Promise<void> => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
},
|
||||
|
||||
/**
|
||||
* 等待条件满足
|
||||
* @param condition 条件函数
|
||||
* @param timeout 超时时间(毫秒)
|
||||
* @param interval 检查间隔(毫秒)
|
||||
*/
|
||||
waitFor: async (
|
||||
condition: () => boolean,
|
||||
timeout: number = 5000,
|
||||
interval: number = 10
|
||||
): Promise<void> => {
|
||||
const start = Date.now();
|
||||
while (!condition() && Date.now() - start < timeout) {
|
||||
await TestUtils.delay(interval);
|
||||
}
|
||||
if (!condition()) {
|
||||
throw new Error(`等待条件超时 (${timeout}ms)`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 模拟时间前进
|
||||
* @param ms 前进的毫秒数
|
||||
*/
|
||||
advanceTime: (ms: number): void => {
|
||||
jest.advanceTimersByTime(ms);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user