第 6 组:源码链接、构建和文档约束闭环完成
This commit is contained in:
114
web/test-browser-wasm-smoke-opfs-basic-sections.js
Normal file
114
web/test-browser-wasm-smoke-opfs-basic-sections.js
Normal file
@@ -0,0 +1,114 @@
|
||||
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");
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user