按规划继续工作

结论:补齐 OPFS 会话快照自定义文件名与非法快照文件名拒绝验证,覆盖 Node OPFS 与 Chromium OPFS 路径,并通过 host/WASM/browser 聚合验证。
This commit is contained in:
2026-06-08 09:21:00 +08:00
parent 8c7c2f1079
commit 78f54bb64c
5 changed files with 93 additions and 15 deletions

View File

@@ -100,9 +100,9 @@ paths, unavailable OPFS storage, and the host-side OPFS path model for INI,
tool table, parameter, G-code, preview-cache, and session-snapshot storage
targets. It also validates the host-side session snapshot JSON envelope and
round-trip store, including unsupported format/version, session-id mismatch,
and non-object metadata/payload rejection, plus pure-text machine file and
G-code stores without defining CNC machine-state or file-format semantics. It
now also validates the
non-object metadata/payload rejection, custom snapshot filenames, and invalid
snapshot filename rejection, plus pure-text machine file and G-code stores
without defining CNC machine-state or file-format semantics. It now also validates the
OPFS-to-WASM parameter-file bridge with a mock interpreter SDK to ensure the
host boundary copies text into and out of the WASM filesystem without defining
parameter semantics, and that INI-derived machine file names are still
@@ -114,8 +114,8 @@ keeping host override policy outside CNC semantics.
The browser INI/OPFS smoke script serves `wasm-port/` over localhost and runs
Chromium headless against a test page that imports the JS SDK, loads the INI
WASM module, queries vendored LinuxCNC INI parsing through the SDK, and
performs an OPFS text-file, generic session snapshot, invalid snapshot
envelope, machine file, and G-code text round trip.
performs an OPFS text-file, generic session snapshot, custom snapshot filename,
invalid snapshot filename/envelope, machine file, and G-code text round trip.
The browser interpreter smoke script serves `wasm-port/` over localhost and
runs Chromium headless against a test page that loads the interpreter-core
@@ -186,8 +186,8 @@ The validation fails if:
| --- | --- |
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI. |
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the initial interpreter-core WASM module can be built from vendored LinuxCNC interpreter source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including envelope rejection paths, machine file store, G-code text store, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, OPFS text-file round trip, generic session snapshot round trip plus envelope rejection paths, machine file text round trip, G-code text round trip, and the INI panel UI's machine-session load, G-code run, and canonical-event display paths in a real browser runtime. |
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip, and the INI panel UI's machine-session load, G-code run, and canonical-event display paths in a real browser runtime. |
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures through vendored LinuxCNC `Interp::execute()` plus `Interp::open()`/`read()`/`execute()` via the exported C ABI, including OPFS-backed parameter-file restore/save and tool-table load/save through vendored LinuxCNC source. |
| `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, OPFS, and browser smoke validation with shared WASM builds. |
@@ -270,10 +270,11 @@ derived from vendored LinuxCNC INI boolean parsing, INI-derived
`[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE` file names mapped to OPFS
machine files, OPFS path-model rejection of invalid INI-derived file names,
explicit host session file-name overrides taking precedence over INI-derived
names, session snapshot envelope rejection for unsupported format/version and
wrong session id, and a browser interpreter smoke that uses the same session
bridge with real LinuxCNC INI WASM parsing before saving OPFS-backed parameter
and tool-table text through vendored LinuxCNC file APIs.
names, session snapshot custom filename handling plus envelope/path rejection
for unsupported format/version, wrong session id, and invalid snapshot
filenames, and a browser interpreter smoke that uses the same session bridge
with real LinuxCNC INI WASM parsing before saving OPFS-backed parameter and
tool-table text through vendored LinuxCNC file APIs.
Full browser coverage, full SDK coverage, and richer machine-state validation
remain future work.

View File

@@ -71,8 +71,8 @@ semantic rewrites:
file and G-code storage, parameter/tool-table bridge copying into the
interpreter SDK filesystem, grouped machine-session loading, explicit
session file-name overrides, invalid INI-derived machine file names, generic
session snapshot envelope rejection paths, and a Chromium localhost round
trip for those persistence paths.
session snapshot custom filenames plus envelope/path rejection paths, and a
Chromium localhost round trip for those persistence paths.
- Host-side smoke validation is aggregated by
`tests/host/verify_host_smokes.sh` so Node, WASM, OPFS, and browser checks
run from one command.

View File

@@ -83,8 +83,9 @@ Current validation is intentionally mechanical:
`runtime/opfs/file-service.js` adapter. `runtime/opfs/path-model.js` now
defines paths for INI, tool table, parameter file, G-code program,
preview-cache, and session-snapshot targets. `runtime/opfs/snapshot-store.js`
adds a generic JSON session snapshot envelope and validates its format,
version, session id, metadata, and payload shape.
adds a generic JSON session snapshot envelope, supports custom snapshot
filenames under the session directory, and validates its format, version,
session id, metadata, payload shape, and filename boundary.
`runtime/opfs/machine-file-store.js` adds pure-text storage for INI, tool
table, parameter file, and G-code program content.
`runtime/opfs/linuxcnc-parameter-bridge.js` copies OPFS-backed parameter

View File

@@ -116,6 +116,43 @@ JOINTS = 3
assertEqual(loadedSnapshot.createdAt, savedSnapshot.createdAt, "snapshot timestamp");
assertEqual(loadedSnapshot.payload.files.ini, opfsPath, "snapshot payload");
assertEqual(loadedSnapshot.metadata.source, "browser-smoke", "snapshot metadata");
assertEqual(
sessionSnapshotPath("browser-session", "browser-custom-snapshot.json"),
"linuxcnc/sessions/browser-session/browser-custom-snapshot.json",
"snapshot custom path",
);
const customSnapshot = await saveSessionSnapshot(
"browser-session",
snapshotPayload,
{
filename: "browser-custom-snapshot.json",
createdAt: "2026-06-08T00:00:00.000Z",
metadata: { source: "browser-smoke-custom" },
},
);
const loadedCustomSnapshot = await loadSessionSnapshot(
"browser-session",
{ filename: "browser-custom-snapshot.json" },
);
assertEqual(
loadedCustomSnapshot.metadata.source,
customSnapshot.metadata.source,
"custom snapshot metadata",
);
await assertRejects(
"browser snapshot invalid filename save",
() => saveSessionSnapshot("browser-session", {}, {
filename: "nested/snapshot.json",
}),
/Invalid session snapshot filename/,
);
await assertRejects(
"browser snapshot invalid filename load",
() => loadSessionSnapshot("browser-session", {
filename: "../snapshot.json",
}),
/Invalid session snapshot filename/,
);
await saveTextFile(
sessionSnapshotPath("browser-bad-format"),
JSON.stringify({

View File

@@ -104,6 +104,10 @@ assert.equal(parameterFilePath("xyzab-tdr"), "linuxcnc/machines/xyzab-tdr/linuxc
assert.equal(gcodeProgramPath("fixture.ngc"), "linuxcnc/gcode/fixture.ngc");
assert.equal(previewCachePath("fixture"), "linuxcnc/preview-cache/fixture/preview.json");
assert.equal(sessionSnapshotPath("session-1"), "linuxcnc/sessions/session-1/snapshot.json");
assert.equal(
sessionSnapshotPath("session-1", "custom-snapshot.json"),
"linuxcnc/sessions/session-1/custom-snapshot.json",
);
assert.deepEqual(defaultMachinePaths("xyzab-tdr"), {
ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
@@ -167,6 +171,23 @@ await saveSessionSnapshot("session-1", snapshotPayload, {
metadata: { source: "node-smoke" },
});
assert.deepEqual(await loadSessionSnapshot("session-1", { storage }), snapshot);
const customSnapshot = await saveSessionSnapshot("session-1", snapshotPayload, {
storage,
filename: "custom-snapshot.json",
createdAt: "2026-06-08T00:00:00.000Z",
metadata: { source: "node-smoke-custom" },
});
assert.equal(
customSnapshot.metadata.source,
"node-smoke-custom",
);
assert.deepEqual(
await loadSessionSnapshot("session-1", {
storage,
filename: "custom-snapshot.json",
}),
customSnapshot,
);
await saveMachineTextFiles("xyzab-tdr", {
ini: "[EMC]\nMACHINE = xyzab-tdr\n",
@@ -569,6 +590,24 @@ assert.throws(
() => machineIniPath("../escape"),
/Invalid machine id/,
);
assert.throws(
() => sessionSnapshotPath("session-1", "nested/snapshot.json"),
/Invalid session snapshot filename/,
);
await assert.rejects(
() => saveSessionSnapshot("session-1", {}, {
storage,
filename: "nested/snapshot.json",
}),
/Invalid session snapshot filename/,
);
await assert.rejects(
() => loadSessionSnapshot("session-1", {
storage,
filename: "../snapshot.json",
}),
/Invalid session snapshot filename/,
);
await assert.rejects(
() => saveMachineTextFiles("xyzab-tdr", null, { storage }),
/machine files must be a plain object/,