组件引用完整性,升级到es2021使用weakref
This commit is contained in:
254
packages/core/tests/ECS/Core/ReferenceTracker.test.ts
Normal file
254
packages/core/tests/ECS/Core/ReferenceTracker.test.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
import { describe, test, expect, beforeEach } from '@jest/globals';
|
||||
import { ReferenceTracker } from '../../../src/ECS/Core/ReferenceTracker';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Scene } from '../../../src/ECS/Scene';
|
||||
|
||||
class TestComponent extends Component {
|
||||
public target: Entity | null = null;
|
||||
}
|
||||
|
||||
describe('ReferenceTracker', () => {
|
||||
let tracker: ReferenceTracker;
|
||||
let scene: Scene;
|
||||
let entity1: Entity;
|
||||
let entity2: Entity;
|
||||
let component: TestComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
tracker = new ReferenceTracker();
|
||||
scene = new Scene();
|
||||
entity1 = scene.createEntity('Entity1');
|
||||
entity2 = scene.createEntity('Entity2');
|
||||
component = new TestComponent();
|
||||
entity1.addComponent(component);
|
||||
});
|
||||
|
||||
describe('registerReference', () => {
|
||||
test('应该成功注册Entity引用', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0].component.deref()).toBe(component);
|
||||
expect(refs[0].propertyKey).toBe('target');
|
||||
});
|
||||
|
||||
test('应该避免重复注册相同引用', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('应该支持多个Component引用同一Entity', () => {
|
||||
const component2 = new TestComponent();
|
||||
const entity3 = scene.createEntity('Entity3');
|
||||
entity3.addComponent(component2);
|
||||
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component2, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('应该支持同一Component引用多个属性', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component, 'parent');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unregisterReference', () => {
|
||||
test('应该成功注销Entity引用', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.unregisterReference(entity2, component, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('注销不存在的引用不应报错', () => {
|
||||
expect(() => {
|
||||
tracker.unregisterReference(entity2, component, 'target');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('应该只注销指定的引用', () => {
|
||||
const component2 = new TestComponent();
|
||||
const entity3 = scene.createEntity('Entity3');
|
||||
entity3.addComponent(component2);
|
||||
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component2, 'target');
|
||||
|
||||
tracker.unregisterReference(entity2, component, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0].component.deref()).toBe(component2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearReferencesTo', () => {
|
||||
test('应该将所有引用设为null', () => {
|
||||
component.target = entity2;
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
tracker.clearReferencesTo(entity2.id);
|
||||
|
||||
expect(component.target).toBeNull();
|
||||
});
|
||||
|
||||
test('应该清理多个Component的引用', () => {
|
||||
const component2 = new TestComponent();
|
||||
const entity3 = scene.createEntity('Entity3');
|
||||
entity3.addComponent(component2);
|
||||
|
||||
component.target = entity2;
|
||||
component2.target = entity2;
|
||||
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component2, 'target');
|
||||
|
||||
tracker.clearReferencesTo(entity2.id);
|
||||
|
||||
expect(component.target).toBeNull();
|
||||
expect(component2.target).toBeNull();
|
||||
});
|
||||
|
||||
test('清理不存在的Entity引用不应报错', () => {
|
||||
expect(() => {
|
||||
tracker.clearReferencesTo(999);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('应该移除引用记录', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.clearReferencesTo(entity2.id);
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearComponentReferences', () => {
|
||||
test('应该清理Component的所有引用注册', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
tracker.clearComponentReferences(component);
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('应该只清理指定Component的引用', () => {
|
||||
const component2 = new TestComponent();
|
||||
const entity3 = scene.createEntity('Entity3');
|
||||
entity3.addComponent(component2);
|
||||
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
tracker.registerReference(entity2, component2, 'target');
|
||||
|
||||
tracker.clearComponentReferences(component);
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1);
|
||||
expect(refs[0].component.deref()).toBe(component2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getReferencesTo', () => {
|
||||
test('应该返回空数组当Entity没有引用时', () => {
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toEqual([]);
|
||||
});
|
||||
|
||||
test('应该只返回有效的引用记录', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanup', () => {
|
||||
test('应该清理失效的WeakRef引用', () => {
|
||||
let tempComponent: TestComponent | null = new TestComponent();
|
||||
const entity3 = scene.createEntity('Entity3');
|
||||
entity3.addComponent(tempComponent);
|
||||
|
||||
tracker.registerReference(entity2, tempComponent, 'target');
|
||||
|
||||
expect(tracker.getReferencesTo(entity2.id)).toHaveLength(1);
|
||||
|
||||
tempComponent = null;
|
||||
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
|
||||
tracker.cleanup();
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs.length).toBeLessThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDebugInfo', () => {
|
||||
test('应该返回调试信息', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
const debugInfo = tracker.getDebugInfo();
|
||||
|
||||
expect(debugInfo).toHaveProperty(`entity_${entity2.id}`);
|
||||
const entityRefs = (debugInfo as any)[`entity_${entity2.id}`];
|
||||
expect(entityRefs).toHaveLength(1);
|
||||
expect(entityRefs[0]).toMatchObject({
|
||||
componentId: component.id,
|
||||
propertyKey: 'target'
|
||||
});
|
||||
});
|
||||
|
||||
test('应该只包含有效的引用', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
const debugInfo = tracker.getDebugInfo();
|
||||
expect(Object.keys(debugInfo)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况', () => {
|
||||
test('应该处理Component被GC回收的情况', () => {
|
||||
tracker.registerReference(entity2, component, 'target');
|
||||
|
||||
tracker.cleanup();
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('应该支持大量引用', () => {
|
||||
const components: TestComponent[] = [];
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const comp = new TestComponent();
|
||||
const ent = scene.createEntity(`Entity${i}`);
|
||||
ent.addComponent(comp);
|
||||
components.push(comp);
|
||||
tracker.registerReference(entity2, comp, 'target');
|
||||
}
|
||||
|
||||
const refs = tracker.getReferencesTo(entity2.id);
|
||||
expect(refs).toHaveLength(1000);
|
||||
|
||||
tracker.clearReferencesTo(entity2.id);
|
||||
|
||||
const refsAfter = tracker.getReferencesTo(entity2.id);
|
||||
expect(refsAfter).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user