diff --git a/check-linuxcnc-wasm-smoke-source-map.sh b/check-linuxcnc-wasm-smoke-source-map.sh new file mode 100755 index 0000000..55ae506 --- /dev/null +++ b/check-linuxcnc-wasm-smoke-source-map.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +# LinuxCNC source basis: this guard ties Node/browser WASM smoke coverage to +# LinuxCNC RS274 parameter/INI handling, M-code canon side effects, generated +# switchkins/remap config cases, kinematics sources, and OPFS-only browser +# persistence. It validates smoke-test source coverage only; it does not add +# CNC behavior or expand smoke parsing. +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +grep -F 'int Interp::ini_load(const char *filename)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'int Interp::restore_parameters(const char *filename)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'int Interp::save_parameters(const char *filename,' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'std::string tempfile = std::string(filename) + ".new";' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'int read_text(const char *command, FILE * inport, char *raw_line,' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_interp.hh" >/dev/null + +grep -F 'int Interp::convert_m(block_pointer block' \ + "$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null +grep -F 'int ret = WAIT(round_to_int(block->p_number), DIGITAL_INPUT, type, timeout);' \ + "$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null +grep -F 'SET_AUX_OUTPUT_VALUE(round_to_int(block->e_number), block->q_number);' \ + "$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null +grep -F 'extern int WAIT(int index' "$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null +grep -F 'extern void SET_AUX_OUTPUT_VALUE(int index, double value);' \ + "$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null + +grep -F 'kp->kinsname = "5axiskins";' "$linuxcnc_root/src/emc/kinematics/5axiskins.c" >/dev/null +grep -F 'int xyzbcKinematicsInverse(const EmcPose * pos' \ + "$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null +grep -F 'int xyzacKinematicsInverse(const EmcPose * pos' \ + "$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null +grep -F '*kinv1 = xyzacKinematicsInverse;' \ + "$linuxcnc_root/src/emc/kinematics/xyzac-trt-kins.c" >/dev/null +grep -F '*kinv1 = xyzbcKinematicsInverse;' \ + "$linuxcnc_root/src/emc/kinematics/xyzbc-trt-kins.c" >/dev/null +grep -F 'int userkKinematicsInverse(const EmcPose * pos' \ + "$linuxcnc_root/src/emc/kinematics/userkfuncs.c" >/dev/null + +grep -F 'LinuxCNC WASM smoke source map' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'OPFS-only' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F '`src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage' \ + docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F '`src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`' \ + docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'Generated switchkins config smoke cases must continue to come from' \ + docs/linuxcnc-wasm-smoke-source-map.md >/dev/null + +grep -F 'LinuxCNC source basis: Node smoke only verifies the wasm/API bridge around' \ + test-web-wasm-node-smoke.sh >/dev/null +grep -F 'minimum_steps=${WASM_NODE_SMOKE_MIN_STEPS:-50}' test-web-wasm-node-smoke.sh >/dev/null +grep -F 'WASM Node smoke covered $passed_steps steps, expected at least $minimum_steps' \ + test-web-wasm-node-smoke.sh >/dev/null +grep -F 'expected Node wasm-core wrapper to leave OPFS disabled' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected Node wasm-core parseFile to require OPFS' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'linuxcnc_switchkins_remap_config_cases.json' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect' \ + test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected LinuxCNC WASM M66 E0 L0 remap side effect' \ + test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse' \ + test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse' \ + test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse' \ + test-web-wasm-node-smoke.cjs >/dev/null + +grep -F 'LinuxCNC source basis: browser smoke verifies OPFS-backed wasm/API/app flows' \ + test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'minimum_sections=${BROWSER_SMOKE_MIN_SECTIONS:-50}' test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'BROWSER_SMOKE_MIN_SECTIONS must be a positive integer' test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'browser WASM smoke covered $passed_sections sections, expected at least $minimum_sections' \ + test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'runLinuxCncBrowserSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null +grep -F 'runBrowserOpfsWorkspaceSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null +grep -F 'runBrowserAppOpfsSections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null +grep -F 'linuxcnc_switchkins_remap_config_cases.json' \ + web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null +grep -F 'missing LinuxCNC switchkins config cases after INI_FILE_NAME remap-load filtering' \ + web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null +grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()' \ + web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null +grep -F 'LinuxCNC source basis: rs274ngc_pre.cc save_parameters()' \ + web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null +grep -F 'LinuxCNC source basis: rs274ngc_pre.cc read_text() reads' \ + web/test-browser-wasm-smoke-opfs-basic-sections.js >/dev/null +grep -F 'app frame did not persist current program into OPFS' \ + web/test-browser-wasm-smoke-app-sections.js >/dev/null +grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \ + web/test-browser-wasm-smoke-app-sections.js >/dev/null + +if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then + echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2 + exit 1 +fi + +python3 - <<'PY' +from pathlib import Path +import json +import re + +source_map = Path("docs/linuxcnc-wasm-smoke-source-map.md").read_text(encoding="utf-8") +json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8")) +if not isinstance(json_cases, list) or len(json_cases) == 0: + raise SystemExit("generated LinuxCNC switchkins browser config cases are missing") + +node_wrapper = Path("test-web-wasm-node-smoke.sh").read_text(encoding="utf-8") +browser_wrapper = Path("test-web-wasm-browser-smoke.sh").read_text(encoding="utf-8") +node_min = re.search(r"minimum_steps=\$\{WASM_NODE_SMOKE_MIN_STEPS:-(\d+)\}", node_wrapper) +browser_min = re.search(r"minimum_sections=\$\{BROWSER_SMOKE_MIN_SECTIONS:-(\d+)\}", browser_wrapper) +if not node_min or int(node_min.group(1)) < 50: + raise SystemExit("Node WASM smoke minimum step threshold is missing or below 50") +if not browser_min or int(browser_min.group(1)) < 50: + raise SystemExit("browser WASM smoke minimum section threshold is missing or below 50") + +for path in [ + "test-web-wasm-node-smoke.sh", + "test-web-wasm-node-smoke.cjs", + "test-web-wasm-browser-smoke.sh", + "web/test-browser-wasm-smoke-linuxcnc-sections.js", + "web/test-browser-wasm-smoke-opfs-parameter-sections.js", + "web/test-browser-wasm-smoke-app-sections.js", +]: + if f"| `{path}` |" not in source_map: + raise SystemExit(f"docs/linuxcnc-wasm-smoke-source-map.md missing smoke entry: {path}") + +required_phrases = [ + "must not add CNC behavior", + "must not expand the temporary smoke parser", + "persistence OPFS-only", + "Node smoke must leave OPFS disabled", + "Generated switchkins config smoke cases", + "./check-linuxcnc-wasm-smoke-source-map.sh", +] +for phrase in required_phrases: + if phrase not in source_map: + raise SystemExit(f"docs/linuxcnc-wasm-smoke-source-map.md missing boundary phrase: {phrase}") +PY diff --git a/docs/linuxcnc-wasm-smoke-source-map.md b/docs/linuxcnc-wasm-smoke-source-map.md new file mode 100644 index 0000000..b8bd582 --- /dev/null +++ b/docs/linuxcnc-wasm-smoke-source-map.md @@ -0,0 +1,60 @@ +# LinuxCNC WASM smoke source map + +This map records the source basis and boundaries for the Node and browser WASM +smoke wrappers. It is a smoke-test guard only; it must not add CNC behavior, +must not expand the temporary smoke parser, and must keep browser filesystem +persistence OPFS-only. In browser contexts, browser filesystem persistence +OPFS-only is the required boundary. + +## LinuxCNC Source Basis + +- `src/emc/rs274ngc/rs274ngc_pre.cc` provides `Interp::ini_load()`, + `Interp::restore_parameters()`, `Interp::save_parameters()`, and `read_text()` + behavior exercised by Node/browser bridge tests. +- `src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage for + M66 waits and M68 analog output side effects used by generated remap cases. +- `src/emc/nml_intf/canon.hh` declares the `WAIT()` and + `SET_AUX_OUTPUT_VALUE()` canon entry points surfaced through the event bridge. +- `src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`, + `src/emc/kinematics/xyzac-trt-kins.c`, + `src/emc/kinematics/xyzbc-trt-kins.c`, and + `src/emc/kinematics/userkfuncs.c` provide the RTCP/kinematics inverse paths + covered by WASM smoke tests. +- `configs/sim/**.ini` files and `remap_subs/{428,429,430}remap.ngc` sources + generate `web/public/linuxcnc_switchkins_remap_config_cases.json`. + +## Smoke Coverage + +| Smoke entry | Boundary | +| --- | --- | +| `test-web-wasm-node-smoke.sh` | Runs the Node WASM wrapper with generated LinuxCNC switchkins cases and LinuxCNC-backed RTCP checks. | +| `test-web-wasm-node-smoke.cjs` | Exercises the C ABI, `web/src/wasm-core.js`, generated switchkins config cases, and source-backed kinematics events. | +| `test-web-wasm-browser-smoke.sh` | Runs browser checks after verifying copied WASM artifacts and smoke module structure. | +| `web/test-browser-wasm-smoke-linuxcnc-sections.js` | Exercises LinuxCNC browser parsing and generated switchkins cases without INI_FILE_NAME remap-load aliases. | +| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` | Exercises LinuxCNC parameter restore/save/backup semantics through OPFS. | +| `web/test-browser-wasm-smoke-app-sections.js` | Verifies the browser app uses OPFS program and parameter persistence. | + +## Boundaries + +- Node smoke must leave OPFS disabled and must reject `parseFile*` methods that + require an OPFS workspace. +- Browser smoke must keep program files, LinuxCNC parameter files, temporary + `.new` files, and `.bak` backups under OPFS-backed storage. +- Generated switchkins config smoke cases must continue to come from + `web/public/linuxcnc_switchkins_remap_config_cases.json`. +- Smoke wrappers must keep positive minimum coverage thresholds so accidental + section loss is visible. + +## Checks + +Run: + +```bash +./check-linuxcnc-wasm-smoke-source-map.sh +./test-native.sh +./test-linuxcnc-source-link.sh +``` + +`check-linuxcnc-wasm-smoke-source-map.sh` keeps this document, LinuxCNC source +anchors, smoke wrapper boundaries, OPFS-only policy, and generated switchkins +case usage synchronized. diff --git a/test-all-native.sh b/test-all-native.sh index af49a0c..0b3d9e6 100755 --- a/test-all-native.sh +++ b/test-all-native.sh @@ -150,6 +150,9 @@ grep -F './check-linuxcnc-opfs-source-map.sh' test-native.sh >/dev/null grep -F 'LinuxCNC source basis: this guard ties generated switchkins/remap aliases' check-linuxcnc-switchkins-remap-source-map.sh >/dev/null grep -F './check-linuxcnc-switchkins-remap-source-map.sh' test-native.sh >/dev/null grep -F './check-linuxcnc-switchkins-remap-source-map.sh' test-linuxcnc-source-link.sh >/dev/null +grep -F 'LinuxCNC source basis: this guard ties Node/browser WASM smoke coverage' check-linuxcnc-wasm-smoke-source-map.sh >/dev/null +grep -F './check-linuxcnc-wasm-smoke-source-map.sh' test-native.sh >/dev/null +grep -F './check-linuxcnc-wasm-smoke-source-map.sh' test-linuxcnc-source-link.sh >/dev/null grep -F 'LinuxCNC source basis: this guard ties the documented WASM blocker map' check-linuxcnc-wasm-blockers-source-map.sh >/dev/null grep -F 'LinuxCNC source basis: this guard ties the documented wasm-safe shim map' check-linuxcnc-wasm-shims-source-map.sh >/dev/null grep -F 'LinuxCNC source basis: the cached input checker keys check-linuxcnc-inputs.sh' check-linuxcnc-inputs-cached.sh >/dev/null @@ -230,6 +233,9 @@ grep -F 'Python/Boost.Python remap paths remain tracked blockers' docs/linuxcnc- grep -F 'LinuxCNC WASM shims source map' docs/linuxcnc-wasm-shims-source-map.md >/dev/null grep -F '| CMake `linuxcnc_wasm_safe_probe_shims` sources | 11 | Browser-safe LinuxCNC adaptation shims linked into WASM source probes. |' docs/linuxcnc-wasm-shims-source-map.md >/dev/null grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-shims-source-map.md >/dev/null +grep -F 'LinuxCNC WASM smoke source map' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'Node smoke must leave OPFS disabled' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'Generated switchkins config smoke cases must continue to come from' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-native.sh >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' test-linuxcnc-source-link.sh >/dev/null grep -F 'check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt' build-wasm.sh >/dev/null diff --git a/test-linuxcnc-source-link.sh b/test-linuxcnc-source-link.sh index 108c54e..3e8d42c 100755 --- a/test-linuxcnc-source-link.sh +++ b/test-linuxcnc-source-link.sh @@ -37,6 +37,7 @@ preflight_cache_dir="$build_dir/preflight-cache" LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" ./check-linuxcnc-switchkins-remap-source-map.sh +./check-linuxcnc-wasm-smoke-source-map.sh LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt # The LinuxCNC source-linked dump path is not concurrency-safe under parallel execution. exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock" diff --git a/test-native.sh b/test-native.sh index fa97c20..53301cc 100755 --- a/test-native.sh +++ b/test-native.sh @@ -38,6 +38,7 @@ preflight_cache_dir="$build_dir/preflight-cache" ./check-linuxcnc-kinematics-source-map.sh ./check-linuxcnc-opfs-source-map.sh ./check-linuxcnc-switchkins-remap-source-map.sh +./check-linuxcnc-wasm-smoke-source-map.sh ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt grep -F 'tooldata:src/emc/tooldata/tooldata_nml.cc:NML tool table backend selected by LinuxCNC TOOL_NML_FLAG' linuxcnc-rs274-source-files.txt >/dev/null grep -F 'tooldata:src/emc/tooldata/tool_mmap_read.cc:LinuxCNC tooldata mmap reader utility source from tooldata Submakefile' linuxcnc-rs274-source-files.txt >/dev/null