Files
wasm-simulator/web/test-browser-wasm-smoke-opfs-directory-sections.js
cnc 0d4e248811 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 align-linuxcnc 约束收窄 OPFS removeFile 和 LinuxCNC 参数 .new/.bak 清理为文件专用语义,避免把同名目录递归删除。依据 ../linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc save_parameters() 的 fopen(filename.new)、unlink(filename.bak)、link、rename 行为。

验证:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-03 13:28:15 +08:00

191 lines
11 KiB
JavaScript

export async function runBrowserOpfsDirectorySections(context, simulator) {
const {
expectFileList,
expectMissingOpfsFile,
expectMissingWasmPath,
expectText,
runSection,
withSmokeSimulator,
} = context;
await runSection("opfs directory persist and load", async () => {
const fixturePath = simulator.fs.opfs.resolvePath("fixtures");
simulator.fs.module.FS.mkdirTree(`${fixturePath}/nested`);
simulator.fs.module.FS.mkdirTree(`${fixturePath}/empty/nested`);
simulator.fs.module.FS.writeFile(`${fixturePath}/nested/fixture.ngc`, "G21 G90\nG0 X3\n");
simulator.fs.module.FS.writeFile(`${fixturePath}/root.ngc`, "G21 G90\nG0 X4\n");
const persistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
const fixtureStat = await simulator.fs.opfs.stat("fixtures");
if (fixtureStat.kind !== "directory" || fixtureStat.size !== null) {
throw new Error("OPFS stat did not report a persisted directory");
}
expectFileList(persistedFiles, ["nested/fixture.ngc", "root.ngc"],
"unexpected OPFS directory persist file list");
const fixtureDecoded = new TextDecoder().decode(
await simulator.fs.opfs.readFile("fixtures/nested/fixture.ngc"),
);
expectText(fixtureDecoded, "G21 G90\nG0 X3\n",
"persisted WASM filesystem directory file was not readable from OPFS");
const emptyFixtureStat = await simulator.fs.opfs.stat("fixtures/empty/nested");
if (emptyFixtureStat.kind !== "directory" || emptyFixtureStat.size !== null) {
throw new Error("OPFS persistDirectory did not preserve an empty directory");
}
await withSmokeSimulator(async (staleDirectorySimulator) => {
await staleDirectorySimulator.fs.opfs.writeFile("fixtures/stale.ngc", "G21 G90\nG0 X6\n");
});
simulator.fs.module.FS.unlink(`${fixturePath}/nested/fixture.ngc`);
const refreshedPersistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
expectFileList(refreshedPersistedFiles, ["root.ngc"],
"unexpected refreshed OPFS directory persist file list");
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("fixtures/stale.ngc"),
"stale OPFS directory file remained after directory persist",
);
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("fixtures/nested/fixture.ngc"),
"removed WASM directory file remained in OPFS after directory persist",
);
simulator.fs.module.FS.writeFile(`${fixturePath}/nested/fixture.ngc`, "G21 G90\nG0 X3\n");
const restoredPersistedFiles = await simulator.fs.opfs.persistDirectory("fixtures");
expectFileList(restoredPersistedFiles, ["nested/fixture.ngc", "root.ngc"],
"unexpected restored OPFS directory persist file list");
const loadedFiles = await simulator.fs.opfs.readDirectory("fixtures");
expectFileList(loadedFiles, ["nested/fixture.ngc", "root.ngc"],
"unexpected OPFS directory load file list");
const loadedRootPath = simulator.fs.opfs.resolvePath("fixtures/root.ngc");
const loadedRootDecoded = new TextDecoder().decode(simulator.fs.module.FS.readFile(loadedRootPath));
expectText(loadedRootDecoded, "G21 G90\nG0 X4\n",
"OPFS directory file was not mirrored into the WASM filesystem");
const loadedEmptyPath = simulator.fs.opfs.resolvePath("fixtures/empty/nested");
const loadedEmptyMode = simulator.fs.module.FS.stat(loadedEmptyPath).mode;
if (!simulator.fs.module.FS.isDir(loadedEmptyMode)) {
throw new Error("OPFS readDirectory did not mirror an empty directory into WASM FS");
}
});
await runSection("opfs directory refresh and type conflicts", async () => {
const loadedRootPath = simulator.fs.opfs.resolvePath("fixtures/root.ngc");
await withSmokeSimulator(async (updateDirectorySimulator) => {
await updateDirectorySimulator.fs.opfs.removeFile("fixtures/root.ngc");
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/nested/fixture.ngc", "G21 G90\nG0 X5\n");
const refreshedFiles = await simulator.fs.opfs.readDirectory("fixtures");
expectFileList(refreshedFiles, ["nested/fixture.ngc"],
"unexpected refreshed OPFS directory file list");
expectMissingWasmPath(
() => simulator.fs.module.FS.stat(loadedRootPath),
"stale OPFS directory mirror file remained in WASM FS",
);
const refreshedFixturePath = simulator.fs.opfs.resolvePath("fixtures/nested/fixture.ngc");
const refreshedFixture = new TextDecoder().decode(
simulator.fs.module.FS.readFile(refreshedFixturePath),
);
expectText(refreshedFixture, "G21 G90\nG0 X5\n",
"OPFS directory refresh did not replace the WASM mirror file");
await simulator.fs.opfs.writeFile("fixtures/type-conflict/original.ngc", "G21 G90\n");
await updateDirectorySimulator.fs.opfs.removeDirectory("fixtures/type-conflict");
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/type-conflict", "G21 G90\nG0 X11\n");
const replacedConflictFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("fixtures/type-conflict"),
);
expectText(replacedConflictFile, "G21 G90\nG0 X11\n",
"OPFS readFile did not load a file that replaced a directory");
const conflictMirrorPath = simulator.fs.opfs.resolvePath("fixtures/type-conflict");
const conflictMirrorMode = simulator.fs.module.FS.stat(conflictMirrorPath).mode;
if (!simulator.fs.module.FS.isFile(conflictMirrorMode)) {
throw new Error("OPFS readFile did not replace the WASM mirror directory with a file");
}
await updateDirectorySimulator.fs.opfs.removeFile("fixtures/type-conflict");
await updateDirectorySimulator.fs.opfs.writeFile(
"fixtures/type-conflict/original.ngc",
"G21 G90\n",
);
await updateDirectorySimulator.fs.opfs.removeDirectory("fixtures/type-conflict");
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/root.ngc", "G21 G90\nG0 X4\n");
await updateDirectorySimulator.fs.opfs.writeFile("fixtures/nested/fixture.ngc", "G21 G90\nG0 X3\n");
const restoredFiles = await simulator.fs.opfs.readDirectory("fixtures");
expectFileList(restoredFiles, ["nested/fixture.ngc", "root.ngc"],
"unexpected reloaded OPFS directory file list");
});
});
await runSection("opfs directory restore in new instance", async () => {
await withSmokeSimulator(async (restoredDirectorySimulator) => {
const restoredDirectoryFiles = await restoredDirectorySimulator.fs.opfs.readDirectory("fixtures");
expectFileList(restoredDirectoryFiles, ["nested/fixture.ngc", "root.ngc"],
"unexpected restored OPFS directory file list");
const restoredFixtureRootPath = restoredDirectorySimulator.fs.opfs.resolvePath("fixtures/root.ngc");
const restoredFixtureRoot = new TextDecoder().decode(
restoredDirectorySimulator.fs.module.FS.readFile(restoredFixtureRootPath),
);
expectText(restoredFixtureRoot, "G21 G90\nG0 X4\n",
"OPFS directory was not restored into a new WASM filesystem instance");
const restoredEmptyFixturePath = restoredDirectorySimulator.fs.opfs.resolvePath("fixtures/empty/nested");
const restoredEmptyFixtureMode =
restoredDirectorySimulator.fs.module.FS.stat(restoredEmptyFixturePath).mode;
if (!restoredDirectorySimulator.fs.module.FS.isDir(restoredEmptyFixtureMode)) {
throw new Error("OPFS empty directory was not restored into a new WASM filesystem instance");
}
});
});
await runSection("opfs metadata queries avoid wasm mirror pollution", async () => {
await withSmokeSimulator(async (existsOnlySimulator) => {
await existsOnlySimulator.fs.opfs.writeFile("programs/exists-only.ngc", "G21 G90\nG0 X10\n");
const existsOnlyPath = simulator.fs.opfs.resolvePath("programs/exists-only.ngc");
if (!(await simulator.fs.opfs.exists("programs/exists-only.ngc"))) {
throw new Error("OPFS exists did not see a file written by another simulator instance");
}
const existsOnlyStat = await simulator.fs.opfs.stat("programs/exists-only.ngc");
if (existsOnlyStat.kind !== "file" || existsOnlyStat.size !== 15) {
throw new Error("OPFS stat did not see a file written by another simulator instance");
}
expectMissingWasmPath(
() => simulator.fs.module.FS.stat(existsOnlyPath),
"OPFS metadata queries polluted the WASM filesystem mirror",
);
});
});
await runSection("opfs workspace clear and mirror removal", async () => {
await simulator.fs.opfs.clear();
await withSmokeSimulator(async (clearedWorkspaceSimulator) => {
await expectMissingOpfsFile(
() => clearedWorkspaceSimulator.fs.opfs.readDirectory("fixtures"),
"cleared OPFS workspace still exposed a persisted directory",
);
});
await simulator.fs.opfs.removeFile("programs/browser-smoke.ngc");
if (await simulator.fs.opfs.exists("programs/browser-smoke.ngc")) {
throw new Error("OPFS exists reported a removed file");
}
const removedProgramPath = simulator.fs.opfs.resolvePath("programs/browser-smoke.ngc");
expectMissingWasmPath(
() => simulator.fs.module.FS.stat(removedProgramPath),
"OPFS removeFile did not remove the WASM mirror",
);
const fixturePath = simulator.fs.opfs.resolvePath("fixtures");
await simulator.fs.opfs.removeDirectory("fixtures");
await simulator.fs.opfs.removeDirectory("fixtures");
expectMissingWasmPath(
() => simulator.fs.module.FS.stat(fixturePath),
"OPFS removeDirectory did not remove the WASM mirror",
);
});
await runSection("opfs file removal preserves directories", async () => {
await simulator.fs.opfs.writeFile("parameters/remove-file-keeps-directory.var.new/stale.ngc", "G21 G90\n");
const staleDirectoryPath = simulator.fs.opfs.resolvePath("parameters/remove-file-keeps-directory.var.new");
await simulator.fs.opfs.readDirectory("parameters/remove-file-keeps-directory.var.new");
await simulator.fs.opfs.removeFile("parameters/remove-file-keeps-directory.var.new");
const staleDirectoryStat = await simulator.fs.opfs.stat("parameters/remove-file-keeps-directory.var.new");
if (staleDirectoryStat.kind !== "directory" || staleDirectoryStat.size !== null) {
throw new Error("OPFS removeFile removed a directory instead of preserving file-only semantics");
}
const staleDirectoryMode = simulator.fs.module.FS.stat(staleDirectoryPath).mode;
if (!simulator.fs.module.FS.isDir(staleDirectoryMode)) {
throw new Error("OPFS removeFile removed a WASM mirror directory instead of preserving file-only semantics");
}
await simulator.fs.opfs.removeDirectory("parameters/remove-file-keeps-directory.var.new");
});
}