penpot/render-wasm/scripts/cargo-build.test.cjs
Miguel de Benito Delgado 6f34794f54
🐛 Fix render-wasm build arguments and artifact selection (#12254)
Forward Cargo arguments as an array and honor explicit profiles.
Build and copy each renderer target once under the repository lock,
with watch rebuilds using the same wrapper.

Select this checkout's compiler artifact and matching generated enum
table from Cargo JSON. Record actual features, profile and output paths,
and allow callers to override publication destinations.

Run the existing Cargo parser tests before the renderer's native tests
and stop on parser failures.

AI-assisted-by: gpt-6.1-sol
2026-10-13 12:54:48 +02:00

350 lines
9.4 KiB
JavaScript

const assert = require("node:assert/strict");
const { spawn, spawnSync } = require("node:child_process");
const { once } = require("node:events");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { test } = require("node:test");
const helper = path.join(__dirname, "cargo-build.cjs");
const finished = { reason: "build-finished", success: true };
function fixture(t) {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), "render-wasm-cargo-build-"),
);
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const manifest = path.join(root, "Cargo.toml");
const artifactDir = path.join(
root,
"target with spaces",
"wasm32-unknown-emscripten",
"release",
);
const outDir = path.join(artifactDir, "build", "render-active", "out");
const staleOutDir = path.join(
artifactDir,
"build",
"000-render-stale",
"out",
);
for (const dir of [outDir, staleOutDir]) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(manifest, '[package]\nname = "render"\n');
fs.writeFileSync(
path.join(artifactDir, "render_wasm.js"),
"export default {};\n",
);
fs.writeFileSync(
path.join(outDir, "render_wasm_shared.js"),
"active enum table\n",
);
fs.writeFileSync(
path.join(staleOutDir, "render_wasm_shared.js"),
"stale enum table\n",
);
const artifact = {
reason: "compiler-artifact",
package_id: `path+file://${root}#render@0.1.0`,
manifest_path: manifest,
target: { name: "render_wasm", kind: ["bin"] },
features: ["default", "profile", "profile-macros", "profile-raf"],
profile: {
opt_level: "3",
debuginfo: 0,
debug_assertions: false,
overflow_checks: false,
test: false,
},
filenames: [path.join(artifactDir, "render_wasm.js")],
fresh: false,
};
const buildScript = {
reason: "build-script-executed",
package_id: artifact.package_id,
out_dir: outDir,
};
const otherScript = {
...buildScript,
package_id:
"registry+https://github.com/rust-lang/crates.io-index#render@0.1.0",
out_dir: staleOutDir,
};
const otherArtifact = {
...artifact,
package_id: otherScript.package_id,
manifest_path: path.join(root, "dependency", "Cargo.toml"),
features: ["stats"],
profile: { ...artifact.profile, opt_level: "0", debug_assertions: true },
};
return {
root,
manifest,
artifactDir,
outDir,
staleOutDir,
artifact,
buildScript,
otherScript,
otherArtifact,
info: path.join(root, "build-info.json"),
};
}
function cargoOutput(records) {
return records.map((record) => JSON.stringify(record)).join("\n") + "\n";
}
function run(f, records) {
return spawnSync(process.execPath, [helper, f.info, f.manifest], {
cwd: f.root,
input: cargoOutput(records),
encoding: "utf8",
});
}
for (const fresh of [false, true]) {
test(`selects the root package's active enum table and actual features (fresh=${fresh})`, (t) => {
const f = fixture(t);
const result = run(f, [
f.otherScript,
f.otherArtifact,
{ ...f.artifact, fresh },
f.buildScript,
{
...f.otherScript,
out_dir: path.join(f.root, "another dependency out"),
},
finished,
]);
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stdout, "");
const info = JSON.parse(fs.readFileSync(f.info, "utf8"));
assert.deepEqual(info, {
package_id: f.artifact.package_id,
features: f.artifact.features,
profile: f.artifact.profile,
out_dir: f.outDir,
artifact_dir: f.artifactDir,
shared_file: path.join(f.outDir, "render_wasm_shared.js"),
});
assert.equal(
fs.readFileSync(info.shared_file, "utf8"),
"active enum table\n",
);
});
}
test("records an empty actual feature set", (t) => {
const f = fixture(t);
const result = run(f, [
f.buildScript,
{ ...f.artifact, features: [] },
finished,
]);
assert.equal(result.status, 0, result.stderr);
assert.deepEqual(JSON.parse(fs.readFileSync(f.info, "utf8")).features, []);
});
for (const profile of [
{
opt_level: "0",
debuginfo: 2,
debug_assertions: true,
overflow_checks: true,
test: false,
},
{
opt_level: "z",
debuginfo: 0,
debug_assertions: false,
overflow_checks: false,
test: false,
},
]) {
test(`records the actual Cargo profile without changing fields (opt_level=${profile.opt_level})`, (t) => {
const f = fixture(t);
const result = run(f, [
f.buildScript,
{ ...f.artifact, profile },
finished,
]);
assert.equal(result.status, 0, result.stderr);
assert.deepEqual(
JSON.parse(fs.readFileSync(f.info, "utf8")).profile,
profile,
);
});
}
test(
"streams rendered compiler messages to stderr before Cargo finishes",
{ timeout: 10000 },
async (t) => {
const f = fixture(t);
const child = spawn(process.execPath, [helper, f.info, f.manifest], {
cwd: f.root,
});
t.after(() => {
if (child.exitCode === null) child.kill();
});
child.stdout.resume();
const closed = once(child, "close");
const diagnostic = "warning: renderer diagnostic\n";
let stderr = "";
const rendered = new Promise((resolve, reject) => {
child.stderr.on("data", (chunk) => {
stderr += chunk;
if (stderr.includes(diagnostic)) resolve();
});
child.on("error", reject);
child.on("close", () => {
if (!stderr.includes(diagnostic))
reject(new Error(stderr || "Missing diagnostic"));
});
});
child.stdin.write(
cargoOutput([
{ reason: "compiler-message", message: { rendered: diagnostic } },
]),
);
await rendered;
assert.equal(child.exitCode, null);
assert.equal(fs.existsSync(f.info), false);
child.stdin.end(cargoOutput([f.buildScript, f.artifact, finished]));
const [code] = await closed;
assert.equal(code, 0, stderr);
assert.equal(stderr, diagnostic);
},
);
const rejected = [
[
"failed Cargo build",
(f) => [f.buildScript, f.artifact, { ...finished, success: false }],
],
["missing build-finished record", (f) => [f.buildScript, f.artifact]],
["missing root artifact", (f) => [f.otherScript, f.otherArtifact, finished]],
[
"missing matching build script",
(f) => [f.otherScript, f.artifact, finished],
],
[
"missing out_dir",
(f) => [{ ...f.buildScript, out_dir: undefined }, f.artifact, finished],
],
[
"missing actual features",
(f) => [f.buildScript, { ...f.artifact, features: undefined }, finished],
],
[
"invalid actual features",
(f) => [f.buildScript, { ...f.artifact, features: [42] }, finished],
],
[
"missing profile",
(f) => [f.buildScript, { ...f.artifact, profile: undefined }, finished],
],
[
"null profile",
(f) => [f.buildScript, { ...f.artifact, profile: null }, finished],
],
[
"non-object profile",
(f) => [f.buildScript, { ...f.artifact, profile: "release" }, finished],
],
[
"array profile",
(f) => [f.buildScript, { ...f.artifact, profile: [] }, finished],
],
[
"missing profile opt_level",
(f) => [
f.buildScript,
{ ...f.artifact, profile: { debug_assertions: false } },
finished,
],
],
[
"invalid profile opt_level",
(f) => [
f.buildScript,
{ ...f.artifact, profile: { opt_level: 3, debug_assertions: false } },
finished,
],
],
[
"missing profile debug_assertions",
(f) => [
f.buildScript,
{ ...f.artifact, profile: { opt_level: "3" } },
finished,
],
],
[
"invalid profile debug_assertions",
(f) => [
f.buildScript,
{ ...f.artifact, profile: { opt_level: "3", debug_assertions: "false" } },
finished,
],
],
[
"missing JS artifact",
(f) => [f.buildScript, { ...f.artifact, filenames: [] }, finished],
],
[
"multiple root artifacts",
(f) => [
f.buildScript,
f.artifact,
{ ...f.artifact, features: [] },
finished,
],
],
[
"ambiguous root out_dir",
(f) => [
f.buildScript,
{ ...f.buildScript, out_dir: f.staleOutDir },
f.artifact,
finished,
],
],
];
for (const [name, records] of rejected) {
test(`rejects ${name} and removes stale build facts`, (t) => {
const f = fixture(t);
fs.writeFileSync(f.info, '{"features":["stale"]}\n');
const result = run(f, records(f));
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /ERROR:/);
assert.equal(fs.existsSync(f.info), false);
});
}
test("rejects a missing active enum table even when a stale table exists", (t) => {
const f = fixture(t);
fs.unlinkSync(path.join(f.outDir, "render_wasm_shared.js"));
const result = run(f, [f.otherScript, f.buildScript, f.artifact, finished]);
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /render_wasm_shared\.js/);
assert.equal(fs.existsSync(f.info), false);
});
test("rejects malformed Cargo JSON and removes stale build facts", (t) => {
const f = fixture(t);
fs.writeFileSync(f.info, '{"features":["stale"]}\n');
const result = spawnSync(process.execPath, [helper, f.info, f.manifest], {
cwd: f.root,
input: "not Cargo JSON\n",
encoding: "utf8",
});
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /ERROR:/);
assert.equal(fs.existsSync(f.info), false);
});