组件引用完整性,升级到es2021使用weakref

This commit is contained in:
YHH
2025-10-10 23:38:48 +08:00
parent 536871d09b
commit 7850fc610c
12 changed files with 1024 additions and 5 deletions

View 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);
});
});
});