From a0205a056b85465234739d4185a97a5b9be9faaa Mon Sep 17 00:00:00 2001 From: wangdequan Date: Mon, 8 Jun 2026 10:38:01 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=20Node=20WASM=20=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=B7=AF=E5=BE=84=E9=94=99=E8=AF=AF=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结论:Node WASM 解释器 smoke 现在通过 Interp::open/read/execute 文件路径 ABI 验证现有负向 G-code fixture 的 LinuxCNC 错误输出。 --- wasm-port/docs/compatibility-validation.md | 21 +++++++- .../tests/wasm/node/verify_interp_wasm.mjs | 54 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/wasm-port/docs/compatibility-validation.md b/wasm-port/docs/compatibility-validation.md index bc7eaf6..2240f2c 100644 --- a/wasm-port/docs/compatibility-validation.md +++ b/wasm-port/docs/compatibility-validation.md @@ -93,7 +93,10 @@ Emscripten filesystem through the SDK and runs them through LinuxCNC file execution path. The same Node smoke writes LinuxCNC-format parameter files into the Emscripten filesystem and validates vendored `Interp::restore_parameters()` and `Interp::save_parameters()`, including the -saved parameter values and `.bak` backup file boundary. +saved parameter values and `.bak` backup file boundary. It also writes the +negative G-code fixtures into the Emscripten filesystem and verifies their +LinuxCNC-produced error text through the `Interp::open()`/`read()`/`execute()` +file path. The OPFS host-boundary script validates the JavaScript file-service adapter with a Node mock of the browser File System Access handles. It covers nested @@ -260,6 +263,9 @@ path additionally covers the same canonical-event fixture group, plus `position_params` uses a dedicated file-path expectation under `tests/fixtures/canon_file/` because LinuxCNC file execution advances the post-execute position parameters differently than the line-by-line MDI smoke. +The same Node WASM file-path smoke also covers the negative fixture group and +checks the LinuxCNC file-execution error text plus absent canonical motion +constraints where applicable. The Node WASM interpreter smoke also covers LinuxCNC parameter-file restore/save behavior through the exported C ABI, including out-of-order file rejection, missing-file success, required numeric parameter writeback, removal @@ -356,6 +362,19 @@ Node WASM file-path coverage currently includes: - `position_params` through the dedicated `canon_file/` expectation - `namedparam_semantics` through the INI-aware file ABI +Node WASM file-path negative coverage currently includes every fixture under +`tests/fixtures/gcode_errors/`: + +- `g1_zero_feed` +- `arc_radius_mismatch` +- `arc_zero_radius` +- `cutter_comp_plane_change` +- `g53_incremental` +- `namedparam_readonly` +- `numbered_param_readonly` +- `tool_length_offset_not_found` +- `tool_not_found` + Browser interpreter `Interp::execute()` coverage currently includes: - `minimal_linear` diff --git a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs index 62b1e8e..0895230 100644 --- a/wasm-port/tests/wasm/node/verify_interp_wasm.mjs +++ b/wasm-port/tests/wasm/node/verify_interp_wasm.mjs @@ -34,6 +34,45 @@ function verifyExpectedOutput(fixtureName, output, expectedText) { } } +function verifyExpectedFileError(fixtureName, output, expectedText) { + assert.equal( + output.includes("file_open=0"), + true, + `${fixtureName}: file open`, + ); + + const expectedLines = expectedText.split("\n").filter(Boolean); + for (const expectedLine of expectedLines) { + if (expectedLine.startsWith("absent=")) { + const forbidden = expectedLine.slice("absent=".length); + assert.equal( + output.includes(forbidden), + false, + `${fixtureName}: unexpected ${forbidden}`, + ); + continue; + } + + if (expectedLine.startsWith("error_text=")) { + const message = expectedLine.slice("error_text=".length); + assert.equal( + output.includes(`file_error_text=${message}`), + true, + `${fixtureName}: ${message}`, + ); + continue; + } + + if (expectedLine.startsWith("canon_event=")) { + assert.equal( + output.includes(expectedLine), + true, + `${fixtureName}: ${expectedLine}`, + ); + } + } +} + const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const rootDir = resolve(__dirname, "../../.."); @@ -92,6 +131,21 @@ for (const fixtureName of INTERP_ERROR_FIXTURES) { verifyExpectedOutput(fixtureName, interp.runProgram(programText), expectedOutput); } +for (const fixtureName of INTERP_ERROR_FIXTURES) { + const programPath = `/work/${fixtureName}-error.ngc`; + const programText = readFileSync( + resolve(rootDir, `tests/fixtures/gcode_errors/${fixtureName}.ngc`), + "utf8", + ); + const expectedOutput = readFileSync( + resolve(rootDir, `tests/fixtures/canon_errors/${fixtureName}.expected`), + "utf8", + ).trimEnd(); + + interp.writeTextFile(programPath, programText); + verifyExpectedFileError(fixtureName, interp.runFile(programPath), expectedOutput); +} + for (const fixtureName of INTERP_FILE_FIXTURES) { const programPath = `/work/${fixtureName}.ngc`; const programText = readFileSync(