Files
wasm-simulator/check-linuxcnc-opfs-source-map.sh
cnc 586c28656d 验证 OPFS rename 持久清理
提示词:快速推进,避免纯 source-map-only,不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc save_parameters() 的 filename + .new rename 语义,补强浏览器 OPFS moveFile 在新 WASM 实例中持久删除源文件并恢复目标 mirror 的覆盖;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 06:31:30 +08:00

183 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties browser OPFS parameter persistence to
# interp_internal.hh defaults and rs274ngc_pre.cc restore_parameters() /
# save_parameters() file semantics. It validates bridge policy only; it does not
# add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F '#define RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX ".bak"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/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::open(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '_setup.file_pointer = fopen(filename, "r");' \
"$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 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 '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'unlink(bakfile.c_str());' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX` as `.bak`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::restore_parameters()` for parameter-file reads' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::open()` by opening' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`fopen(filename, "r")`, then `Interp::_read()`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`read_text()`' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::save_parameters()` for `filename + ".new"`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'unlinks `filename + ".bak"`, links the' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'must not add independent filesystem behavior or expand the temporary smoke parser' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'parseFileWithParameterFile()' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'OPFS Coverage' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/wasm-core.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/wasm-core.d.ts` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/app.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-basic-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-workspace-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-mirror-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-directory-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-policy-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-app-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'const LINUXCNC_DEFAULT_PARAMETER_FILE = "rs274ngc.var";' web/src/wasm-core.js >/dev/null
grep -F 'async parseFile(path, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
grep -F 'OPFS workspace is not available for program file parsing' web/src/wasm-core.js >/dev/null
grep -F 'const program = new TextDecoder().decode(await opfs.readFile(path));' web/src/wasm-core.js >/dev/null
grep -F 'async parseFileWithParameterFile(path, parameterPath, dialect = "linuxcnc", options = {})' \
web/src/wasm-core.js >/dev/null
grep -F 'OPFS workspace is not available for program and parameter file parsing' web/src/wasm-core.js >/dev/null
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`' web/src/wasm-core.js >/dev/null
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`' web/src/wasm-core.js >/dev/null
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null
grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null
grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null
grep -F 'OPFS path must stay inside the CNC workspace' web/src/wasm-core.js >/dev/null
grep -F 'OPFS mount point must stay inside the Emscripten filesystem' web/src/wasm-core.js >/dev/null
grep -F 'async persistDirectory(path)' web/src/wasm-core.js >/dev/null
grep -F 'async readDirectory(path)' web/src/wasm-core.js >/dev/null
grep -F 'export type WasmOpfsWorkspace = {' web/src/wasm-core.d.ts >/dev/null
grep -F 'readDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'persistDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'loadParameterFile(path: string): Promise<Uint8Array | null>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'persistParameterFile(path: string): Promise<Uint8Array>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'opfs?: false | {' web/src/wasm-core.d.ts >/dev/null
grep -F 'mountPoint?: string;' web/src/wasm-core.d.ts >/dev/null
grep -F 'workspacePath?: string;' web/src/wasm-core.d.ts >/dev/null
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \
web/src/app.js >/dev/null
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
grep -F 'runBrowserOpfsBasicSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsParameterSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsMirrorSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsDirectorySections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsPolicySections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runSection("opfs directory persist and load"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("opfs metadata queries avoid wasm mirror pollution"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("opfs copy and move mirror files"' \
web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
grep -F 'runSection("browser opfs policy"' \
web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
grep -F 'runSection("opfs parameter restore in new instance"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs invalid parameter restore failures"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs stale parameter state cleanup"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs program parse and restore"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/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
if grep -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' \
web/test-browser-wasm-smoke-*.js \
web/test-browser-wasm-smoke.html \
test-web-wasm-browser-smoke.sh; then
echo "browser OPFS smoke harness must not add filesystem persistence outside OPFS" >&2
exit 1
fi
python3 - <<'PY'
from pathlib import Path
source_map = Path("docs/linuxcnc-opfs-source-map.md").read_text(encoding="utf-8")
coverage_entries = [
"web/src/wasm-core.js",
"web/src/wasm-core.d.ts",
"web/src/app.js",
"web/test-browser-wasm-smoke-opfs-basic-sections.js",
"web/test-browser-wasm-smoke-opfs-parameter-sections.js",
"web/test-browser-wasm-smoke-opfs-workspace-sections.js",
"web/test-browser-wasm-smoke-opfs-mirror-sections.js",
"web/test-browser-wasm-smoke-opfs-directory-sections.js",
"web/test-browser-wasm-smoke-opfs-policy-sections.js",
"web/test-browser-wasm-smoke-app-sections.js",
]
for entry in coverage_entries:
if f"| `{entry}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage entry: {entry}")
required_phrases = [
"programs/current.ngc",
"parameters/rs274ngc.var",
'fopen(filename, "r")',
"LinuxCNC `read_text()`",
"program reads and parameter restore/save bridge files under the OPFS workspace",
"`.new`, `.bak`, failure, and stale-state semantics",
'unlinks `filename + ".bak"`',
"OPFS/WASM mirror persistence",
"OPFS directory persistence and WASM mirror refresh",
"OPFS workspace and mount policy",
"Browser OPFS smoke harness files must also stay free of Local Storage",
"they may only verify OPFS-backed browser persistence",
"OPFS-backed workspace, directory, parameter-file, and mount/workspace option types",
"app-level OPFS program, parameter, backup, and reload behavior",
"./check-linuxcnc-opfs-source-map.sh",
"does not interpret G-code",
]
for phrase in required_phrases:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-opfs-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-opfs-source-map.sh")
PY