104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { Archetype } from "../archetype/archetype.js";
|
|
import { BitSet } from "../bitset/bitset.js";
|
|
import { ComponentRegistry } from "../component-registry/component-registry.js";
|
|
import { defineComponent } from "../schema/schema.js";
|
|
import { defineQuery, Query } from "./query.js";
|
|
|
|
const Position = defineComponent("Position", { x: 0 });
|
|
const Velocity = defineComponent("Velocity", { dx: 0 });
|
|
const Disabled = defineComponent("Disabled", { d: true });
|
|
|
|
function makeArch(
|
|
registry: ComponentRegistry,
|
|
schemas: (typeof Position)[]
|
|
): Archetype {
|
|
const mask = new BitSet(registry.maxComponents);
|
|
const ids: number[] = [];
|
|
for (const s of schemas) {
|
|
const id = registry.getId(s);
|
|
mask.set(id);
|
|
ids.push(id);
|
|
}
|
|
ids.sort((a, b) => a - b);
|
|
return new Archetype(mask, ids);
|
|
}
|
|
|
|
describe("Query", () => {
|
|
it("include-only matches every superset archetype", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new ComponentRegistry();
|
|
reg.register(Position);
|
|
reg.register(Velocity);
|
|
const arch1 = makeArch(reg, [Position]);
|
|
const arch2 = makeArch(reg, [Position, Velocity]);
|
|
const arch3 = makeArch(reg, [Velocity]);
|
|
for (const id of [10, 11]) arch1.addEntity(id, new Map());
|
|
arch2.addEntity(20, new Map());
|
|
arch3.addEntity(30, new Map());
|
|
|
|
const q = new Query(defineQuery([Position]), reg);
|
|
const result = q.evaluate([arch1, arch2, arch3]);
|
|
expect([...result].sort((a, b) => a - b)).toEqual([10, 11, 20]);
|
|
});
|
|
|
|
it("exclude filters out archetypes whose mask intersects the exclude mask", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new ComponentRegistry();
|
|
reg.register(Position);
|
|
reg.register(Disabled);
|
|
const archEnabled = makeArch(reg, [Position]);
|
|
const archDisabled = makeArch(reg, [Position, Disabled]);
|
|
archEnabled.addEntity(1, new Map());
|
|
archDisabled.addEntity(2, new Map());
|
|
|
|
const q = new Query(defineQuery([Position], [Disabled]), reg);
|
|
const result = q.evaluate([archEnabled, archDisabled]);
|
|
expect([...result]).toEqual([1]);
|
|
});
|
|
|
|
it("invalidate clears the cached match list and forces a re-scan", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new ComponentRegistry();
|
|
reg.register(Position);
|
|
reg.register(Velocity);
|
|
const arch1 = makeArch(reg, [Position]);
|
|
arch1.addEntity(1, new Map());
|
|
|
|
const archetypes: Archetype[] = [arch1];
|
|
const q = new Query(defineQuery([Position]), reg);
|
|
expect(q.evaluate(archetypes)).toEqual([1]);
|
|
|
|
const arch2 = makeArch(reg, [Position, Velocity]);
|
|
arch2.addEntity(2, new Map());
|
|
archetypes.push(arch2);
|
|
|
|
// Without invalidate, the new archetype is not reconsidered.
|
|
expect(q.evaluate(archetypes)).toEqual([1]);
|
|
q.invalidate();
|
|
expect([...q.evaluate(archetypes)].sort()).toEqual([1, 2]);
|
|
});
|
|
|
|
it("defineQuery omits exclude when not supplied", { timeout: 5000 }, () => {
|
|
const descriptor = defineQuery([Position]);
|
|
expect(descriptor.exclude).toBeUndefined();
|
|
});
|
|
|
|
it("defineQuery includes exclude when provided", { timeout: 5000 }, () => {
|
|
const descriptor = defineQuery([Position], [Disabled]);
|
|
expect(descriptor.exclude).toEqual([Disabled]);
|
|
});
|
|
|
|
it("empty exclude array is treated as no exclude", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const reg = new ComponentRegistry();
|
|
reg.register(Position);
|
|
const q = new Query({ include: [Position], exclude: [] }, reg);
|
|
expect(q.excludeMask).toBeNull();
|
|
});
|
|
});
|