Files
esengine/tests/ECS/Core/SoAStorage.comprehensive.test.ts
2025-07-30 14:14:04 +08:00

307 lines
11 KiB
TypeScript

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('✅ 向量化操作测试通过');
});
});