Feature/editor optimization (#251)
* 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 构建错误并优化构建性能
This commit is contained in:
648
packages/core/tests/ECS/Hierarchy.test.ts
Normal file
648
packages/core/tests/ECS/Hierarchy.test.ts
Normal file
@@ -0,0 +1,648 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user