feat: publish Regex Tools 0.2.0
This commit is contained in:
168
src/regex/execution/EngineSupervisor.test.ts
Normal file
168
src/regex/execution/EngineSupervisor.test.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type {
|
||||
RegexExecutionRequest,
|
||||
RegexExecutionResult,
|
||||
} from "../model/match";
|
||||
import type { RegexFlavourId } from "../model/flavour";
|
||||
import { EngineSupervisor } from "./EngineSupervisor";
|
||||
import {
|
||||
ENGINE_WORKER_REGISTRY,
|
||||
EngineWorkerRegistry,
|
||||
} from "./engine-registry";
|
||||
import type { WorkerLike } from "./WorkerSupervisor";
|
||||
import {
|
||||
WORKER_PROTOCOL_VERSION,
|
||||
type EngineWorkerOperation,
|
||||
type EngineWorkerResult,
|
||||
type WorkerRequest,
|
||||
type WorkerResponse,
|
||||
} from "./worker-protocol";
|
||||
|
||||
function request(flavour: RegexFlavourId): RegexExecutionRequest {
|
||||
return {
|
||||
flavour,
|
||||
flavourVersion: "fixture",
|
||||
pattern: "a",
|
||||
flags: [],
|
||||
options: {},
|
||||
subject: "a",
|
||||
captureMetadata: [],
|
||||
scanAll: false,
|
||||
maximumMatches: 10,
|
||||
maximumCaptureRows: 10,
|
||||
};
|
||||
}
|
||||
|
||||
function result(flavour: RegexFlavourId): RegexExecutionResult {
|
||||
return {
|
||||
accepted: true,
|
||||
engine: {
|
||||
flavour,
|
||||
adapterVersion: "fixture",
|
||||
engineName: `${flavour} fixture`,
|
||||
engineVersion: "fixture",
|
||||
offsetUnit: "utf16",
|
||||
capabilities: {
|
||||
compilation: true,
|
||||
matching: true,
|
||||
replacement: false,
|
||||
namedCaptures: false,
|
||||
captureHistory: false,
|
||||
actualTrace: false,
|
||||
benchmark: false,
|
||||
},
|
||||
},
|
||||
flags: {
|
||||
userFlags: "",
|
||||
effectiveFlags: "",
|
||||
internallyAddedIndicesFlag: false,
|
||||
internallyAddedGlobalFlag: false,
|
||||
},
|
||||
matches: [],
|
||||
diagnostics: [],
|
||||
elapsedMs: 0,
|
||||
truncated: false,
|
||||
};
|
||||
}
|
||||
|
||||
class ResponsiveWorker implements WorkerLike {
|
||||
onmessage: ((event: MessageEvent<unknown>) => void) | null = null;
|
||||
onerror: ((event: ErrorEvent) => void) | null = null;
|
||||
onmessageerror: ((event: MessageEvent<unknown>) => void) | null = null;
|
||||
readonly operations: EngineWorkerOperation[] = [];
|
||||
terminateCalls = 0;
|
||||
|
||||
postMessage(message: unknown): void {
|
||||
const request = message as WorkerRequest<EngineWorkerOperation>;
|
||||
this.operations.push(request.payload);
|
||||
const payload: EngineWorkerResult =
|
||||
request.payload.kind === "execute"
|
||||
? {
|
||||
kind: "execute",
|
||||
result: result(request.payload.request.flavour),
|
||||
}
|
||||
: {
|
||||
kind: "replace",
|
||||
result: {
|
||||
execution: result(request.payload.request.flavour),
|
||||
output: "",
|
||||
outputBytes: 0,
|
||||
outputTruncated: false,
|
||||
truncated: false,
|
||||
},
|
||||
};
|
||||
const response: WorkerResponse<EngineWorkerResult> = {
|
||||
protocolVersion: WORKER_PROTOCOL_VERSION,
|
||||
requestId: request.requestId,
|
||||
generation: request.generation,
|
||||
ok: true,
|
||||
payload,
|
||||
};
|
||||
this.onmessage?.({ data: response } as MessageEvent<unknown>);
|
||||
}
|
||||
|
||||
terminate(): void {
|
||||
this.terminateCalls += 1;
|
||||
}
|
||||
}
|
||||
|
||||
describe("engine worker selection", () => {
|
||||
it("registers the shipped ECMAScript and PCRE2 execution workers", () => {
|
||||
expect(
|
||||
ENGINE_WORKER_REGISTRY.registrations.map(
|
||||
(registration) => registration.flavour,
|
||||
),
|
||||
).toEqual(["ecmascript", "pcre2"]);
|
||||
expect(ENGINE_WORKER_REGISTRY.require("pcre2").label).toBe("PCRE2 engine");
|
||||
});
|
||||
|
||||
it("selects and reuses a dedicated supervisor per registered flavour", async () => {
|
||||
const workers = new Map<RegexFlavourId, ResponsiveWorker[]>();
|
||||
const registration = (flavour: RegexFlavourId) => ({
|
||||
flavour,
|
||||
label: `${flavour} fixture`,
|
||||
createWorker: () => {
|
||||
const worker = new ResponsiveWorker();
|
||||
workers.set(flavour, [...(workers.get(flavour) ?? []), worker]);
|
||||
return worker;
|
||||
},
|
||||
});
|
||||
const supervisor = new EngineSupervisor(
|
||||
new EngineWorkerRegistry([
|
||||
registration("ecmascript"),
|
||||
registration("pcre2"),
|
||||
]),
|
||||
);
|
||||
|
||||
await expect(
|
||||
supervisor.execute(request("ecmascript"), 100),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
engine: expect.objectContaining({ flavour: "ecmascript" }),
|
||||
}),
|
||||
);
|
||||
await expect(supervisor.execute(request("pcre2"), 100)).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
engine: expect.objectContaining({ flavour: "pcre2" }),
|
||||
}),
|
||||
);
|
||||
await supervisor.execute(request("ecmascript"), 100);
|
||||
|
||||
expect(workers.get("ecmascript")).toHaveLength(1);
|
||||
expect(workers.get("pcre2")).toHaveLength(1);
|
||||
expect(workers.get("ecmascript")?.[0]?.operations).toHaveLength(2);
|
||||
|
||||
supervisor.dispose();
|
||||
expect(workers.get("ecmascript")?.[0]?.terminateCalls).toBe(1);
|
||||
expect(workers.get("pcre2")?.[0]?.terminateCalls).toBe(1);
|
||||
});
|
||||
|
||||
it("fails closed before constructing a worker for an unavailable flavour", async () => {
|
||||
const supervisor = new EngineSupervisor(new EngineWorkerRegistry([]));
|
||||
|
||||
await expect(supervisor.execute(request("pcre2"), 100)).rejects.toThrow(
|
||||
/No execution worker is registered/u,
|
||||
);
|
||||
supervisor.dispose();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user