* refactor: 编辑器/运行时架构拆分与构建系统升级 * feat(core): 层级系统重构与UI变换矩阵修复 * refactor: 移除 ecs-components 聚合包并修复跨包组件查找问题 * fix(physics): 修复跨包组件类引用问题 * feat: 统一运行时架构与浏览器运行支持 * feat(asset): 实现浏览器运行时资产加载系统 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题和CI类型检查错误 * fix: 修复文档、CodeQL安全问题、CI类型检查和测试错误 * test: 补齐核心模块测试用例,修复CI构建配置 * fix: 修复测试用例中的类型错误和断言问题 * fix: 修复 turbo build:npm 任务的依赖顺序问题 * fix: 修复 CI 构建错误并优化构建性能
649 lines
24 KiB
TypeScript
649 lines
24 KiB
TypeScript
import { Scene, Entity, HierarchyComponent, HierarchySystem } from '../../src';
|
|
|
|
describe('HierarchySystem', () => {
|
|
let scene: Scene;
|
|
let hierarchySystem: HierarchySystem;
|
|
|
|
beforeEach(() => {
|
|
scene = new Scene();
|
|
scene.initialize();
|
|
hierarchySystem = new HierarchySystem();
|
|
scene.addSystem(hierarchySystem);
|
|
});
|
|
|
|
afterEach(() => {
|
|
scene.end();
|
|
});
|
|
|
|
describe('setParent', () => {
|
|
it('should set parent-child relationship', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
expect(hierarchySystem.getParent(child)).toBe(parent);
|
|
expect(hierarchySystem.getChildren(parent)).toContain(child);
|
|
expect(hierarchySystem.getChildCount(parent)).toBe(1);
|
|
});
|
|
|
|
it('should auto-add HierarchyComponent if not present', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
expect(parent.getComponent(HierarchyComponent)).toBeNull();
|
|
expect(child.getComponent(HierarchyComponent)).toBeNull();
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
expect(parent.getComponent(HierarchyComponent)).not.toBeNull();
|
|
expect(child.getComponent(HierarchyComponent)).not.toBeNull();
|
|
});
|
|
|
|
it('should move child to root when parent is null', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
expect(hierarchySystem.getParent(child)).toBe(parent);
|
|
|
|
hierarchySystem.setParent(child, null);
|
|
expect(hierarchySystem.getParent(child)).toBeNull();
|
|
expect(hierarchySystem.getChildren(parent)).not.toContain(child);
|
|
});
|
|
|
|
it('should transfer child to new parent', () => {
|
|
const parent1 = scene.createEntity('Parent1');
|
|
const parent2 = scene.createEntity('Parent2');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent1);
|
|
expect(hierarchySystem.getParent(child)).toBe(parent1);
|
|
expect(hierarchySystem.getChildCount(parent1)).toBe(1);
|
|
|
|
hierarchySystem.setParent(child, parent2);
|
|
expect(hierarchySystem.getParent(child)).toBe(parent2);
|
|
expect(hierarchySystem.getChildCount(parent1)).toBe(0);
|
|
expect(hierarchySystem.getChildCount(parent2)).toBe(1);
|
|
});
|
|
|
|
it('should throw error on circular reference', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
expect(() => {
|
|
hierarchySystem.setParent(parent, grandchild);
|
|
}).toThrow('Cannot set parent: would create circular reference');
|
|
});
|
|
|
|
it('should not change if setting same parent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
const hierarchy = child.getComponent(HierarchyComponent)!;
|
|
hierarchy.bCacheDirty = false;
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
// Should not mark dirty since parent didn't change
|
|
expect(hierarchy.bCacheDirty).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('insertChildAt', () => {
|
|
it('should insert child at specific position', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
const child3 = scene.createEntity('Child3');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child3, parent);
|
|
hierarchySystem.insertChildAt(parent, child2, 1);
|
|
|
|
const children = hierarchySystem.getChildren(parent);
|
|
expect(children[0]).toBe(child1);
|
|
expect(children[1]).toBe(child2);
|
|
expect(children[2]).toBe(child3);
|
|
});
|
|
|
|
it('should append child when index is -1', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.insertChildAt(parent, child2, -1);
|
|
|
|
const children = hierarchySystem.getChildren(parent);
|
|
expect(children[children.length - 1]).toBe(child2);
|
|
});
|
|
});
|
|
|
|
describe('removeChild', () => {
|
|
it('should remove child from parent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
expect(hierarchySystem.getChildCount(parent)).toBe(1);
|
|
|
|
const result = hierarchySystem.removeChild(parent, child);
|
|
expect(result).toBe(true);
|
|
expect(hierarchySystem.getChildCount(parent)).toBe(0);
|
|
expect(hierarchySystem.getParent(child)).toBeNull();
|
|
});
|
|
|
|
it('should return false if child is not a child of parent', () => {
|
|
const parent1 = scene.createEntity('Parent1');
|
|
const parent2 = scene.createEntity('Parent2');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent1);
|
|
|
|
const result = hierarchySystem.removeChild(parent2, child);
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('removeAllChildren', () => {
|
|
it('should remove all children from parent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
const child3 = scene.createEntity('Child3');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child2, parent);
|
|
hierarchySystem.setParent(child3, parent);
|
|
expect(hierarchySystem.getChildCount(parent)).toBe(3);
|
|
|
|
hierarchySystem.removeAllChildren(parent);
|
|
expect(hierarchySystem.getChildCount(parent)).toBe(0);
|
|
expect(hierarchySystem.getParent(child1)).toBeNull();
|
|
expect(hierarchySystem.getParent(child2)).toBeNull();
|
|
expect(hierarchySystem.getParent(child3)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('hierarchy queries', () => {
|
|
it('should check if entity has children', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
expect(hierarchySystem.hasChildren(parent)).toBe(false);
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
expect(hierarchySystem.hasChildren(parent)).toBe(true);
|
|
});
|
|
|
|
it('should check isAncestorOf', () => {
|
|
const grandparent = scene.createEntity('Grandparent');
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(parent, grandparent);
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
expect(hierarchySystem.isAncestorOf(grandparent, child)).toBe(true);
|
|
expect(hierarchySystem.isAncestorOf(parent, child)).toBe(true);
|
|
expect(hierarchySystem.isAncestorOf(child, grandparent)).toBe(false);
|
|
});
|
|
|
|
it('should check isDescendantOf', () => {
|
|
const grandparent = scene.createEntity('Grandparent');
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(parent, grandparent);
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
expect(hierarchySystem.isDescendantOf(child, grandparent)).toBe(true);
|
|
expect(hierarchySystem.isDescendantOf(child, parent)).toBe(true);
|
|
expect(hierarchySystem.isDescendantOf(grandparent, child)).toBe(false);
|
|
});
|
|
|
|
it('should get root entity', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
expect(hierarchySystem.getRoot(grandchild)).toBe(root);
|
|
expect(hierarchySystem.getRoot(child)).toBe(root);
|
|
expect(hierarchySystem.getRoot(root)).toBe(root);
|
|
});
|
|
|
|
it('should get depth correctly', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
root.addComponent(new HierarchyComponent());
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
expect(hierarchySystem.getDepth(root)).toBe(0);
|
|
expect(hierarchySystem.getDepth(child)).toBe(1);
|
|
expect(hierarchySystem.getDepth(grandchild)).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('findChild', () => {
|
|
it('should find child by name', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Target');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child2, parent);
|
|
|
|
const found = hierarchySystem.findChild(parent, 'Target');
|
|
expect(found).toBe(child2);
|
|
});
|
|
|
|
it('should find child recursively', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Target');
|
|
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
const found = hierarchySystem.findChild(root, 'Target', true);
|
|
expect(found).toBe(grandchild);
|
|
|
|
const notFound = hierarchySystem.findChild(root, 'Target', false);
|
|
expect(notFound).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('forEachChild', () => {
|
|
it('should iterate over children', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child2, parent);
|
|
|
|
const visited: Entity[] = [];
|
|
hierarchySystem.forEachChild(parent, (child) => {
|
|
visited.push(child);
|
|
});
|
|
|
|
expect(visited).toContain(child1);
|
|
expect(visited).toContain(child2);
|
|
expect(visited.length).toBe(2);
|
|
});
|
|
|
|
it('should iterate recursively', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
const visited: Entity[] = [];
|
|
hierarchySystem.forEachChild(root, (entity) => {
|
|
visited.push(entity);
|
|
}, true);
|
|
|
|
expect(visited).toContain(child);
|
|
expect(visited).toContain(grandchild);
|
|
expect(visited.length).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('getRootEntities', () => {
|
|
it('should return all root entities', () => {
|
|
const root1 = scene.createEntity('Root1');
|
|
const root2 = scene.createEntity('Root2');
|
|
const child = scene.createEntity('Child');
|
|
|
|
root1.addComponent(new HierarchyComponent());
|
|
root2.addComponent(new HierarchyComponent());
|
|
hierarchySystem.setParent(child, root1);
|
|
|
|
const roots = hierarchySystem.getRootEntities();
|
|
expect(roots).toContain(root1);
|
|
expect(roots).toContain(root2);
|
|
expect(roots).not.toContain(child);
|
|
});
|
|
});
|
|
|
|
describe('activeInHierarchy', () => {
|
|
it('should be inactive if parent is inactive', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
expect(hierarchySystem.isActiveInHierarchy(child)).toBe(true);
|
|
|
|
parent.active = false;
|
|
// Mark cache dirty to recalculate
|
|
const childHierarchy = child.getComponent(HierarchyComponent)!;
|
|
childHierarchy.bCacheDirty = true;
|
|
|
|
expect(hierarchySystem.isActiveInHierarchy(child)).toBe(false);
|
|
});
|
|
|
|
it('should be inactive if self is inactive', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
child.active = false;
|
|
|
|
expect(hierarchySystem.isActiveInHierarchy(child)).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('HierarchyComponent', () => {
|
|
it('should have correct default values', () => {
|
|
const component = new HierarchyComponent();
|
|
|
|
expect(component.parentId).toBeNull();
|
|
expect(component.childIds).toEqual([]);
|
|
expect(component.depth).toBe(0);
|
|
expect(component.bActiveInHierarchy).toBe(true);
|
|
expect(component.bCacheDirty).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('HierarchySystem - Extended Tests', () => {
|
|
let scene: Scene;
|
|
let hierarchySystem: HierarchySystem;
|
|
|
|
beforeEach(() => {
|
|
scene = new Scene();
|
|
scene.initialize();
|
|
hierarchySystem = new HierarchySystem();
|
|
scene.addSystem(hierarchySystem);
|
|
});
|
|
|
|
afterEach(() => {
|
|
scene.end();
|
|
});
|
|
|
|
describe('findChildrenByTag', () => {
|
|
it('should find children by tag', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
const child3 = scene.createEntity('Child3');
|
|
|
|
child1.tag = 0x01;
|
|
child2.tag = 0x02;
|
|
child3.tag = 0x01;
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child2, parent);
|
|
hierarchySystem.setParent(child3, parent);
|
|
|
|
const found = hierarchySystem.findChildrenByTag(parent, 0x01);
|
|
expect(found.length).toBe(2);
|
|
expect(found).toContain(child1);
|
|
expect(found).toContain(child3);
|
|
});
|
|
|
|
it('should find children by tag recursively', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
child.tag = 0x01;
|
|
grandchild.tag = 0x01;
|
|
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
const foundNonRecursive = hierarchySystem.findChildrenByTag(root, 0x01, false);
|
|
expect(foundNonRecursive.length).toBe(1);
|
|
expect(foundNonRecursive[0]).toBe(child);
|
|
|
|
const foundRecursive = hierarchySystem.findChildrenByTag(root, 0x01, true);
|
|
expect(foundRecursive.length).toBe(2);
|
|
expect(foundRecursive).toContain(child);
|
|
expect(foundRecursive).toContain(grandchild);
|
|
});
|
|
|
|
it('should return empty array when no children match tag', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
child.tag = 0x01;
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
const found = hierarchySystem.findChildrenByTag(parent, 0x02);
|
|
expect(found).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('flattenHierarchy', () => {
|
|
it('should flatten hierarchy with expanded nodes', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
root.addComponent(new HierarchyComponent());
|
|
hierarchySystem.setParent(child1, root);
|
|
hierarchySystem.setParent(child2, root);
|
|
hierarchySystem.setParent(grandchild, child1);
|
|
|
|
const expandedIds = new Set([root.id, child1.id]);
|
|
const flattened = hierarchySystem.flattenHierarchy(expandedIds);
|
|
|
|
expect(flattened.length).toBe(4);
|
|
expect(flattened[0].entity).toBe(root);
|
|
expect(flattened[0].depth).toBe(0);
|
|
expect(flattened[0].bHasChildren).toBe(true);
|
|
expect(flattened[0].bIsExpanded).toBe(true);
|
|
});
|
|
|
|
it('should not include children of collapsed nodes', () => {
|
|
const root = scene.createEntity('Root');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
root.addComponent(new HierarchyComponent());
|
|
hierarchySystem.setParent(child, root);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
// Root is expanded, but child is collapsed
|
|
const expandedIds = new Set([root.id]);
|
|
const flattened = hierarchySystem.flattenHierarchy(expandedIds);
|
|
|
|
expect(flattened.length).toBe(2);
|
|
expect(flattened[0].entity).toBe(root);
|
|
expect(flattened[1].entity).toBe(child);
|
|
expect(flattened[1].bHasChildren).toBe(true);
|
|
expect(flattened[1].bIsExpanded).toBe(false);
|
|
});
|
|
|
|
it('should return empty array when no root entities', () => {
|
|
const flattened = hierarchySystem.flattenHierarchy(new Set());
|
|
expect(flattened).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('updateOrder', () => {
|
|
it('should have negative update order for early processing', () => {
|
|
expect(hierarchySystem.updateOrder).toBe(-1000);
|
|
});
|
|
});
|
|
|
|
describe('process - cache update', () => {
|
|
it('should update dirty caches during process', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
// Cache should be dirty after setParent
|
|
const childHierarchy = child.getComponent(HierarchyComponent)!;
|
|
expect(childHierarchy.bCacheDirty).toBe(true);
|
|
|
|
// Update scene to process
|
|
scene.update();
|
|
|
|
// Cache should be clean after process
|
|
expect(childHierarchy.bCacheDirty).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('insertChildAt edge cases', () => {
|
|
it('should handle circular reference prevention', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
const grandchild = scene.createEntity('Grandchild');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
hierarchySystem.setParent(grandchild, child);
|
|
|
|
expect(() => {
|
|
hierarchySystem.insertChildAt(grandchild, parent, 0);
|
|
}).toThrow('Cannot set parent: would create circular reference');
|
|
});
|
|
|
|
it('should move child within same parent to different position', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child1 = scene.createEntity('Child1');
|
|
const child2 = scene.createEntity('Child2');
|
|
const child3 = scene.createEntity('Child3');
|
|
|
|
hierarchySystem.setParent(child1, parent);
|
|
hierarchySystem.setParent(child2, parent);
|
|
hierarchySystem.setParent(child3, parent);
|
|
|
|
// Move child3 to position 0
|
|
hierarchySystem.insertChildAt(parent, child3, 0);
|
|
|
|
const children = hierarchySystem.getChildren(parent);
|
|
expect(children[0]).toBe(child3);
|
|
expect(children[1]).toBe(child1);
|
|
expect(children[2]).toBe(child2);
|
|
});
|
|
});
|
|
|
|
describe('removeChild edge cases', () => {
|
|
it('should return false when parent has no HierarchyComponent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
const result = hierarchySystem.removeChild(parent, child);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('should return false when child has no HierarchyComponent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
parent.addComponent(new HierarchyComponent());
|
|
|
|
const result = hierarchySystem.removeChild(parent, child);
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('removeAllChildren edge cases', () => {
|
|
it('should handle entity with no HierarchyComponent', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
|
|
expect(() => {
|
|
hierarchySystem.removeAllChildren(parent);
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('getChildren edge cases', () => {
|
|
it('should return empty array when entity has no HierarchyComponent', () => {
|
|
const entity = scene.createEntity('Entity');
|
|
const children = hierarchySystem.getChildren(entity);
|
|
expect(children).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('getChildCount edge cases', () => {
|
|
it('should return 0 when entity has no HierarchyComponent', () => {
|
|
const entity = scene.createEntity('Entity');
|
|
expect(hierarchySystem.getChildCount(entity)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('getDepth edge cases', () => {
|
|
it('should return 0 when entity has no HierarchyComponent', () => {
|
|
const entity = scene.createEntity('Entity');
|
|
expect(hierarchySystem.getDepth(entity)).toBe(0);
|
|
});
|
|
|
|
it('should use cached depth when cache is valid', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
parent.addComponent(new HierarchyComponent());
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
// First call computes depth
|
|
const depth1 = hierarchySystem.getDepth(child);
|
|
expect(depth1).toBe(1);
|
|
|
|
// Mark cache as valid
|
|
const childHierarchy = child.getComponent(HierarchyComponent)!;
|
|
childHierarchy.bCacheDirty = false;
|
|
|
|
// Second call should use cache
|
|
const depth2 = hierarchySystem.getDepth(child);
|
|
expect(depth2).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('isActiveInHierarchy edge cases', () => {
|
|
it('should return entity.active when entity has no HierarchyComponent', () => {
|
|
const entity = scene.createEntity('Entity');
|
|
entity.active = true;
|
|
expect(hierarchySystem.isActiveInHierarchy(entity)).toBe(true);
|
|
|
|
entity.active = false;
|
|
expect(hierarchySystem.isActiveInHierarchy(entity)).toBe(false);
|
|
});
|
|
|
|
it('should use cached value when cache is valid', () => {
|
|
const parent = scene.createEntity('Parent');
|
|
const child = scene.createEntity('Child');
|
|
|
|
hierarchySystem.setParent(child, parent);
|
|
|
|
// First call computes activeInHierarchy
|
|
const active1 = hierarchySystem.isActiveInHierarchy(child);
|
|
expect(active1).toBe(true);
|
|
|
|
// Mark cache as valid
|
|
const childHierarchy = child.getComponent(HierarchyComponent)!;
|
|
childHierarchy.bCacheDirty = false;
|
|
|
|
// Second call should use cache
|
|
const active2 = hierarchySystem.isActiveInHierarchy(child);
|
|
expect(active2).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('dispose', () => {
|
|
it('should not throw when disposing', () => {
|
|
expect(() => {
|
|
hierarchySystem.dispose();
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
});
|