// @vitest-environment node import { readFile } from "node:fs/promises"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { beforeAll, describe, expect, it } from "vitest"; import { CppEngineAdapter, CPP_ENGINE_VERSION, CPP_FLAVOUR_VERSION, } from "../../src/regex/execution/adapters/cpp/CppEngineAdapter"; import type { CppRegexModuleFactory } from "../../src/regex/execution/adapters/cpp/cpp-module"; import type { RegexExecutionRequest, RegexReplacementRequest, } from "../../src/regex/model/match"; const pack = path.resolve("public/engines/cpp"); let adapter: CppEngineAdapter; function request( pattern: string, subject: string, overrides: Partial = {}, ): RegexExecutionRequest { return { flavour: "cpp", flavourVersion: CPP_FLAVOUR_VERSION, pattern, flags: ["g"], options: { grammar: "ECMAScript" }, subject, captureMetadata: [], scanAll: false, maximumMatches: 100, maximumCaptureRows: 1_000, ...overrides, }; } function replacement( pattern: string, subject: string, replacementText: string, overrides: Partial = {}, ): RegexReplacementRequest { return { ...request(pattern, subject), replacement: replacementText, maximumOutputBytes: 1_024, ...overrides, }; } beforeAll(async () => { const imported = (await import( `${pathToFileURL(path.join(pack, "cpp-regex.mjs")).href}?conformance` )) as { default: CppRegexModuleFactory }; const module = await imported.default({ locateFile: (name) => path.join(pack, name), print: () => undefined, printErr: () => undefined, }); adapter = new CppEngineAdapter(module, "Node.js conformance"); await adapter.load(); }); describe("C++ Emscripten libc++ std::wregex conformance", () => { it("loads the exact libc++ and compiler identity", async () => { await expect(adapter.load()).resolves.toEqual( expect.objectContaining({ flavour: "cpp", engineName: "C++ std::wregex (Emscripten libc++)", engineVersion: CPP_ENGINE_VERSION, runtimeVersion: expect.stringContaining( "32-bit wchar_t code-point bridge", ), offsetUnit: "code-point", }), ); }); it("returns code-point-native ranges without losing editor UTF-16 offsets", async () => { const result = await adapter.execute(request("(\\w+)", "😀 one two")); expect(result.accepted).toBe(true); expect( result.matches.map(({ nativeRange, range, value }) => ({ nativeRange, range, value, })), ).toEqual([ { nativeRange: { start: 2, end: 5, unit: "code-point" }, range: { startUtf16: 3, endUtf16: 6 }, value: "one", }, { nativeRange: { start: 6, end: 9, unit: "code-point" }, range: { startUtf16: 7, endUtf16: 10 }, value: "two", }, ]); }); it("uses native std::regex replacement and grammar selection", async () => { const replaced = await adapter.replace( replacement("(\\w+)", "one two", "<$1>"), ); expect(replaced.execution.accepted).toBe(true); expect(replaced.output).toBe(" "); const basic = await adapter.execute( request("\\(ab\\)", "xxabyy", { options: { grammar: "basic" }, }), ); expect(basic.matches[0]?.value).toBe("ab"); }); it("bounds capture amplification and preserves the untouched partial suffix", async () => { const bounded = await adapter.replace( replacement("(a+)", "a".repeat(256 * 1024), "$1".repeat(4_096), { maximumOutputBytes: 5, }), ); expect(bounded.execution.accepted).toBe(true); expect(bounded.output).toBe("aaaaa"); expect(bounded.outputBytes).toBe(5); expect(bounded.outputTruncated).toBe(true); const partial = await adapter.replace( replacement("a", "aaa", "x", { maximumMatches: 1 }), ); expect(partial.execution.matches).toHaveLength(1); expect(partial.execution.truncated).toBe(true); expect(partial.output).toBe("xaa"); expect(partial.outputTruncated).toBe(false); }); it("reports syntax that libc++ std::regex does not accept", async () => { const result = await adapter.execute(request("(?a)", "a")); expect(result.accepted).toBe(false); expect(result.diagnostics[0]).toEqual( expect.objectContaining({ source: "execution-engine", severity: "error", }), ); }); it("ships a fixed bridge module without dynamic source evaluation", async () => { const module = await readFile(path.join(pack, "cpp-regex.mjs"), "utf8"); expect(module).not.toContain("new Function"); expect(module).not.toMatch(/\beval\s*\(/u); const metadata = JSON.parse( await readFile(path.join(pack, "engine-metadata.json"), "utf8"), ) as { bridge: { dynamicExecutionDisabled: boolean }; toolchain: { version: string; commitSha1: string }; }; expect(metadata.bridge.dynamicExecutionDisabled).toBe(true); expect(metadata.toolchain).toEqual( expect.objectContaining({ version: "6.0.4", commitSha1: "fe5be6afdff43ad58860d821fcc8572a23f92d19", }), ); }); });