按建议,继续完成后续工作

结论:已新增 Chromium headless 浏览器 smoke,验证 INI WASM SDK 可在真实浏览器运行并完成 OPFS 文本文件往返,继续保持 OPFS 在 host-side、LinuxCNC INI 解析来自 vendored inifile.cc。
This commit is contained in:
2026-06-08 03:19:44 +08:00
parent 0d78849d80
commit cb1901d4c4
5 changed files with 158 additions and 9 deletions

View File

@@ -24,6 +24,12 @@ The current OPFS host-boundary validation command is:
wasm-port/tests/opfs/node/verify_file_service.sh wasm-port/tests/opfs/node/verify_file_service.sh
``` ```
The current browser smoke validation command is:
```bash
wasm-port/tests/browser/verify_ini_panel_browser.sh
```
## Validation Chain ## Validation Chain
The native validation script runs these checks in order: The native validation script runs these checks in order:
@@ -57,6 +63,11 @@ with a Node mock of the browser File System Access handles. It covers nested
directory creation, text save/load, missing file behavior, invalid relative directory creation, text save/load, missing file behavior, invalid relative
paths, and unavailable OPFS storage. paths, and unavailable OPFS storage.
The browser 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 round trip.
## Source Coverage ## Source Coverage
Every `.c` and `.cc` entry in `tools/source-manifest.txt` must have a Every `.c` and `.cc` entry in `tools/source-manifest.txt` must have a
@@ -105,6 +116,7 @@ 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_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/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter used by the browser INI panel without moving file persistence into the WASM core. | | `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter used by the browser INI panel without moving file persistence into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, WASM module loading, and OPFS text-file round trip in a real browser runtime. |
## Fixture Coverage ## Fixture Coverage
@@ -149,8 +161,9 @@ Negative fixtures currently cover:
Current full-core validation is native-only. WASM/SDK validation is limited to Current full-core validation is native-only. WASM/SDK validation is limited to
the INI parser smoke harness. OPFS validation is limited to the JavaScript the INI parser smoke harness. OPFS validation is limited to the JavaScript
host-boundary adapter. Browser-level validation, full SDK coverage, and full host-boundary adapter plus the INI browser smoke harness. Full browser
machine-session validation remain future work. coverage, full SDK coverage, and full machine-session validation remain future
work.
The current fixture expectations validate standalone behavior against both the The current fixture expectations validate standalone behavior against both the
vendored LinuxCNC source path and an upstream `rs274` side-by-side baseline for vendored LinuxCNC source path and an upstream `rs274` side-by-side baseline for

View File

@@ -56,12 +56,12 @@ semantic rewrites:
## Known Gaps ## Known Gaps
- No browser/full-core WASM parity tests yet. The INI parser now has a Node - No browser/full-core WASM parity tests yet. The INI parser now has Node and
WASM smoke harness against vendored LinuxCNC `inifile.cc`. Chromium smoke harnesses against vendored LinuxCNC `inifile.cc`.
- JS SDK validation is currently limited to the INI WASM wrapper around - JS SDK validation is currently limited to the INI WASM wrapper around
vendored LinuxCNC `inifile.cc`. vendored LinuxCNC `inifile.cc`.
- OPFS validation is currently limited to a Node mock of the browser - OPFS validation covers a Node mock of the file-service adapter and a
file-service adapter; browser-level OPFS validation is not yet established. Chromium localhost round trip for INI text-file persistence.
- Identity/trivial, `5axiskins`, TRT `xyzac`/`xyzbc`, delta, SCARA, PUMA, - Identity/trivial, `5axiskins`, TRT `xyzac`/`xyzbc`, delta, SCARA, PUMA,
serial, hexapod, pentapod, and related kinematics sources now have native serial, hexapod, pentapod, and related kinematics sources now have native
source-probe coverage. source-probe coverage.

View File

@@ -31,7 +31,7 @@ Current validation is intentionally mechanical:
| Capability | LinuxCNC source files | Port classification | Standalone boundary | Current validation | | Capability | LinuxCNC source files | Port classification | Standalone boundary | Current validation |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| INI parsing | `src/emc/ini/inifile.cc`, `inifile.h`, `inifile.hh` | Copy unchanged | Native file IO remains LinuxCNC-style in the vendored parser; browser OPFS integration remains outside this layer; `runtime/sdk/src/linuxcnc-ini.js` only wraps the exported WASM C ABI | Vendor byte sync, `linuxcnc_ini_probe`, `linuxcnc_inifile_source_probe`, `tests/wasm/node/verify_ini_wasm.sh` | | INI parsing | `src/emc/ini/inifile.cc`, `inifile.h`, `inifile.hh` | Copy unchanged | Native file IO remains LinuxCNC-style in the vendored parser; browser OPFS integration remains outside this layer; `runtime/sdk/src/linuxcnc-ini.js` only wraps the exported WASM C ABI | Vendor byte sync, `linuxcnc_ini_probe`, `linuxcnc_inifile_source_probe`, `tests/wasm/node/verify_ini_wasm.sh`, `tests/browser/verify_ini_panel_browser.sh` |
| RTAPI compatibility headers | `src/rtapi/rtapi_*.h` in the manifest | Copy unchanged plus standalone shim include path | `runtime/core/shims/rtapi.h` supplies the minimal standalone RTAPI surface needed by vendored code | Vendor byte sync, compile coverage through dependent source probes | | RTAPI compatibility headers | `src/rtapi/rtapi_*.h` in the manifest | Copy unchanged plus standalone shim include path | `runtime/core/shims/rtapi.h` supplies the minimal standalone RTAPI surface needed by vendored code | Vendor byte sync, compile coverage through dependent source probes |
| Canon/NML-facing interpreter types | `src/emc/nml_intf/canon*.hh`, `emctool.h`, `interp_return.hh`, `motion_types.h`, `emcpose.*`, `emcpos.h`, `debugflags.h`, `src/emc/linuxcnc.h` | Copy unchanged plus narrow standalone status shim | NML transport is not ported; `runtime/core/shims/nml_intf/emc.hh` exposes only the `emcStatus` machine-units status edge currently needed by vendored interpreter conversion and initialization code | Vendor byte sync, dependent source probes, `linuxcnc_emc_status_probe`, `linuxcnc_tp_api_probe`, interpreter harnesses | | Canon/NML-facing interpreter types | `src/emc/nml_intf/canon*.hh`, `emctool.h`, `interp_return.hh`, `motion_types.h`, `emcpose.*`, `emcpos.h`, `debugflags.h`, `src/emc/linuxcnc.h` | Copy unchanged plus narrow standalone status shim | NML transport is not ported; `runtime/core/shims/nml_intf/emc.hh` exposes only the `emcStatus` machine-units status edge currently needed by vendored interpreter conversion and initialization code | Vendor byte sync, dependent source probes, `linuxcnc_emc_status_probe`, `linuxcnc_tp_api_probe`, interpreter harnesses |
| Motion state headers | `src/emc/motion/state_tag.h`, `emcmotcfg.h`, `simple_tp.h`, `motion.h`, `mot_priv.h`, `axis.h` | Copy unchanged | Realtime motion process is not ported; standalone probes seed the small motion status/config state required by TP calls | Vendor byte sync, `linuxcnc_tp_api_probe` | | Motion state headers | `src/emc/motion/state_tag.h`, `emcmotcfg.h`, `simple_tp.h`, `motion.h`, `mot_priv.h`, `axis.h` | Copy unchanged | Realtime motion process is not ported; standalone probes seed the small motion status/config state required by TP calls | Vendor byte sync, `linuxcnc_tp_api_probe` |
@@ -70,8 +70,9 @@ Current validation is intentionally mechanical:
fixture-covered through vendored `interp_convert.cc` and `interp_queue.cc`. fixture-covered through vendored `interp_convert.cc` and `interp_queue.cc`.
- Browser/WASM C ABI and JS SDK layers are not yet built for the full - Browser/WASM C ABI and JS SDK layers are not yet built for the full
interpreter/planner core. The INI parser has a minimal JS SDK wrapper and interpreter/planner core. The INI parser has a minimal JS SDK wrapper and
Node WASM smoke harness. Node/browser WASM smoke harnesses.
- OPFS persistence is connected to the INI panel through the host-side - OPFS persistence is connected to the INI panel through the host-side
`runtime/opfs/file-service.js` adapter; tool table, parameter file, G-code `runtime/opfs/file-service.js` adapter; tool table, parameter file, G-code
program loading, and browser-level OPFS validation remain future work. program loading, and broader browser-level OPFS validation remain future
work.
- Native LinuxCNC GUI code remains out of scope for implementation. - Native LinuxCNC GUI code remains out of scope for implementation.

View File

@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LinuxCNC INI Browser Smoke</title>
</head>
<body>
<pre id="status">running</pre>
<script type="module">
import { createLinuxCncIniSdk } from "../../runtime/sdk/src/linuxcnc-ini.js";
import { loadTextFile, saveTextFile } from "../../runtime/opfs/file-service.js";
const status = document.getElementById("status");
function assertEqual(actual, expected, label) {
if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`);
}
}
try {
const ini = await createLinuxCncIniSdk();
const wasmPath = "/work/browser-smoke.ini";
const iniText = `[EMC]
MACHINE = browser-smoke
[TRAJ]
LINEAR_UNITS = mm
COORDINATES = X Y Z
[KINS]
KINEMATICS = trivkins
JOINTS = 3
`;
ini.writeTextFile(wasmPath, iniText);
assertEqual(ini.getString(wasmPath, "EMC", "MACHINE"), "browser-smoke", "machine");
assertEqual(ini.getString(wasmPath, "TRAJ", "LINEAR_UNITS"), "mm", "units");
assertEqual(ini.getString(wasmPath, "KINS", "KINEMATICS"), "trivkins", "kinematics");
assertEqual(ini.getString(wasmPath, "TRAJ", "MISSING"), null, "missing tag");
const fields = ini.getFields(wasmPath, {
machine: { section: "EMC", tag: "MACHINE" },
joints: { section: "KINS", tag: "JOINTS" },
});
assertEqual(fields.machine, "browser-smoke", "field machine");
assertEqual(fields.joints, "3", "field joints");
const opfsPath = "linuxcnc/browser-smoke.ini";
await saveTextFile(opfsPath, iniText);
assertEqual(await loadTextFile(opfsPath), iniText, "opfs roundtrip");
status.textContent = "browser_ini_opfs_smoke=ok";
} catch (error) {
status.textContent = `browser_ini_opfs_smoke=fail ${error.stack || error.message}`;
}
</script>
</body>
</html>

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
if [[ -z "$CHROMIUM" ]]; then
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
exit 1
fi
"$ROOT_DIR/tools/build_ini_panel.sh"
TMP_DIR="$(mktemp -d)"
PORT_FILE="$TMP_DIR/port"
SERVER_LOG="$TMP_DIR/server.log"
CHROME_PROFILE="$TMP_DIR/chrome-profile"
mkdir -p "$CHROME_PROFILE"
cleanup() {
if [[ -n "${SERVER_PID:-}" ]]; then
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
fi
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
import functools
import http.server
import pathlib
import socketserver
import sys
root = pathlib.Path(sys.argv[1])
port_file = pathlib.Path(sys.argv[2])
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
httpd.serve_forever()
PY
SERVER_PID=$!
for _ in $(seq 1 100); do
[[ -s "$PORT_FILE" ]] && break
sleep 0.05
done
if [[ ! -s "$PORT_FILE" ]]; then
echo "browser smoke HTTP server did not start" >&2
cat "$SERVER_LOG" >&2 || true
exit 1
fi
PORT="$(cat "$PORT_FILE")"
URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_smoke.html"
OUT="$TMP_DIR/chromium.out"
"$CHROMIUM" \
--headless=new \
--disable-gpu \
--no-sandbox \
--user-data-dir="$CHROME_PROFILE" \
--virtual-time-budget=10000 \
--dump-dom \
"$URL" >"$OUT" 2>&1
if ! grep -Fq "browser_ini_opfs_smoke=ok" "$OUT"; then
echo "browser INI/OPFS smoke failed" >&2
sed -n '1,220p' "$OUT" >&2
exit 1
fi
echo "browser_ini_opfs_smoke=ok"