111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { spawnSync } from "node:child_process";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
generatePcre2C,
|
|
type Pcre2CGenerationRequest,
|
|
} from "../src/regex/codegen/pcre2-c";
|
|
|
|
function request(operation: "match" | "replace"): Pcre2CGenerationRequest {
|
|
return {
|
|
flavour: "pcre2",
|
|
flavourVersion: "PCRE2 10.47 8-bit WebAssembly",
|
|
operation,
|
|
pattern: String.raw`(?<word>\p{L}+)-(\d+)`,
|
|
flags: ["g"],
|
|
options: {
|
|
matchLimit: 1_000_000,
|
|
depthLimit: 1_000,
|
|
heapLimitKib: 32_768,
|
|
},
|
|
subject: "x Grüße-42 y alpha-7",
|
|
scanAll: true,
|
|
...(operation === "replace" ? { replacement: "$2:$<word>" } : {}),
|
|
maximumMatches: 100,
|
|
maximumCaptureRows: 1_000,
|
|
maximumOutputBytes: 4_096,
|
|
};
|
|
}
|
|
|
|
function sha256(value: string): string {
|
|
return createHash("sha256").update(value).digest("hex");
|
|
}
|
|
|
|
const exactToolchainPrefix = process.env.PCRE2_CODEGEN_PREFIX;
|
|
const compileWithExactPcre2 = exactToolchainPrefix ? it : it.skip;
|
|
|
|
describe("PCRE2 C code-generation golden", () => {
|
|
it("retains a reviewed deterministic source identity", () => {
|
|
expect(sha256(generatePcre2C(request("match")).source)).toBe(
|
|
"22b91d578a449b0ed5c30e6eadad30f19a2bcb26ac9db355c8ec7a69328675da",
|
|
);
|
|
expect(sha256(generatePcre2C(request("replace")).source)).toBe(
|
|
"ab90a0d9831ea7b7145aa0072ac352b42baf9a795319d81f531b0f341b04a636",
|
|
);
|
|
});
|
|
|
|
compileWithExactPcre2(
|
|
"compiles and executes against the explicitly supplied PCRE2 10.47 C toolchain",
|
|
async () => {
|
|
if (!exactToolchainPrefix) return;
|
|
const header = path.join(exactToolchainPrefix, "include", "pcre2.h");
|
|
const library =
|
|
process.env.PCRE2_CODEGEN_LIBRARY ??
|
|
path.join(exactToolchainPrefix, "lib64", "libpcre2-8.a");
|
|
await expect(readFile(header)).resolves.toBeInstanceOf(Buffer);
|
|
await expect(readFile(library)).resolves.toBeInstanceOf(Buffer);
|
|
const directory = await mkdtemp(
|
|
path.join(tmpdir(), "regex-tools-codegen-"),
|
|
);
|
|
try {
|
|
for (const operation of ["match", "replace"] as const) {
|
|
const generated = generatePcre2C(request(operation));
|
|
const source = path.join(directory, generated.fileName);
|
|
const executable = path.join(directory, `pcre2-${operation}`);
|
|
await writeFile(source, generated.source);
|
|
const compiled = spawnSync(
|
|
process.env.CC ?? "cc",
|
|
[
|
|
"-std=c17",
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
`-I${path.join(exactToolchainPrefix, "include")}`,
|
|
source,
|
|
library,
|
|
"-o",
|
|
executable,
|
|
],
|
|
{ encoding: "utf8" },
|
|
);
|
|
expect(
|
|
`${compiled.stdout}${compiled.stderr}`,
|
|
`${operation} fixture did not compile`,
|
|
).toBe("");
|
|
expect(compiled.status).toBe(0);
|
|
const executed = spawnSync(executable, [], { encoding: "utf8" });
|
|
expect(
|
|
executed.status,
|
|
`${operation} fixture failed: ${executed.stdout}${executed.stderr}`,
|
|
).toBe(0);
|
|
if (operation === "match") {
|
|
expect(executed.stdout).toContain("match 1: UTF-8 bytes 2..12");
|
|
expect(executed.stdout).toContain("group 1 / word");
|
|
expect(executed.stdout).toContain(
|
|
"completed: 2 match(es), 4 capture row(s)",
|
|
);
|
|
} else {
|
|
expect(executed.stdout).toBe("x 42:Grüße y 7:alpha");
|
|
expect(executed.stderr).toContain("completed: 2 substitution(s)");
|
|
}
|
|
}
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|
|
});
|