1
This commit is contained in:
parent
1a3ba56d9a
commit
810ad6f5cb
33 changed files with 10710 additions and 1087 deletions
102
tests/patch-worker-cancel.mjs
Normal file
102
tests/patch-worker-cancel.mjs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { Worker } from "node:worker_threads";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { generateMap } from "../src/mapPipeline.js";
|
||||
import { createWorldMap } from "../src/worldMap.js";
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(message);
|
||||
console.log(`OK: ${message}`);
|
||||
}
|
||||
|
||||
function deriveSeed(worldSeed, terrainType, variant) {
|
||||
let hash = (worldSeed >>> 0) ^ 0x9e3779b9;
|
||||
hash = Math.imul(hash ^ (variant >>> 0), 668265263) >>> 0;
|
||||
for (const char of String(terrainType || "auto")) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
|
||||
return hash >>> 0;
|
||||
}
|
||||
|
||||
const initial = generateMap(114514);
|
||||
const world = createWorldMap(initial);
|
||||
const width = 470;
|
||||
const height = 333;
|
||||
const x0 = Math.max(0, Math.min(world.width - width, world.originX + 238));
|
||||
const y0 = Math.max(0, Math.min(world.height - height, world.originY));
|
||||
const rect = { x0, y0, x1: x0 + width, y1: y0 + height, kind: "lasso" };
|
||||
const insetX = Math.max(4, Math.floor(width * 0.16));
|
||||
const insetY = Math.max(4, Math.floor(height * 0.16));
|
||||
rect.polygon = [
|
||||
{ x: rect.x0 + insetX, y: rect.y0 },
|
||||
{ x: rect.x1 - 1, y: rect.y0 + insetY },
|
||||
{ x: rect.x1 - insetX, y: rect.y1 - 1 },
|
||||
{ x: rect.x0, y: rect.y1 - insetY },
|
||||
];
|
||||
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
|
||||
const seed = deriveSeed(world.seed, terrainType, 0);
|
||||
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
|
||||
let terminalMessage = null;
|
||||
let cancelStartedAt = 0;
|
||||
let terminationMs = Infinity;
|
||||
let cancellationTriggered = false;
|
||||
|
||||
const timeout = setTimeout(async () => {
|
||||
try { await worker.terminate(); } catch {}
|
||||
console.error("FAIL: production patch worker did not reach cancellable large-precompute work in time");
|
||||
process.exit(2);
|
||||
}, 35_000);
|
||||
|
||||
worker.on("message", async (message) => {
|
||||
if (message.id !== 1) return;
|
||||
if (message.type !== "progress") {
|
||||
terminalMessage = message;
|
||||
return;
|
||||
}
|
||||
const progress = message.progress || {};
|
||||
if (cancellationTriggered || progress.phase !== "large-candidate-precompute") return;
|
||||
cancellationTriggered = true;
|
||||
// Let the coordinator enter nested-worker work rather than measuring an idle
|
||||
// worker immediately after the phase marker.
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
cancelStartedAt = performance.now();
|
||||
await worker.terminate();
|
||||
terminationMs = performance.now() - cancelStartedAt;
|
||||
clearTimeout(timeout);
|
||||
assert(terminationMs < 500, `production patch Worker termination settles within 500 ms (${terminationMs.toFixed(1)} ms)`);
|
||||
// terminate() is the hard cancellation boundary. No terminal candidate can
|
||||
// be published after it resolves.
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
assert(terminalMessage === null, "terminated production Worker cannot publish a candidate after cancellation");
|
||||
console.log("All patch-worker cancellation tests passed.");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
worker.on("error", async (error) => {
|
||||
clearTimeout(timeout);
|
||||
try { await worker.terminate(); } catch {}
|
||||
console.error(error?.stack || String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
worker.postMessage({
|
||||
id: 1,
|
||||
world,
|
||||
rect,
|
||||
options: {
|
||||
patchMode: "expansion",
|
||||
terrainType,
|
||||
variant: 0,
|
||||
seed,
|
||||
maxQualityRetries: 0,
|
||||
qualityTerrainAttempts: 1,
|
||||
acceptBestAvailableQuality: false,
|
||||
includeSeamVisualization: false,
|
||||
},
|
||||
search: {
|
||||
searchId: "cancel-integration",
|
||||
operationId: "cancel-integration",
|
||||
committedRevision: 1,
|
||||
workerEpoch: 1,
|
||||
executionAttempt: 1,
|
||||
totalCandidateCount: 1,
|
||||
candidatePlan: [{ candidateId: "cancel:0", candidateOrdinal: 1, variant: 0, seed }],
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue