Files
reverse-engineering-anything/tests/conformance/package/conformanceFixtureVerifier.test.ts
T
morlutoandCursor Agent 83f0a4d238 fix(ci)!: verify exact conformance fixture records (#9977)
* fix(ci)!: verify exact conformance fixture records

BREAKING CHANGE: Regenerate unversioned conformance manifests with npm run build:fixtures. Schema version 2 separates exact string markers from intentional fragments and rejects the ambiguous strings field.

* test(conformance): wait out a cold verifier instead of treating SIGTERM as rejection

The compiler-produced complete/REA_C_ENTRY run was killed by its 10s
execFile timeout on the loaded source-test runner, with empty stderr and
SIGTERM. The stricter record checks already passed locally and for the
other compiler-produced cases. Assert the process status and give that
cold run room inside the test budget.

* fix(ci): preserve carriage returns in nm symbol records

nm -j terminates each name with LF. A carriage return before that LF is
part of the symbol. Splitting on CR LF dropped it, so a renamed symbol
ending in CR matched the name without it.

* test(ci): supervise conformance verifier subprocesses

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-11 17:28:10 +08:00

182 lines
5.9 KiB
TypeScript

import { createHash } from "node:crypto";
import {
mkdir,
mkdtemp,
readFile,
realpath,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { expect, it, onTestFinished } from "vitest";
import { splitNmSymbolRecords } from "../../../scripts/lib/conformance-fixtures.mjs";
import { createVerifierCommands } from "../../../scripts/lib/verify-commands.mjs";
import { removeTestWorkspace } from "../../support/workspace/workspaceFixture.js";
const digest = (bytes: string | Buffer) =>
createHash("sha256").update(bytes).digest("hex");
const verifier = fileURLToPath(
new URL("../../../scripts/verify-conformance-fixtures.mjs", import.meta.url),
);
const ownedFixture = async (prefix: string) => {
const commands = await createVerifierCommands();
let root: string | undefined;
onTestFinished(async () => {
await commands.close();
if (root !== undefined) await removeTestWorkspace(root);
});
root = await realpath(await mkdtemp(join(await realpath(tmpdir()), prefix)));
return { commands, root };
};
it.skipIf(process.platform !== "darwin" && process.platform !== "linux").each([
{ mode: "complete", marker: "REA_C_ENTRY" },
{ mode: "renamed", marker: "REA_C_ENTRY" },
{ mode: "wrong-prefix", marker: "REA_C_ENTRY" },
{ mode: "prefix-complete", marker: "REA_C_ENTRY" },
{ mode: "marker", marker: "REA_C_ENTRY" },
{ mode: "marker", marker: "REA_VERSION_ONE" },
{ mode: "marker", marker: "reaProbeOne" },
{ mode: "marker", marker: "REA_SWIFT_EXECUTE" },
{ mode: "fragment", marker: "REAService" },
{ mode: "fragment", marker: "performAction:error:" },
])(
"verifies compiler-produced fixture records: $mode / $marker",
async ({ mode, marker }) => {
const { commands, root } = await ownedFixture("rea-fixture-symbols-");
const sources = join(root, "sources");
const output = join(root, "output");
await mkdir(sources);
await mkdir(output);
const fixtures = [];
const prefix = process.platform === "darwin" ? "_" : "";
for (const version of [1, 2]) {
const name = `version-v${version}`;
const path = `${name}.c`;
const artifact = `${name}.o`;
const suffix = mode === "renamed" ? "_renamed" : "";
const literal =
mode === "marker"
? `${marker}_renamed`
: mode === "fragment"
? `embedded_${marker}_suffix`
: marker;
const counted = mode === "prefix-complete" ? "" : "unrelated_";
const content = `int rea_version_entry${suffix}(void) { return ${version}; }\nint ${counted}rea_inventory_0000(void) { return 0; }\nconst char *marker = "${literal}";\n`;
await writeFile(join(sources, path), content);
await commands.exec(
"cc",
["-c", join(sources, path), "-o", join(output, artifact)],
{ timeout: 10_000 },
);
const bytes = await readFile(join(output, artifact));
fixtures.push({
name,
artifact,
artifactSha256: digest(bytes),
sources: [{ path, sha256: digest(content) }],
sourceSha256: digest(`${path}\0${digest(content)}\n`),
expectations: {
symbols: [`${prefix}rea_version_entry`],
...(mode === "fragment"
? { stringFragments: [marker] }
: { stringMarkers: [marker] }),
...(mode === "wrong-prefix" || mode === "prefix-complete"
? { symbolPrefix: `${prefix}rea_inventory_`, symbolCount: 1 }
: {}),
},
});
}
const manifest = join(output, "manifest.json");
await writeFile(
manifest,
JSON.stringify({
schemaVersion: 2,
platform: process.platform,
fixtures,
}),
);
const result = commands.exec(
process.execPath,
[verifier, manifest, sources],
{
cwd: root,
timeout: 25_000,
},
);
if (!["complete", "fragment", "prefix-complete"].includes(mode))
await expect(result).rejects.toMatchObject({
code: 1,
snapshot: {
stderr: {
text: expect.stringContaining(
mode === "renamed"
? `missing symbol ${prefix}rea_version_entry`
: mode === "wrong-prefix"
? `expected 1 ${prefix}rea_inventory_ records, received 0`
: `missing string marker ${marker}`,
),
},
},
});
else {
const completed = await result;
expect(completed.stderr).toBe("");
expect(JSON.parse(completed.stdout)).toMatchObject({ verified: 2 });
}
},
120_000,
);
it.each([
{ schemaVersion: undefined, expectations: {} },
{ schemaVersion: 1, expectations: {} },
{ schemaVersion: 2, expectations: { strings: ["ambiguous"] } },
])(
"refuses retired manifest semantics: %j",
async (legacy) => {
const { commands, root } = await ownedFixture(
"rea-fixture-manifest-version-",
);
const manifest = join(root, "manifest.json");
await writeFile(
manifest,
JSON.stringify({
schemaVersion: legacy.schemaVersion,
platform: "linux",
fixtures: [{ expectations: legacy.expectations }],
}),
);
await expect(
commands.exec(process.execPath, [verifier, manifest, root], {
cwd: root,
timeout: 10_000,
}),
).rejects.toMatchObject({
code: 1,
snapshot: {
stderr: {
text: expect.stringContaining(
legacy.schemaVersion === 2
? "Invalid conformance fixture string expectations"
: "Unsupported conformance manifest",
),
},
},
});
},
30_000,
);
it("preserves a carriage return that belongs to an nm symbol name", () => {
expect(splitNmSymbolRecords("rea_entry\r\nrea_inventory_0000\n")).toEqual([
"rea_entry\r",
"rea_inventory_0000",
]);
expect(splitNmSymbolRecords("rea_entry\r\n")).toEqual(["rea_entry\r"]);
});