77 lines
4.8 KiB
JavaScript
77 lines
4.8 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("assert");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const vm = require("vm");
|
|
const ROOT = path.resolve(__dirname, "..");
|
|
const read = rel => fs.readFileSync(path.join(ROOT, rel), "utf8");
|
|
|
|
const render = read("js/render.js");
|
|
assert(!render.includes("insertionSortNearPrevious"), "custom insertion sort still exists");
|
|
assert(!render.includes("_renderPrevDynamicLayeredIds") && !render.includes("_renderBackSortMap"), "previous-order render bookkeeping still exists");
|
|
assert(render.includes("dynamicBack.sort(compareBackItems)") && render.includes("dynamicLayered.sort(compareRenderEntries)"), "native render sort is not active");
|
|
console.log("[OK] previous-order Map and custom insertion sort are removed");
|
|
|
|
const pathfinding = read("js/world_pathfinding_system.js");
|
|
assert(!pathfinding.includes("_sharedRouteCache") && !pathfinding.includes("trySharedRoute") && !pathfinding.includes("storeSharedRoute"), "shared route cache still exists");
|
|
assert(pathfinding.includes("actor._routeCache"), "actor-local route cache was removed accidentally");
|
|
console.log("[OK] shared route cache is removed while actor-local throttling remains");
|
|
|
|
const profiler = read("js/perf_profiler.js");
|
|
const budget = read("js/world_spatial_budget.js");
|
|
assert(profiler.includes("diagnosticsEnabled"), "diagnostic-mode gate is missing");
|
|
assert(budget.includes("diagnostics ?") && budget.includes("this.workStats = diagnostics ?"), "production detailed diagnostic allocation is still unconditional");
|
|
assert(profiler.includes("transitionHoldSeconds") && profiler.includes("pendingProfileSeconds"), "performance-profile hysteresis hold timers are missing");
|
|
console.log("[OK] production diagnostics are gated and auto profile switching uses hysteresis");
|
|
|
|
const environment = read("js/world_environment.js");
|
|
assert(environment.includes("`${this.routingObstacleVersion || 0}:${qx},${qy},${qr},${cacheLimit},${maxRects}`"), "obstacle cache is not keyed by routingObstacleVersion only");
|
|
assert(!environment.includes("`${this.spatialVersion || 0}:${this.spatialDirtyMarksTotal || 0}:${qx}"), "obstacle cache still invalidates on generic spatial movement");
|
|
console.log("[OK] obstacle cache invalidation is isolated from generic spatial movement");
|
|
|
|
const simCore = read("js/sim_core.js");
|
|
const start = simCore.indexOf("class SpatialGrid {");
|
|
const end = simCore.indexOf("\nfunction drawHeartShape", start);
|
|
assert(start >= 0 && end > start, "SpatialGrid source could not be isolated");
|
|
const context = { console, Map, Set, Math, Number, Array, globalThis: {} };
|
|
vm.createContext(context);
|
|
vm.runInContext(`${simCore.slice(start, end)}\nglobalThis.SpatialGrid = SpatialGrid;`, context);
|
|
const SpatialGrid = context.globalThis.SpatialGrid;
|
|
const grid = new SpatialGrid(100);
|
|
const a = { id: "a", x: 10, y: 10, dead: false };
|
|
const b = { id: "b", x: 40, y: 40, dead: false };
|
|
grid.rebuildTarinai([a, b]);
|
|
const firstKey = a._spatialTarinaiCellKey;
|
|
a.x = 50;
|
|
assert.strictEqual(grid.syncTarinai([a, b]), 0, "same-cell movement should not rewrite the grid");
|
|
a.x = 150;
|
|
assert.strictEqual(grid.syncTarinai([a, b]), 1, "cell crossing should update exactly one entity");
|
|
assert.notStrictEqual(a._spatialTarinaiCellKey, firstKey, "cell key did not change after crossing");
|
|
b.dead = true;
|
|
assert(grid.syncTarinai([a, b]) >= 1, "dead entity was not removed incrementally");
|
|
assert(!grid._indexedTarinai.has(b), "dead entity remains in the Tarinai spatial index");
|
|
console.log("[OK] Tarinai spatial grid updates only cell crossings/removals instead of full rebuilds");
|
|
|
|
class World {}
|
|
global.World = World;
|
|
global.TarinaiHistory = { trimMemory() { return 0; } };
|
|
require(path.join(ROOT, "js", "world_spatial_budget.js"));
|
|
const world = new World();
|
|
world.time = 100;
|
|
world.tarinai = Array.from({ length: 120 }, (_, i) => ({ id: `T${i}`, dead: false, relationships: { D: { affinity: 1 } } }));
|
|
world.liveTarinai = new Map(world.tarinai.map(t => [t.id, { target: t }]));
|
|
world._deadTarinaiIdsPendingCleanup = new Set(["D"]);
|
|
world.tarinaiCollisionMemo = new Map();
|
|
world.relationNotices = {};
|
|
world.fightPairCooldowns = {};
|
|
world.resolvedFightIds = {};
|
|
assert(world.queueTarinaiRuntimeCachePrune("audit", { resetRelationPeerCaches: true }), "cleanup job was not queued");
|
|
world.processTarinaiRuntimeCachePruneStep(24);
|
|
assert(world._tarinaiRuntimePruneJob, "cleanup finished in one frame instead of being chunked");
|
|
assert.strictEqual(world._tarinaiRuntimePruneJob.cursor, 24, "cleanup chunk size was not respected");
|
|
while (world._tarinaiRuntimePruneJob) world.processTarinaiRuntimeCachePruneStep(24);
|
|
assert(world.tarinai.every(t => !Object.prototype.hasOwnProperty.call(t.relationships, "D")), "chunked cleanup left dead references behind");
|
|
console.log("[OK] death-reference cleanup runs in bounded per-frame chunks");
|
|
|
|
console.log("Performance cleanup regression audit passed.");
|