结论:按 LinuxCNC src/emc/rs274ngc/rs274ngc_pre.cc save_parameters() 的 .bak 保存语义,强化浏览器 smoke,在新 WASM 实例中读取 OPFS 主参数文件和备份文件,证明备份不是当前实例镜像残留。 检查:./check-linuxcnc-opfs-source-map.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
300 lines
16 KiB
JavaScript
300 lines
16 KiB
JavaScript
export async function runBrowserOpfsParameterSections(context, simulator) {
|
|
const {
|
|
expectErrorContaining,
|
|
expectEvent,
|
|
expectIncludes,
|
|
expectMissingOpfsFile,
|
|
expectMissingWasmPath,
|
|
expectNoEvent,
|
|
expectOpfsDirectoryStat,
|
|
expectText,
|
|
expectWasmFilesMissing,
|
|
near,
|
|
readOpfsText,
|
|
removeWasmPathIfExists,
|
|
runSection,
|
|
withSmokeSimulator,
|
|
} = context;
|
|
const backend = { backend: "linuxcnc-rs274" };
|
|
const parseWithParameters = (program, path) => simulator.parseWithParameterFile(program, path, "linuxcnc", backend);
|
|
|
|
await runSection("opfs parameter save", async () => {
|
|
await simulator.parseWithParameterFile("G21 G90\nG0 X1\nG28.1\nM30\n", "parameters/rs274ngc.var", "linuxcnc", backend);
|
|
const opfsParameterFile = await readOpfsText(simulator, "parameters/rs274ngc.var");
|
|
expectIncludes(opfsParameterFile, "5161\t1.000000", "LinuxCNC parameter file was not persisted into OPFS");
|
|
expectWasmFilesMissing(simulator.fs.module.FS, ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"], (parameterPath) => `LinuxCNC parameter scratch file remained outside OPFS after save: ${parameterPath}`);
|
|
});
|
|
|
|
await runSection("opfs parameter restore in new instance", async () => {
|
|
await withSmokeSimulator(async (restoredWorkspaceSimulator) => {
|
|
// LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()
|
|
// loads persisted numbered parameters before interpreting blocks.
|
|
const restoredWorkspaceEvents = await restoredWorkspaceSimulator.parseWithParameterFile(
|
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X8\nO10 endif\n",
|
|
"parameters/rs274ngc.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
expectEvent(restoredWorkspaceEvents, (event) => event.type === "linear-feed" && near(event.end.x, 8),
|
|
"LinuxCNC OPFS parameter file was not restored across WASM simulator instances");
|
|
});
|
|
});
|
|
|
|
await runSection("opfs default parameter backup", async () => {
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
|
// preserves the previous default parameter file as filename + ".bak".
|
|
await parseWithParameters("G21 G90\nM30\n", "parameters/rs274ngc.var");
|
|
const defaultParameterBackup = await readOpfsText(simulator, "parameters/rs274ngc.var.bak");
|
|
expectIncludes(defaultParameterBackup, "5161\t1.000000", "LinuxCNC default OPFS parameter backup did not preserve the previous file");
|
|
});
|
|
|
|
await runSection("opfs parameter temporary cleanup", async () => {
|
|
await simulator.fs.opfs.writeFile("parameters/rs274ngc.var.new", "stale temporary parameter file\n");
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes
|
|
// filename + ".new", then renames it over the parameter file.
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary parameter file\n");
|
|
const restoredParameterEvents = await parseWithParameters(
|
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
|
"parameters/rs274ngc.var",
|
|
);
|
|
expectEvent(restoredParameterEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
|
"LinuxCNC parameter file was not restored from OPFS");
|
|
await expectMissingOpfsFile(
|
|
() => simulator.fs.opfs.readFile("parameters/rs274ngc.var.new"),
|
|
"stale LinuxCNC temporary parameter file remained in OPFS",
|
|
);
|
|
expectMissingWasmPath(
|
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.new"),
|
|
"stale LinuxCNC temporary parameter file remained in WASM FS",
|
|
);
|
|
simulator.fs.module.FS.mkdir(simulator.fs.opfs.resolvePath("parameters/new-directory.var.new")); await simulator.fs.opfs.persistDirectory("parameters/new-directory.var.new");
|
|
await parseWithParameters("G21 G90\nG0 X5\nG28.1\nM30\n", "parameters/new-directory.var");
|
|
expectIncludes(await readOpfsText(simulator, "parameters/new-directory.var"), "5161\t5.000000", "LinuxCNC OPFS parameter file was not saved when stale temporary cleanup saw a directory");
|
|
await expectOpfsDirectoryStat(simulator, "parameters/new-directory.var.new", "LinuxCNC temporary file cleanup removed an existing OPFS directory"); await simulator.fs.opfs.removeDirectory("parameters/new-directory.var.new");
|
|
});
|
|
|
|
await runSection("opfs parameter backup bridge", async () => {
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
|
// attempts link(filename, filename + ".bak") but treats link
|
|
// failure as non-fatal. The browser bridge must persist a backup
|
|
// when the WASM filesystem exposes one.
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var", "5161\t2.000000\n");
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "5161\t1.000000\n");
|
|
await simulator.fs.opfs.persistParameterFile("parameters/backup-bridge.var");
|
|
const opfsParameterBackup = await readOpfsText(simulator, "parameters/backup-bridge.var.bak");
|
|
expectIncludes(opfsParameterBackup, "5161\t1.000000",
|
|
"LinuxCNC parameter backup file was not bridged into OPFS");
|
|
expectWasmFilesMissing(simulator.fs.module.FS, ["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"], (parameterPath) => `LinuxCNC parameter scratch file remained outside OPFS after backup bridge: ${parameterPath}`);
|
|
});
|
|
|
|
await runSection("opfs parameter backup after existing save", async () => {
|
|
await simulator.fs.opfs.writeFile("parameters/existing-save.var", "5161\t3.000000\n");
|
|
await simulator.fs.opfs.writeFile("parameters/existing-save.var.bak", "stale previous backup\n");
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
|
|
// filename + ".bak", links the current parameter file to it,
|
|
// then renames filename + ".new" over filename.
|
|
await parseWithParameters("G21 G90\nG0 X4\nG28.1\nM30\n", "parameters/existing-save.var");
|
|
await withSmokeSimulator(async (restoredBackupSimulator) => {
|
|
expectIncludes(await readOpfsText(restoredBackupSimulator, "parameters/existing-save.var"), "5161\t4.000000", "LinuxCNC existing OPFS parameter file was not restored in a new WASM instance");
|
|
expectIncludes(await readOpfsText(restoredBackupSimulator, "parameters/existing-save.var.bak"), "5161\t3.000000", "LinuxCNC existing OPFS parameter backup was not restored in a new WASM instance");
|
|
});
|
|
await expectMissingOpfsFile(
|
|
() => simulator.fs.opfs.readFile("parameters/existing-save.var.new"),
|
|
"LinuxCNC existing OPFS parameter temporary file remained after save",
|
|
);
|
|
await simulator.fs.opfs.writeFile("parameters/backup-directory.var", "5161\t6.000000\n");
|
|
const backupDirectory = simulator.fs.opfs.resolvePath("parameters/backup-directory.var.bak");
|
|
simulator.fs.module.FS.mkdir(backupDirectory);
|
|
await simulator.fs.opfs.persistDirectory("parameters/backup-directory.var.bak");
|
|
await parseWithParameters("G21 G90\nG0 X7\nG28.1\nM30\n", "parameters/backup-directory.var");
|
|
expectIncludes(await readOpfsText(simulator, "parameters/backup-directory.var"), "5161\t7.000000",
|
|
"LinuxCNC OPFS parameter file was not saved when backup link failed");
|
|
await expectOpfsDirectoryStat(simulator, "parameters/backup-directory.var.bak",
|
|
"LinuxCNC non-fatal OPFS backup failure did not leave the existing backup path intact");
|
|
});
|
|
|
|
await runSection("opfs parameter stale backup cleanup", async () => {
|
|
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var");
|
|
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var.new");
|
|
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var.bak");
|
|
await simulator.fs.opfs.writeFile("parameters/stale-backup-cleanup.var.bak", "stale backup without main file\n");
|
|
removeWasmPathIfExists(simulator, "rs274ngc.var.bak");
|
|
removeWasmPathIfExists(simulator, "/rs274ngc.var.bak");
|
|
await simulator.parseWithParameterFile(
|
|
"G21 G90\nM30\n",
|
|
"parameters/stale-backup-cleanup.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
await expectMissingOpfsFile(
|
|
() => simulator.fs.opfs.readFile("parameters/stale-backup-cleanup.var.bak"),
|
|
"stale LinuxCNC parameter backup file remained in OPFS",
|
|
);
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
|
// unlinks stale backup files before replacing the parameter file.
|
|
await simulator.fs.opfs.removeFile("parameters/missing-cleanup/rs274ngc.var.bak");
|
|
});
|
|
|
|
await runSection("opfs program file with parameter persistence", async () => {
|
|
await simulator.fs.opfs.writeFile("programs/parameter-file-smoke.ngc", "G21 G90\nF100\nG0 X1\nG28.1\nM30\n");
|
|
const fileParameterEvents = await simulator.parseFileWithParameterFile(
|
|
"programs/parameter-file-smoke.ngc",
|
|
"parameters/rs274ngc.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
expectEvent(fileParameterEvents, (event) => event.type === "program-end",
|
|
"OPFS program file with LinuxCNC parameter persistence did not parse");
|
|
});
|
|
|
|
await runSection("opfs missing parameter file creation", async () => {
|
|
await simulator.fs.opfs.removeFile("parameters/missing-main.var");
|
|
await simulator.fs.opfs.removeFile("parameters/missing-main.var.new");
|
|
await simulator.fs.opfs.removeFile("parameters/missing-main.var.bak");
|
|
await simulator.fs.opfs.writeFile("parameters/missing-main.var.bak", "stale backup without main file\n");
|
|
await simulator.fs.opfs.writeFile("parameters/missing-main.var.new", "stale temporary without main file\n");
|
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
|
|
// filename + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX before linking,
|
|
// then renames filename + ".new" over the parameter file.
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "stale wasm backup without main file\n");
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary without main file\n");
|
|
await simulator.parseWithParameterFile(
|
|
"G21 G90\nM30\n",
|
|
"parameters/missing-main.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
const missingMainParameterFile = await readOpfsText(simulator, "parameters/missing-main.var");
|
|
expectIncludes(missingMainParameterFile, "5220\t1.000000",
|
|
"LinuxCNC missing parameter file was not created through OPFS");
|
|
expectIncludes(missingMainParameterFile, "5161\t0.000000",
|
|
"LinuxCNC missing parameter file did not include required G28 parameter");
|
|
await expectMissingOpfsFile(
|
|
() => simulator.fs.opfs.readFile("parameters/missing-main.var.bak"),
|
|
"stale LinuxCNC missing-parameter backup remained in OPFS after first creation",
|
|
);
|
|
const missingMainRestoreEvents = await simulator.parseWithParameterFile(
|
|
"G21 G90\nF100\nO10 if [#5220 EQ 1]\nG1 X6\nO10 endif\n",
|
|
"parameters/missing-main.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
expectEvent(missingMainRestoreEvents, (event) => event.type === "linear-feed" && near(event.end.x, 6),
|
|
"LinuxCNC-created missing OPFS parameter file was not restored on the next parse");
|
|
const missingMainParameterBackup = await readOpfsText(simulator, "parameters/missing-main.var.bak");
|
|
expectIncludes(missingMainParameterBackup, "5220\t1.000000",
|
|
"LinuxCNC-created OPFS parameter file was not backed up on the next successful save");
|
|
expectMissingWasmPath(
|
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.bak"),
|
|
"stale LinuxCNC missing-parameter backup remained in WASM FS",
|
|
);
|
|
await expectMissingOpfsFile(
|
|
() => simulator.fs.opfs.readFile("parameters/missing-main.var.new"),
|
|
"stale LinuxCNC missing-parameter temporary file remained in OPFS",
|
|
);
|
|
expectMissingWasmPath(
|
|
() => simulator.fs.module.FS.readFile("rs274ngc.var.new"),
|
|
"stale LinuxCNC missing-parameter temporary file remained in WASM FS",
|
|
);
|
|
});
|
|
|
|
await runSection("opfs invalid parameter restore failures", async () => {
|
|
await simulator.fs.opfs.writeFile("parameters/out-of-order.var", "5162\t1.000000\n5161\t1.000000\n");
|
|
await expectErrorContaining(
|
|
() => simulator.parseWithParameterFile(
|
|
"G21 G90\nM30\n",
|
|
"parameters/out-of-order.var",
|
|
"linuxcnc",
|
|
backend,
|
|
),
|
|
"Parameter file out of order",
|
|
"LinuxCNC accepted an out-of-order OPFS parameter file",
|
|
);
|
|
const outOfOrderParameterFile = await readOpfsText(simulator, "parameters/out-of-order.var");
|
|
expectText(outOfOrderParameterFile, "5162\t1.000000\n5161\t1.000000\n",
|
|
"LinuxCNC persisted an out-of-order OPFS parameter file after restore failure");
|
|
expectMissingWasmPath(
|
|
() => simulator.fs.module.FS.readFile("rs274ngc.var"),
|
|
"LinuxCNC out-of-order parameter file remained in WASM FS after restore failure",
|
|
);
|
|
await simulator.fs.opfs.writeFile("parameters/out-of-range.var", "0\t1.000000\n");
|
|
await expectErrorContaining(
|
|
() => simulator.parseWithParameterFile(
|
|
"G21 G90\nM30\n",
|
|
"parameters/out-of-range.var",
|
|
"linuxcnc",
|
|
backend,
|
|
),
|
|
"Parameter number out of range",
|
|
"LinuxCNC accepted an out-of-range OPFS parameter file",
|
|
);
|
|
const outOfRangeParameterFile = await readOpfsText(simulator, "parameters/out-of-range.var");
|
|
expectText(outOfRangeParameterFile, "0\t1.000000\n",
|
|
"LinuxCNC persisted an out-of-range OPFS parameter file after restore failure");
|
|
expectMissingWasmPath(
|
|
() => simulator.fs.module.FS.readFile("rs274ngc.var"),
|
|
"LinuxCNC out-of-range parameter file remained in WASM FS after restore failure",
|
|
);
|
|
await simulator.fs.opfs.writeFile("parameters/failed-parse.var", "1\t0.000000\n");
|
|
await expectErrorContaining(
|
|
() => simulator.parseWithParameterFile(
|
|
"G21 G90\nG28.1\n#0 = 1\n",
|
|
"parameters/failed-parse.var",
|
|
"linuxcnc",
|
|
backend,
|
|
),
|
|
"",
|
|
"LinuxCNC accepted a failing parse that should not persist parameters",
|
|
);
|
|
const failedParseParameterFile = await readOpfsText(simulator, "parameters/failed-parse.var");
|
|
if (failedParseParameterFile.includes("5161\t1.000000")) {
|
|
throw new Error("LinuxCNC persisted failed parse parameter updates into OPFS");
|
|
}
|
|
// LinuxCNC source basis: save_parameters() runs only after successful interpretation.
|
|
await simulator.fs.opfs.writeFile("parameters/failed-backup.var", "5161\t5.000000\n");
|
|
await simulator.fs.opfs.writeFile("parameters/failed-backup.var.bak", "5161\t4.000000\n");
|
|
await expectErrorContaining(
|
|
() => simulator.parseWithParameterFile("G21 G90\n#0 = 1\n", "parameters/failed-backup.var", "linuxcnc", backend),
|
|
"",
|
|
"LinuxCNC accepted a failing parse that should not replace the parameter backup",
|
|
);
|
|
expectText(await readOpfsText(simulator, "parameters/failed-backup.var.bak"), "5161\t4.000000\n", "LinuxCNC replaced the OPFS parameter backup after a failed parse");
|
|
});
|
|
|
|
await runSection("opfs stale parameter state cleanup", async () => {
|
|
await simulator.fs.opfs.writeFile("parameters/stale-state.var", "5161\t1.000000\n");
|
|
const staleStateEvents = await simulator.parseWithParameterFile(
|
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
|
"parameters/stale-state.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
expectEvent(staleStateEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
|
"LinuxCNC did not honor the loaded OPFS parameter state");
|
|
await simulator.fs.opfs.removeFile("parameters/stale-state.var");
|
|
const missingStateEvents = await simulator.parseWithParameterFile(
|
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
|
"parameters/stale-state.var",
|
|
"linuxcnc",
|
|
backend,
|
|
);
|
|
expectNoEvent(missingStateEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
|
"LinuxCNC stale OPFS parameter state was not cleared when the file was missing");
|
|
// LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()
|
|
// treats a missing parameter file as OK; the browser OPFS bridge
|
|
// must not leave stale default parameter files in the WASM mirror.
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var", "5161\t1.000000\n");
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale temporary parameter file\n");
|
|
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "stale backup parameter file\n");
|
|
const missingLoad = await simulator.fs.opfs.loadParameterFile("parameters/load-missing.var");
|
|
if (missingLoad !== null) {
|
|
throw new Error("missing OPFS parameter file did not return null");
|
|
}
|
|
expectWasmFilesMissing(
|
|
simulator.fs.module.FS,
|
|
["rs274ngc.var", "rs274ngc.var.new", "rs274ngc.var.bak"],
|
|
(parameterPath) => `missing OPFS parameter load left stale WASM file ${parameterPath}`,
|
|
);
|
|
});
|
|
}
|