Files
wasm-simulator/web/test-browser-wasm-smoke-opfs-basic-sections.js

115 lines
5.1 KiB
JavaScript

export async function runBrowserOpfsBasicSections(context, simulator) {
const {
expectErrorContaining,
expectEvent,
expectMissingWasmPath,
expectText,
near,
runSection,
withSmokeSimulator,
} = context;
await runSection("opfs workspace clear and healthcheck", async () => {
if (!simulator.fs.opfs) {
throw new Error("expected OPFS workspace to be available in browser smoke");
}
await simulator.fs.opfs.clear();
if (simulator.fs.opfs.workspacePath !== "cnc-simulator-smoke") {
throw new Error("unexpected OPFS workspace path");
}
await simulator.fs.opfs.writeFile("healthcheck.ngc", "G21 G90\n");
if (!(await simulator.fs.opfs.exists("healthcheck.ngc"))) {
throw new Error("OPFS exists did not find a persisted file");
}
if (await simulator.fs.opfs.exists("missing-healthcheck.ngc")) {
throw new Error("OPFS exists reported a missing file");
}
const healthcheckStat = await simulator.fs.opfs.stat("healthcheck.ngc");
if (healthcheckStat.kind !== "file" || healthcheckStat.size !== 8) {
throw new Error("OPFS stat did not report the persisted healthcheck file size");
}
const clearCheck = new TextDecoder().decode(await simulator.fs.opfs.readFile("healthcheck.ngc"));
expectText(clearCheck, "G21 G90\n", "OPFS workspace did not survive clear/write round trip");
});
await runSection("opfs default mount check", async () => {
await withSmokeSimulator((defaultMountSimulator) => {
if (!defaultMountSimulator.fs.opfs || defaultMountSimulator.fs.opfs.mountPoint !== "/cnc") {
throw new Error("default OPFS mount point was not preserved");
}
});
});
await runSection("opfs program write and read", async () => {
const encoded = new TextEncoder().encode("G21 G90\nG0 X1\n");
await simulator.fs.opfs.writeFile("programs/browser-smoke.ngc", encoded);
const decoded = new TextDecoder().decode(
await simulator.fs.opfs.readFile("programs/browser-smoke.ngc"),
);
expectText(decoded, "G21 G90\nG0 X1\n", "OPFS round trip did not preserve browser smoke program");
});
await runSection("opfs unsafe path rejection", async () => {
for (const unsafePath of ["../escape.ngc", "programs/../escape.ngc", "/escape.ngc", "programs//escape.ngc"]) {
await expectErrorContaining(
() => simulator.fs.opfs.writeFile(unsafePath, "G21\n"),
"inside the CNC workspace",
`OPFS accepted unsafe path ${unsafePath}`,
);
}
for (const [operation, action] of [
["readFile", () => simulator.fs.opfs.readFile("../escape.ngc")],
["readDirectory", () => simulator.fs.opfs.readDirectory("../escape")],
["persistFile", () => simulator.fs.opfs.persistFile("../escape.ngc")],
["persistDirectory", () => simulator.fs.opfs.persistDirectory("../escape")],
["copyFile", () => simulator.fs.opfs.copyFile("../escape.ngc", "programs/escape.ngc")],
["copyFileTarget", () => simulator.fs.opfs.copyFile("healthcheck.ngc", "../escape.ngc")],
["exists", () => simulator.fs.opfs.exists("../escape.ngc")],
["stat", () => simulator.fs.opfs.stat("../escape.ngc")],
["moveFile", () => simulator.fs.opfs.moveFile("../escape.ngc", "programs/escape.ngc")],
["moveFileTarget", () => simulator.fs.opfs.moveFile("healthcheck.ngc", "../escape.ngc")],
["removeFile", () => simulator.fs.opfs.removeFile("../escape.ngc")],
["removeDirectory", () => simulator.fs.opfs.removeDirectory("../escape")],
["parseFile", () => simulator.parseFile("../escape.ngc", "linuxcnc", { backend: "linuxcnc-rs274" })],
[
"parseWithParameterFile",
() =>
simulator.parseWithParameterFile("G21 G90\nM30\n", "../escape.var", "linuxcnc", {
backend: "linuxcnc-rs274",
}),
],
]) {
await expectErrorContaining(
action,
"inside the CNC workspace",
`OPFS ${operation} accepted an unsafe path`,
);
}
});
await runSection("opfs program parse and restore", async () => {
const opfsProgramEvents = await simulator.parseFile("programs/browser-smoke.ngc", "linuxcnc", {
backend: "linuxcnc-rs274",
});
expectEvent(opfsProgramEvents, (event) => event.type === "rapid" && near(event.end.x, 1),
"OPFS program file was not parsed through LinuxCNC WASM");
for (const parameterPath of ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"]) {
expectMissingWasmPath(
() => simulator.fs.module.FS.stat(parameterPath),
`browser parseFile left LinuxCNC parameter state outside OPFS: ${parameterPath}`,
);
}
await withSmokeSimulator(async (restoredProgramSimulator) => {
// LinuxCNC source basis: rs274ngc_pre.cc read_text() reads
// blocks from the program stream after the OPFS mirror is loaded.
const restoredProgramEvents = await restoredProgramSimulator.parseFile(
"programs/browser-smoke.ngc",
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
expectEvent(restoredProgramEvents, (event) => event.type === "rapid" && near(event.end.x, 1),
"LinuxCNC OPFS program file was not restored across WASM simulator instances");
});
});
}