参考所有分组:保持OPFS备份失败非致命
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters() 中 unlink/link 失败不阻止 rename 主参数文件的语义,OPFS 参数 .bak 写入失败时不让解析保存失败,并避免把目录当作可 unlink 的备份文件删除;未扩展 smoke 解析行为。 验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-browser-smoke.sh。
This commit is contained in:
@@ -141,6 +141,21 @@ async function removeOpfsEntry(workspacePath, relativePath, recursive) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeOpfsFileEntry(workspacePath, relativePath) {
|
||||||
|
assertRelativePath(relativePath);
|
||||||
|
const parts = relativePath.split("/");
|
||||||
|
const entryName = parts.pop();
|
||||||
|
try {
|
||||||
|
const directory = await getOpfsDirectoryHandle([workspacePath, ...parts].filter(Boolean).join("/"), false);
|
||||||
|
await directory.getFileHandle(entryName, { create: false });
|
||||||
|
await directory.removeEntry(entryName, { recursive: false });
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError" && error?.name !== "TypeMismatchError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function opfsEntryExists(workspacePath, relativePath) {
|
async function opfsEntryExists(workspacePath, relativePath) {
|
||||||
assertRelativePath(relativePath);
|
assertRelativePath(relativePath);
|
||||||
const parts = relativePath.split("/");
|
const parts = relativePath.split("/");
|
||||||
@@ -498,7 +513,7 @@ function installOpfsWorkspace(module, options) {
|
|||||||
if (backupData !== null) {
|
if (backupData !== null) {
|
||||||
await writeOpfsFile(workspacePath, `${path}.bak`, backupData);
|
await writeOpfsFile(workspacePath, `${path}.bak`, backupData);
|
||||||
} else {
|
} else {
|
||||||
await removeOpfsEntry(workspacePath, `${path}.bak`, false);
|
await removeOpfsFileEntry(workspacePath, `${path}.bak`);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
@@ -712,9 +727,22 @@ export async function createWasmSimulator(moduleOptions = {}) {
|
|||||||
// replacing filename. Browser filesystems may not expose that link
|
// replacing filename. Browser filesystems may not expose that link
|
||||||
// through the WASM mirror, so preserve the loaded OPFS file here.
|
// through the WASM mirror, so preserve the loaded OPFS file here.
|
||||||
if (previousParameterFile === null) {
|
if (previousParameterFile === null) {
|
||||||
|
try {
|
||||||
|
const backupStat = await opfs.stat(`${parameterPath}.bak`);
|
||||||
|
if (backupStat.kind === "file") {
|
||||||
await opfs.removeFile(`${parameterPath}.bak`);
|
await opfs.removeFile(`${parameterPath}.bak`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.name !== "NotFoundError") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
try {
|
||||||
await opfs.writeFile(`${parameterPath}.bak`, previousParameterFile);
|
await opfs.writeFile(`${parameterPath}.bak`, previousParameterFile);
|
||||||
|
} catch (_backupError) {
|
||||||
|
// LinuxCNC treats link(filename, filename + ".bak") failure as non-fatal.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return events;
|
return events;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
expectMissingOpfsFile,
|
expectMissingOpfsFile,
|
||||||
expectMissingWasmPath,
|
expectMissingWasmPath,
|
||||||
expectNoEvent,
|
expectNoEvent,
|
||||||
|
expectOpfsDirectoryStat,
|
||||||
expectText,
|
expectText,
|
||||||
expectWasmFilesMissing,
|
expectWasmFilesMissing,
|
||||||
near,
|
near,
|
||||||
@@ -15,14 +16,11 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
withSmokeSimulator,
|
withSmokeSimulator,
|
||||||
} = context;
|
} = context;
|
||||||
const backend = { backend: "linuxcnc-rs274" };
|
const backend = { backend: "linuxcnc-rs274" };
|
||||||
|
const parseWithParameters = (program, path) =>
|
||||||
|
simulator.parseWithParameterFile(program, path, "linuxcnc", backend);
|
||||||
|
|
||||||
await runSection("opfs parameter save", async () => {
|
await runSection("opfs parameter save", async () => {
|
||||||
await simulator.parseWithParameterFile(
|
await simulator.parseWithParameterFile("G21 G90\nG0 X1\nG28.1\nM30\n", "parameters/rs274ngc.var", "linuxcnc", backend);
|
||||||
"G21 G90\nG0 X1\nG28.1\nM30\n",
|
|
||||||
"parameters/rs274ngc.var",
|
|
||||||
"linuxcnc",
|
|
||||||
backend,
|
|
||||||
);
|
|
||||||
const opfsParameterFile = await readOpfsText(simulator, "parameters/rs274ngc.var");
|
const opfsParameterFile = await readOpfsText(simulator, "parameters/rs274ngc.var");
|
||||||
expectIncludes(opfsParameterFile, "5161\t1.000000", "LinuxCNC parameter file was not persisted into OPFS");
|
expectIncludes(opfsParameterFile, "5161\t1.000000", "LinuxCNC parameter file was not persisted into OPFS");
|
||||||
});
|
});
|
||||||
@@ -45,12 +43,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
await runSection("opfs default parameter backup", async () => {
|
await runSection("opfs default parameter backup", async () => {
|
||||||
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
|
||||||
// preserves the previous default parameter file as filename + ".bak".
|
// preserves the previous default parameter file as filename + ".bak".
|
||||||
await simulator.parseWithParameterFile(
|
await parseWithParameters("G21 G90\nM30\n", "parameters/rs274ngc.var");
|
||||||
"G21 G90\nM30\n",
|
|
||||||
"parameters/rs274ngc.var",
|
|
||||||
"linuxcnc",
|
|
||||||
backend,
|
|
||||||
);
|
|
||||||
const defaultParameterBackup = await readOpfsText(simulator, "parameters/rs274ngc.var.bak");
|
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");
|
expectIncludes(defaultParameterBackup, "5161\t1.000000", "LinuxCNC default OPFS parameter backup did not preserve the previous file");
|
||||||
});
|
});
|
||||||
@@ -60,11 +53,9 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes
|
||||||
// filename + ".new", then renames it over the parameter file.
|
// filename + ".new", then renames it over the parameter file.
|
||||||
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary parameter file\n");
|
simulator.fs.module.FS.writeFile("rs274ngc.var.new", "stale wasm temporary parameter file\n");
|
||||||
const restoredParameterEvents = await simulator.parseWithParameterFile(
|
const restoredParameterEvents = await parseWithParameters(
|
||||||
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X9\nO10 endif\n",
|
||||||
"parameters/rs274ngc.var",
|
"parameters/rs274ngc.var",
|
||||||
"linuxcnc",
|
|
||||||
backend,
|
|
||||||
);
|
);
|
||||||
expectEvent(restoredParameterEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
expectEvent(restoredParameterEvents, (event) => event.type === "linear-feed" && near(event.end.x, 9),
|
||||||
"LinuxCNC parameter file was not restored from OPFS");
|
"LinuxCNC parameter file was not restored from OPFS");
|
||||||
@@ -97,12 +88,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
|
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
|
||||||
// filename + ".bak", links the current parameter file to it,
|
// filename + ".bak", links the current parameter file to it,
|
||||||
// then renames filename + ".new" over filename.
|
// then renames filename + ".new" over filename.
|
||||||
await simulator.parseWithParameterFile(
|
await parseWithParameters("G21 G90\nG0 X4\nG28.1\nM30\n", "parameters/existing-save.var");
|
||||||
"G21 G90\nG0 X4\nG28.1\nM30\n",
|
|
||||||
"parameters/existing-save.var",
|
|
||||||
"linuxcnc",
|
|
||||||
backend,
|
|
||||||
);
|
|
||||||
const existingSaveParameterFile = await readOpfsText(simulator, "parameters/existing-save.var");
|
const existingSaveParameterFile = await readOpfsText(simulator, "parameters/existing-save.var");
|
||||||
expectIncludes(existingSaveParameterFile, "5161\t4.000000", "LinuxCNC existing OPFS parameter file was not replaced after save");
|
expectIncludes(existingSaveParameterFile, "5161\t4.000000", "LinuxCNC existing OPFS parameter file was not replaced after save");
|
||||||
const existingSaveParameterBackup = await readOpfsText(simulator, "parameters/existing-save.var.bak");
|
const existingSaveParameterBackup = await readOpfsText(simulator, "parameters/existing-save.var.bak");
|
||||||
@@ -111,6 +97,15 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
|
|||||||
() => simulator.fs.opfs.readFile("parameters/existing-save.var.new"),
|
() => simulator.fs.opfs.readFile("parameters/existing-save.var.new"),
|
||||||
"LinuxCNC existing OPFS parameter temporary file remained after save",
|
"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 runSection("opfs parameter stale backup cleanup", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user