From 69655f1936dfa5ca387e6ef36da325f22773076b Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 31 Jul 2025 15:37:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Core.test.ts | 6 +- tests/ECS/Core/ComponentIndexManager.test.ts | 10 +- tests/ECS/Core/ComponentStorage.test.ts | 30 +- tests/ECS/Core/DecoratorSystem.test.ts | 23 +- tests/ECS/Core/EntityManager.test.ts | 44 +- tests/ECS/Core/FluentAPI.test.ts | 22 +- tests/ECS/Core/QuerySystem.test.ts | 44 +- tests/ECS/Core/SystemInitializeIssue.test.ts | 24 +- .../ECS/Core/SystemMultipleInitialize.test.ts | 8 +- tests/ECS/Core/SystemTimingIssue.test.ts | 22 +- tests/ECS/Entity.test.ts | 12 +- tests/ECS/Scene.test.ts | 28 +- tests/ECS/Systems/SystemTypes.test.ts | 12 +- tests/ECS/Utils/Matcher.test.ts | 403 +++++++----------- tests/Utils/Extensions/TypeUtils.test.ts | 45 +- tests/performance/Matcher.performance.test.ts | 24 +- 16 files changed, 460 insertions(+), 297 deletions(-) diff --git a/tests/Core.test.ts b/tests/Core.test.ts index e919dea2..726ff8f5 100644 --- a/tests/Core.test.ts +++ b/tests/Core.test.ts @@ -7,8 +7,12 @@ import { ITimer } from '../src/Utils/Timers/ITimer'; // 测试组件 class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number; + + constructor(...args: unknown[]) { super(); + const [value = 0] = args as [number?]; + this.value = value; } } diff --git a/tests/ECS/Core/ComponentIndexManager.test.ts b/tests/ECS/Core/ComponentIndexManager.test.ts index b7254d22..27e2550e 100644 --- a/tests/ECS/Core/ComponentIndexManager.test.ts +++ b/tests/ECS/Core/ComponentIndexManager.test.ts @@ -5,14 +5,20 @@ import { Component } from '../../../src/ECS/Component'; // 测试用组件 class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number = 0; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.value = args[0] as number; } } class AnotherTestComponent extends Component { - constructor(public name: string = 'test') { + public name: string = 'test'; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.name = args[0] as string; } } diff --git a/tests/ECS/Core/ComponentStorage.test.ts b/tests/ECS/Core/ComponentStorage.test.ts index 60b4f3a7..b1f9e1f9 100644 --- a/tests/ECS/Core/ComponentStorage.test.ts +++ b/tests/ECS/Core/ComponentStorage.test.ts @@ -9,26 +9,48 @@ import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; // 测试组件类(默认使用原始存储) class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number; + + constructor(...args: unknown[]) { super(); + const [value = 0] = args as [number?]; + this.value = value; } } class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class HealthComponent extends Component { - constructor(public health: number = 100, public maxHealth: number = 100) { + public health: number; + public maxHealth: number; + + constructor(...args: unknown[]) { super(); + const [health = 100, maxHealth = 100] = args as [number?, number?]; + this.health = health; + this.maxHealth = maxHealth; } } diff --git a/tests/ECS/Core/DecoratorSystem.test.ts b/tests/ECS/Core/DecoratorSystem.test.ts index 4e2df6e5..6e7b1e2e 100644 --- a/tests/ECS/Core/DecoratorSystem.test.ts +++ b/tests/ECS/Core/DecoratorSystem.test.ts @@ -7,20 +7,37 @@ import { EventBus } from '../../../src/ECS/Core/EventBus'; // 测试组件 class TransformComponent extends Component { - constructor(public x: number = 0, public y: number = 0, public rotation: number = 0) { + public x: number = 0; + public y: number = 0; + public rotation: number = 0; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.x = args[0] as number; + if (args.length >= 2) this.y = args[1] as number; + if (args.length >= 3) this.rotation = args[2] as number; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number = 0; + public vy: number = 0; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.vx = args[0] as number; + if (args.length >= 2) this.vy = args[1] as number; } } class HealthComponent extends Component { - constructor(public health: number = 100, public maxHealth: number = 100) { + public health: number = 100; + public maxHealth: number = 100; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.health = args[0] as number; + if (args.length >= 2) this.maxHealth = args[1] as number; } } diff --git a/tests/ECS/Core/EntityManager.test.ts b/tests/ECS/Core/EntityManager.test.ts index dd502824..a633d738 100644 --- a/tests/ECS/Core/EntityManager.test.ts +++ b/tests/ECS/Core/EntityManager.test.ts @@ -4,38 +4,70 @@ import { Component } from '../../../src/ECS/Component'; // 测试组件 class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class HealthComponent extends Component { - constructor(public health: number = 100, public maxHealth: number = 100) { + public health: number; + public maxHealth: number; + + constructor(...args: unknown[]) { super(); + const [health = 100, maxHealth = 100] = args as [number?, number?]; + this.health = health; + this.maxHealth = maxHealth; } } class RenderComponent extends Component { - constructor(public visible: boolean = true, public color: string = 'white') { + public visible: boolean; + public color: string; + + constructor(...args: unknown[]) { super(); + const [visible = true, color = 'white'] = args as [boolean?, string?]; + this.visible = visible; + this.color = color; } } class AIComponent extends Component { - constructor(public intelligence: number = 50) { + public intelligence: number; + + constructor(...args: unknown[]) { super(); + const [intelligence = 50] = args as [number?]; + this.intelligence = intelligence; } } class PlayerComponent extends Component { - constructor(public name: string = 'Player') { + public name: string; + + constructor(...args: unknown[]) { super(); + const [name = 'Player'] = args as [string?]; + this.name = name; } } diff --git a/tests/ECS/Core/FluentAPI.test.ts b/tests/ECS/Core/FluentAPI.test.ts index 73e2d975..4a98d2a5 100644 --- a/tests/ECS/Core/FluentAPI.test.ts +++ b/tests/ECS/Core/FluentAPI.test.ts @@ -18,20 +18,36 @@ import { Matcher } from '../../../src/ECS/Utils/Matcher'; // 测试组件 class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number; + + constructor(...args: unknown[]) { super(); + const [value = 0] = args as [number?]; + this.value = value; } } class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } diff --git a/tests/ECS/Core/QuerySystem.test.ts b/tests/ECS/Core/QuerySystem.test.ts index ca2d8c1c..5f766876 100644 --- a/tests/ECS/Core/QuerySystem.test.ts +++ b/tests/ECS/Core/QuerySystem.test.ts @@ -5,38 +5,70 @@ import { ComponentRegistry, ComponentType } from '../../../src/ECS/Core/Componen // 测试组件 class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class HealthComponent extends Component { - constructor(public health: number = 100, public maxHealth: number = 100) { + public health: number; + public maxHealth: number; + + constructor(...args: unknown[]) { super(); + const [health = 100, maxHealth = 100] = args as [number?, number?]; + this.health = health; + this.maxHealth = maxHealth; } } class RenderComponent extends Component { - constructor(public visible: boolean = true, public layer: number = 0) { + public visible: boolean; + public layer: number; + + constructor(...args: unknown[]) { super(); + const [visible = true, layer = 0] = args as [boolean?, number?]; + this.visible = visible; + this.layer = layer; } } class AIComponent extends Component { - constructor(public behavior: string = 'idle') { + public behavior: string; + + constructor(...args: unknown[]) { super(); + const [behavior = 'idle'] = args as [string?]; + this.behavior = behavior; } } class PhysicsComponent extends Component { - constructor(public mass: number = 1.0) { + public mass: number; + + constructor(...args: unknown[]) { super(); + const [mass = 1.0] = args as [number?]; + this.mass = mass; } } diff --git a/tests/ECS/Core/SystemInitializeIssue.test.ts b/tests/ECS/Core/SystemInitializeIssue.test.ts index fb3cc5f1..d6dd6520 100644 --- a/tests/ECS/Core/SystemInitializeIssue.test.ts +++ b/tests/ECS/Core/SystemInitializeIssue.test.ts @@ -6,26 +6,42 @@ import { Matcher } from '../../../src/ECS/Utils/Matcher'; // 测试组件 class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + this.x = (args[0] as number) ?? 0; + this.y = (args[1] as number) ?? 0; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + this.vx = (args[0] as number) ?? 0; + this.vy = (args[1] as number) ?? 0; } } class HealthComponent extends Component { - constructor(public health: number = 100) { + public health: number; + + constructor(...args: unknown[]) { super(); + this.health = (args[0] as number) ?? 100; } } class TagComponent extends Component { - constructor(public tag: string = '') { + public tag: string; + + constructor(...args: unknown[]) { super(); + this.tag = (args[0] as string) ?? ''; } } diff --git a/tests/ECS/Core/SystemMultipleInitialize.test.ts b/tests/ECS/Core/SystemMultipleInitialize.test.ts index de60c61d..74a67f19 100644 --- a/tests/ECS/Core/SystemMultipleInitialize.test.ts +++ b/tests/ECS/Core/SystemMultipleInitialize.test.ts @@ -5,8 +5,12 @@ import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem'; import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number = 0; + + constructor(...args: unknown[]) { super(); + const [value = 0] = args as [number?]; + this.value = value; } } @@ -26,7 +30,7 @@ class TrackingSystem extends EntitySystem { } } - public override onChanged(entity: Entity): void { + public onChanged(entity: Entity): void { this.onChangedCallCount++; if (this.isInterestedEntity(entity)) { if (!this.trackedEntities.includes(entity)) { diff --git a/tests/ECS/Core/SystemTimingIssue.test.ts b/tests/ECS/Core/SystemTimingIssue.test.ts index 3ea2cd71..9c5286ef 100644 --- a/tests/ECS/Core/SystemTimingIssue.test.ts +++ b/tests/ECS/Core/SystemTimingIssue.test.ts @@ -6,20 +6,36 @@ import { Matcher } from '../../../src/ECS/Utils/Matcher'; // 测试组件 class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number = 0; + public y: number = 0; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number = 0; + public vy: number = 0; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class HealthComponent extends Component { - constructor(public health: number = 100) { + public health: number = 100; + + constructor(...args: unknown[]) { super(); + const [health = 100] = args as [number?]; + this.health = health; } } diff --git a/tests/ECS/Entity.test.ts b/tests/ECS/Entity.test.ts index 363ce174..24704e20 100644 --- a/tests/ECS/Entity.test.ts +++ b/tests/ECS/Entity.test.ts @@ -6,8 +6,9 @@ class TestPositionComponent extends Component { public x: number = 0; public y: number = 0; - constructor(x: number = 0, y: number = 0) { + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; this.x = x; this.y = y; } @@ -16,8 +17,9 @@ class TestPositionComponent extends Component { class TestHealthComponent extends Component { public health: number = 100; - constructor(health: number = 100) { + constructor(...args: unknown[]) { super(); + const [health = 100] = args as [number?]; this.health = health; } } @@ -26,8 +28,9 @@ class TestVelocityComponent extends Component { public vx: number = 0; public vy: number = 0; - constructor(vx: number = 0, vy: number = 0) { + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; this.vx = vx; this.vy = vy; } @@ -36,8 +39,9 @@ class TestVelocityComponent extends Component { class TestRenderComponent extends Component { public visible: boolean = true; - constructor(visible: boolean = true) { + constructor(...args: unknown[]) { super(); + const [visible = true] = args as [boolean?]; this.visible = visible; } } diff --git a/tests/ECS/Scene.test.ts b/tests/ECS/Scene.test.ts index 918af2e8..17211100 100644 --- a/tests/ECS/Scene.test.ts +++ b/tests/ECS/Scene.test.ts @@ -6,26 +6,46 @@ import { Matcher } from '../../src/ECS/Utils/Matcher'; // 测试组件 class PositionComponent extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number; + public y: number; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class VelocityComponent extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number; + public vy: number; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class HealthComponent extends Component { - constructor(public health: number = 100) { + public health: number; + + constructor(...args: unknown[]) { super(); + const [health = 100] = args as [number?]; + this.health = health; } } class RenderComponent extends Component { - constructor(public visible: boolean = true) { + public visible: boolean; + + constructor(...args: unknown[]) { super(); + const [visible = true] = args as [boolean?]; + this.visible = visible; } } diff --git a/tests/ECS/Systems/SystemTypes.test.ts b/tests/ECS/Systems/SystemTypes.test.ts index 50d878ee..18ac9c69 100644 --- a/tests/ECS/Systems/SystemTypes.test.ts +++ b/tests/ECS/Systems/SystemTypes.test.ts @@ -9,14 +9,22 @@ import { Matcher } from '../../../src/ECS/Utils/Matcher'; // 测试组件 class TestComponent extends Component { - constructor(public value: number = 0) { + public value: number = 0; + + constructor(...args: unknown[]) { super(); + const [value = 0] = args as [number?]; + this.value = value; } } class AnotherComponent extends Component { - constructor(public name: string = 'test') { + public name: string = 'test'; + + constructor(...args: unknown[]) { super(); + const [name = 'test'] = args as [string?]; + this.name = name; } } diff --git a/tests/ECS/Utils/Matcher.test.ts b/tests/ECS/Utils/Matcher.test.ts index 3a54c586..117512ef 100644 --- a/tests/ECS/Utils/Matcher.test.ts +++ b/tests/ECS/Utils/Matcher.test.ts @@ -1,6 +1,6 @@ /** * Matcher完整测试套件 - * 包含功能测试、性能测试和向后兼容性测试 + * 测试新的Matcher条件构建功能和QuerySystem集成 */ import { Scene } from '../../../src/ECS/Scene'; @@ -10,20 +10,36 @@ import { Matcher } from '../../../src/ECS/Utils/Matcher'; // 测试组件 class Position extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number = 0; + public y: number = 0; + + constructor(...args: unknown[]) { super(); + const [x = 0, y = 0] = args as [number?, number?]; + this.x = x; + this.y = y; } } class Velocity extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number = 0; + public vy: number = 0; + + constructor(...args: unknown[]) { super(); + const [vx = 0, vy = 0] = args as [number?, number?]; + this.vx = vx; + this.vy = vy; } } class Health extends Component { - constructor(public hp: number = 100) { + public hp: number = 100; + + constructor(...args: unknown[]) { super(); + const [hp = 100] = args as [number?]; + this.hp = hp; } } @@ -71,293 +87,202 @@ describe('Matcher测试套件', () => { scene.end(); }); - describe('新API测试', () => { - test('create()应该创建有效的matcher', () => { - const matcher = Matcher.create(scene.querySystem); - expect(matcher).toBeInstanceOf(Matcher); - }); - - test('all()查询应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position, Health); + describe('Matcher条件构建测试', () => { + test('Matcher.all()应该创建正确的查询条件', () => { + const matcher = Matcher.all(Position, Health); + const condition = matcher.getCondition(); - const result = matcher.query(); - expect(result).toHaveLength(2); - expect(result.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']); + expect(condition.all).toContain(Position); + expect(condition.all).toContain(Health); + expect(condition.all.length).toBe(2); }); - test('any()查询应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .any(Health, Dead); + test('Matcher.any()应该创建正确的查询条件', () => { + const matcher = Matcher.any(Health, Dead); + const condition = matcher.getCondition(); - const result = matcher.query(); - expect(result).toHaveLength(4); // 所有实体 + expect(condition.any).toContain(Health); + expect(condition.any).toContain(Dead); + expect(condition.any.length).toBe(2); }); - test('none()查询应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position) - .none(Dead); + test('Matcher.none()应该创建正确的查询条件', () => { + const matcher = Matcher.none(Dead); + const condition = matcher.getCondition(); - const result = matcher.query(); - expect(result).toHaveLength(2); - expect(result.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']); + expect(condition.none).toContain(Dead); + expect(condition.none.length).toBe(1); }); - test('复合查询应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position) + test('链式调用应该正确工作', () => { + const matcher = Matcher.all(Position) .any(Health, Velocity) .none(Dead); - const result = matcher.query(); - expect(result).toHaveLength(2); - expect(result.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']); + const condition = matcher.getCondition(); + expect(condition.all).toContain(Position); + expect(condition.any).toContain(Health); + expect(condition.any).toContain(Velocity); + expect(condition.none).toContain(Dead); }); - test('matches()应该正确检查单个实体', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position, Velocity); + test('byComponent()应该创建单组件查询条件', () => { + const matcher = Matcher.byComponent(Position); + const condition = matcher.getCondition(); - expect(matcher.matches(entities[0])).toBe(true); // MovingAlive - expect(matcher.matches(entities[1])).toBe(false); // StillAlive - expect(matcher.matches(entities[2])).toBe(true); // MovingDead - expect(matcher.matches(entities[3])).toBe(false); // StillDead + expect(condition.component).toBe(Position); }); - test('count()和exists()应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Health); + test('byTag()应该创建标签查询条件', () => { + const matcher = Matcher.byTag(123); + const condition = matcher.getCondition(); - expect(matcher.count()).toBe(2); - expect(matcher.exists()).toBe(true); - - const emptyMatcher = Matcher.create(scene.querySystem) - .all(Health, Dead); - - expect(emptyMatcher.count()).toBe(0); - expect(emptyMatcher.exists()).toBe(false); + expect(condition.tag).toBe(123); }); - test('clone()应该创建独立的matcher', () => { - const baseMatcher = Matcher.create(scene.querySystem) - .all(Position); + test('byName()应该创建名称查询条件', () => { + const matcher = Matcher.byName('TestEntity'); + const condition = matcher.getCondition(); - const livingMatcher = baseMatcher.clone() - .all(Health) - .none(Dead); - - const deadMatcher = baseMatcher.clone() - .all(Dead); - - expect(livingMatcher.count()).toBe(2); - expect(deadMatcher.count()).toBe(2); - expect(baseMatcher.count()).toBe(4); // 原始matcher不受影响 - }); - - test('reset()应该清空所有条件', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position) - .any(Health) - .none(Dead); - - expect(matcher.count()).toBe(2); - - matcher.reset(); - expect(matcher.count()).toBe(4); // 所有实体 + expect(condition.name).toBe('TestEntity'); }); }); - - describe('向后兼容性测试', () => { - test('empty()和withQuerySystem()应该正常工作', () => { - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - const matcher = Matcher.empty() - .all(Position, Health) - .withQuerySystem(scene.querySystem); - - const result = matcher.query(); - expect(result).toHaveLength(2); - - // 应该有deprecation警告 - expect(consoleSpy).toHaveBeenCalledWith( - 'withQuerySystem() is deprecated. Use Matcher.create(querySystem) instead.' - ); - - consoleSpy.mockRestore(); + + describe('QuerySystem集成测试', () => { + test('使用QuerySystem的queryAll()查询所有匹配实体', () => { + const result = scene.querySystem.queryAll(Position, Health); + expect(result.entities.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']); }); - test('deprecated方法应该工作并显示警告', () => { - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - const matcher = Matcher.empty() - .all(Position) - .withQuerySystem(scene.querySystem); - - // 测试deprecated方法 - expect(matcher.isInterestedEntity(entities[0])).toBe(true); - const result = matcher.queryEntities(); - expect(result).toHaveLength(4); - - // 测试getter方法 - expect(matcher.getAllSet()).toEqual([Position]); - expect(matcher.getExclusionSet()).toEqual([]); - expect(matcher.getOneSet()).toEqual([]); - - // 验证警告 - expect(consoleSpy).toHaveBeenCalledWith( - 'isInterestedEntity() is deprecated. Use matches() instead.' - ); - expect(consoleSpy).toHaveBeenCalledWith( - 'queryEntities() is deprecated. Use query() instead.' - ); - - consoleSpy.mockRestore(); + test('使用QuerySystem的queryAny()查询任一匹配实体', () => { + const result = scene.querySystem.queryAny(Health, Dead); + expect(result.entities.length).toBe(4); // 所有实体都有Health或Dead }); - test('无QuerySystem时应该抛出错误', () => { - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - const matcher = Matcher.empty() - .all(Position, Health); - - // 应该抛出错误而不是回退 - expect(() => matcher.matches(entities[0])).toThrow( - 'Matcher requires QuerySystem. Use Matcher.create(querySystem) or call withQuerySystem() first.' - ); - - expect(() => matcher.query()).toThrow( - 'Matcher requires QuerySystem. Use Matcher.create(querySystem) or call withQuerySystem() first.' - ); - - consoleSpy.mockRestore(); + test('使用QuerySystem的queryNone()查询排除实体', () => { + const result = scene.querySystem.queryNone(Dead); + const aliveEntities = result.entities.filter(e => e.hasComponent(Position)); + expect(aliveEntities.map(e => e.name).sort()).toEqual(['MovingAlive', 'StillAlive']); }); - test('新旧API应该产生相同结果', () => { - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); + test('QuerySystem查询性能统计', () => { + scene.querySystem.queryAll(Position, Velocity); + const stats = scene.querySystem.getStats(); - // 旧API - const oldMatcher = Matcher.empty() - .all(Position) - .exclude(Dead) - .withQuerySystem(scene.querySystem); + expect(stats.queryStats.totalQueries).toBeGreaterThan(0); + expect(stats.queryStats.cacheHits).toBeGreaterThanOrEqual(0); + }); + }); + + describe('实际使用场景测试', () => { + test('游戏系统中的移动实体查询', () => { + // 查询所有可移动的实体(有位置和速度的) + const movableEntities = scene.querySystem.queryAll(Position, Velocity); + expect(movableEntities.entities.length).toBe(2); // MovingAlive, MovingDead - // 新API - const newMatcher = Matcher.create(scene.querySystem) - .all(Position) - .none(Dead); + movableEntities.entities.forEach(entity => { + const pos = entity.getComponent(Position)!; + const vel = entity.getComponent(Velocity)!; + expect(pos).toBeDefined(); + expect(vel).toBeDefined(); + }); + }); + + test('游戏系统中的活体实体查询', () => { + // 查询所有活体实体(有血量,没有死亡标记的) + const aliveEntitiesAll = scene.querySystem.queryAll(Health); + const deadEntitiesAll = scene.querySystem.queryAll(Dead); - // 结果应该相同 - const oldResult = oldMatcher.query().sort((a, b) => a.id - b.id); - const newResult = newMatcher.query().sort((a, b) => a.id - b.id); + expect(aliveEntitiesAll.entities.length).toBe(2); // MovingAlive, StillAlive + expect(deadEntitiesAll.entities.length).toBe(2); // MovingDead, StillDead + }); + + test('复杂查询:查找活着的移动实体', () => { + // 首先获取所有有位置和速度的实体 + const movableEntities = scene.querySystem.queryAll(Position, Velocity); - expect(oldResult).toEqual(newResult); + // 然后过滤出活着的(有血量的) + const aliveMovableEntities = movableEntities.entities.filter(entity => + entity.hasComponent(Health) + ); - // 单个实体检查也应该相同 - for (const entity of entities) { - expect(oldMatcher.matches(entity)).toBe(newMatcher.matches(entity)); + expect(aliveMovableEntities.length).toBe(1); // 只有MovingAlive + expect(aliveMovableEntities[0].name).toBe('MovingAlive'); + }); + + test('复合查询条件应用', () => { + // 使用Matcher建立复杂条件,然后用QuerySystem执行 + const matcher = Matcher.all(Position).any(Health, Dead); + const condition = matcher.getCondition(); + + // 这里演示如何用条件,实际执行需要QuerySystem支持复合条件 + expect(condition.all).toContain(Position); + expect(condition.any).toContain(Health); + expect(condition.any).toContain(Dead); + }); + }); + + describe('性能测试', () => { + test('大量简单查询的性能', () => { + const startTime = performance.now(); + + for (let i = 0; i < 1000; i++) { + scene.querySystem.queryAll(Position); } - consoleSpy.mockRestore(); - }); - }); - - describe('缓存机制测试', () => { - test('条件变更应该使缓存失效', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position); - - const result1 = matcher.query(); - - // 添加条件 - matcher.all(Health); - const result2 = matcher.query(); - - // 结果应该不同 - expect(result2.length).toBeLessThan(result1.length); + const executionTime = performance.now() - startTime; + expect(executionTime).toBeLessThan(100); // 应该在100ms内完成 }); - test('QuerySystem版本变更应该使缓存失效', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position); + test('复杂查询的性能', () => { + const startTime = performance.now(); - const result1 = matcher.query(); + for (let i = 0; i < 100; i++) { + scene.querySystem.queryAll(Position, Health); + scene.querySystem.queryAny(Health, Dead); + scene.querySystem.queryNone(Dead); + } - // 添加新实体触发版本变更 - const newEntity = scene.createEntity('NewEntity'); - newEntity.addComponent(new Position(100, 100)); - - const result2 = matcher.query(); - - // 结果应该包含新实体 - expect(result2.length).toBe(result1.length + 1); + const executionTime = performance.now() - startTime; + expect(executionTime).toBeLessThan(50); }); - test('重复查询应该使用缓存', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position); + test('不存在组件的查询性能', () => { + class NonExistentComponent extends Component { + constructor(...args: unknown[]) { + super(); + } + } - const result1 = matcher.query(); - const result2 = matcher.query(); - - // 结果应该相同(功能测试,不测性能) - expect(result1).toEqual(result2); + const result = scene.querySystem.queryAll(NonExistentComponent); + expect(result.entities.length).toBe(0); }); }); - + describe('边界情况测试', () => { - test('空条件应该返回所有实体', () => { - const matcher = Matcher.create(scene.querySystem); - const result = matcher.query(); - expect(result.length).toBeGreaterThan(0); + test('空查询应该返回所有实体', () => { + const result = scene.querySystem.queryAll(); + expect(result.entities.length).toBe(entities.length); }); - test('不存在的组件查询应该返回空结果', () => { - class NonExistentComponent extends Component {} + test('查询不存在的组件应该返回空结果', () => { + class NonExistentComponent extends Component { + constructor(...args: unknown[]) { + super(); + } + } - const matcher = Matcher.create(scene.querySystem) - .all(NonExistentComponent); - - expect(matcher.query()).toEqual([]); - expect(matcher.count()).toBe(0); - expect(matcher.exists()).toBe(false); + const result = scene.querySystem.queryAll(NonExistentComponent); + expect(result.entities.length).toBe(0); }); - test('复杂的排除条件应该正确工作', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position) - .none(Health, Dead); // 排除有血量或死亡的 + test('Matcher条件构建的边界情况', () => { + const emptyMatcher = Matcher.complex(); + const condition = emptyMatcher.getCondition(); - // 应该没有结果,因为所有有Position的实体都有Health或Dead - expect(matcher.query()).toEqual([]); - }); - - test('toString()应该提供有用的描述', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position, Health) - .any(Velocity) - .none(Dead); - - const description = matcher.toString(); - expect(description).toContain('all(Position, Health)'); - expect(description).toContain('any(Velocity)'); - expect(description).toContain('none(Dead)'); - }); - - test('getCondition()应该返回只读条件', () => { - const matcher = Matcher.create(scene.querySystem) - .all(Position) - .any(Health) - .none(Dead); - - const condition = matcher.getCondition(); - expect(condition.all).toEqual([Position]); - expect(condition.any).toEqual([Health]); - expect(condition.none).toEqual([Dead]); - - // 修改返回的条件不应该影响原matcher - condition.all.push(Velocity as any); - expect(matcher.getCondition().all).toEqual([Position]); + expect(condition.all.length).toBe(0); + expect(condition.any.length).toBe(0); + expect(condition.none.length).toBe(0); }); }); }); \ No newline at end of file diff --git a/tests/Utils/Extensions/TypeUtils.test.ts b/tests/Utils/Extensions/TypeUtils.test.ts index 1313ea98..c37a4421 100644 --- a/tests/Utils/Extensions/TypeUtils.test.ts +++ b/tests/Utils/Extensions/TypeUtils.test.ts @@ -3,11 +3,19 @@ import { TypeUtils } from '../../../src/Utils/Extensions/TypeUtils'; describe('TypeUtils - 类型工具类测试', () => { // 测试用的类和对象 class TestClass { - constructor(public value: number = 0) {} + public value: number = 0; + + constructor(...args: unknown[]) { + if (args.length >= 1) this.value = args[0] as number; + } } class AnotherTestClass { - constructor(public name: string = '') {} + public name: string = ''; + + constructor(...args: unknown[]) { + if (args.length >= 1) this.name = args[0] as string; + } } function TestFunction() { @@ -106,12 +114,19 @@ describe('TypeUtils - 类型工具类测试', () => { it('应该处理继承关系', () => { class Parent { - constructor(public value: number = 0) {} + public value: number = 0; + + constructor(...args: unknown[]) { + if (args.length >= 1) this.value = args[0] as number; + } } class Child extends Parent { - constructor(value: number = 0, public name: string = '') { - super(value); + public name: string = ''; + + constructor(...args: unknown[]) { + super(args[0]); + if (args.length >= 2) this.name = args[1] as string; } } @@ -232,18 +247,28 @@ describe('TypeUtils - 类型工具类测试', () => { it('应该能够用于多态类型识别', () => { class Animal { - constructor(public name: string) {} + public name: string = ''; + + constructor(...args: unknown[]) { + if (args.length >= 1) this.name = args[0] as string; + } } class Dog extends Animal { - constructor(name: string, public breed: string) { - super(name); + public breed: string = ''; + + constructor(...args: unknown[]) { + super(args[0]); + if (args.length >= 2) this.breed = args[1] as string; } } class Cat extends Animal { - constructor(name: string, public color: string) { - super(name); + public color: string = ''; + + constructor(...args: unknown[]) { + super(args[0]); + if (args.length >= 2) this.color = args[1] as string; } } diff --git a/tests/performance/Matcher.performance.test.ts b/tests/performance/Matcher.performance.test.ts index b65ac758..0bd50828 100644 --- a/tests/performance/Matcher.performance.test.ts +++ b/tests/performance/Matcher.performance.test.ts @@ -11,26 +11,42 @@ import { Matcher } from '../../src/ECS/Utils/Matcher'; // 测试组件 class Position extends Component { - constructor(public x: number = 0, public y: number = 0) { + public x: number = 0; + public y: number = 0; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.x = args[0] as number; + if (args.length >= 2) this.y = args[1] as number; } } class Velocity extends Component { - constructor(public vx: number = 0, public vy: number = 0) { + public vx: number = 0; + public vy: number = 0; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.vx = args[0] as number; + if (args.length >= 2) this.vy = args[1] as number; } } class Health extends Component { - constructor(public hp: number = 100) { + public hp: number = 100; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.hp = args[0] as number; } } class Weapon extends Component { - constructor(public damage: number = 10) { + public damage: number = 10; + + constructor(...args: unknown[]) { super(); + if (args.length >= 1) this.damage = args[0] as number; } }