diff --git a/wasm-port/docs/compatibility-validation.md b/wasm-port/docs/compatibility-validation.md index 2240f2c..83e5277 100644 --- a/wasm-port/docs/compatibility-validation.md +++ b/wasm-port/docs/compatibility-validation.md @@ -263,9 +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 same Node WASM and browser file-path smokes also cover the negative fixture +group and check 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 @@ -375,6 +375,19 @@ Node WASM file-path negative coverage currently includes every fixture under - `tool_length_offset_not_found` - `tool_not_found` +Browser interpreter file-path negative coverage currently includes the same +negative fixture list through `Interp::open()`/`read()`/`execute()`: + +- `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/browser/interp_smoke.html b/wasm-port/tests/browser/interp_smoke.html index 7911a9c..fac4488 100644 --- a/wasm-port/tests/browser/interp_smoke.html +++ b/wasm-port/tests/browser/interp_smoke.html @@ -61,6 +61,35 @@ } } + function verifyExpectedFileError(fixtureName, output, expectedText) { + if (!output.includes("file_open=0")) { + throw new Error(`${fixtureName}: missing file_open=0`); + } + + const expectedLines = expectedText.split("\n").filter(Boolean); + for (const expectedLine of expectedLines) { + if (expectedLine.startsWith("absent=")) { + const forbidden = expectedLine.slice("absent=".length); + if (output.includes(forbidden)) { + throw new Error(`${fixtureName}: unexpected ${forbidden}`); + } + continue; + } + + if (expectedLine.startsWith("error_text=")) { + const message = expectedLine.slice("error_text=".length); + if (!output.includes(`file_error_text=${message}`)) { + throw new Error(`${fixtureName}: missing file error ${message}`); + } + continue; + } + + if (expectedLine.startsWith("canon_event=") && !output.includes(expectedLine)) { + throw new Error(`${fixtureName}: missing ${expectedLine}`); + } + } + } + async function verifyRejects(fixtureName, operation, expectedPattern) { try { await operation(); @@ -113,6 +142,19 @@ ); } + for (const errorFixtureName of INTERP_ERROR_FIXTURES) { + const programPath = `/work/${errorFixtureName}-error.ngc`; + interp.writeTextFile( + programPath, + await fetchText(`../fixtures/gcode_errors/${errorFixtureName}.ngc`), + ); + verifyExpectedFileError( + `${errorFixtureName}_file`, + interp.runFile(programPath), + (await fetchText(`../fixtures/canon_errors/${errorFixtureName}.expected`)).trimEnd(), + ); + } + const namedParamIniPath = "/work/namedparams.ini"; const namedParamProgramText = await fetchText( `../fixtures/gcode/${INTERP_INI_FIXTURE}.ngc`,