验证ToolDb浏览器证据
结论:已新增Tool DB browser smoke,验证protocol transcript、OPFS flat-file/transcript persistence和diagnostics guard,baseline保持28/28/131/0且promotion继续锁定。
This commit is contained in:
115
wasm-port/tests/browser/tool_db_process_browser_smoke.html
Normal file
115
wasm-port/tests/browser/tool_db_process_browser_smoke.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC Tool DB Process Browser Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
import {
|
||||
createLinuxCncToolDbProcessPort,
|
||||
createToolDbProcessDiagnostics,
|
||||
createToolDbStorePaths,
|
||||
loadToolDbFile,
|
||||
loadToolDbTranscript,
|
||||
saveToolDbFile,
|
||||
saveToolDbTranscript,
|
||||
validateToolDbTranscript,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
const status = document.getElementById("status");
|
||||
|
||||
function assertEqual(actual, expected, label) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${label}: expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertMatch(value, pattern, label) {
|
||||
if (!pattern.test(value)) {
|
||||
throw new Error(`${label}: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const machineId = "browser-tool-db-gate";
|
||||
const runId = "browser-tool-db-run";
|
||||
const paths = createToolDbStorePaths(machineId, { runId });
|
||||
assertEqual(paths.dbFile, "linuxcnc/machines/browser-tool-db-gate/tool-db/db_nonran_file", "DB file path");
|
||||
assertEqual(
|
||||
paths.transcript,
|
||||
"linuxcnc/machines/browser-tool-db-gate/tool-db/transcripts/browser-tool-db-run/transcript.json",
|
||||
"transcript path",
|
||||
);
|
||||
|
||||
const port = createLinuxCncToolDbProcessPort({
|
||||
dbProgramPath: "./db_nonran.py",
|
||||
sourceFiles: [
|
||||
"configs/sim/axis/db_demo/db_nonran.py",
|
||||
"configs/sim/axis/db_demo/db.py",
|
||||
"lib/python/tooldb.py",
|
||||
],
|
||||
runtimeMode: "contract-only",
|
||||
});
|
||||
const startResult = await port.start();
|
||||
assertEqual(startResult.runtimeExecutionReady, false, "contract-only runtime execution");
|
||||
const planResult = await port.runTransactionPlan();
|
||||
assertEqual(planResult.status, "blocked_runtime_adapter_required", "contract-only plan status");
|
||||
assertEqual(planResult.tblFallbackSufficient, false, "tbl fallback guard");
|
||||
|
||||
const transcript = [
|
||||
{ direction: "read", line: "v2.1" },
|
||||
{ direction: "write", line: "g" },
|
||||
{ direction: "read", line: "T10 P110 D0.10 Z0.10" },
|
||||
{ direction: "read", line: "FINI (get_cmd)" },
|
||||
{ direction: "write", line: "p t11 p111 d0.33 z0.11" },
|
||||
{ direction: "write", line: "l t14 p0" },
|
||||
{ direction: "write", line: "u t0 p0" },
|
||||
];
|
||||
assertEqual(validateToolDbTranscript(transcript).ready, true, "transcript readiness");
|
||||
|
||||
const dbFileText = "T11 P111 D0.33 Z0.11\nT14 P114 D0.14 Z0.14\n";
|
||||
const savedDb = await saveToolDbFile(machineId, dbFileText);
|
||||
const savedTranscript = await saveToolDbTranscript(machineId, runId, transcript);
|
||||
assertEqual(savedDb.displayPath, "/machines/browser-tool-db-gate/tool-db/db_nonran_file", "display DB path");
|
||||
assertEqual(
|
||||
savedTranscript.displayPath,
|
||||
"/machines/browser-tool-db-gate/tool-db/transcripts/browser-tool-db-run/transcript.json",
|
||||
"display transcript path",
|
||||
);
|
||||
assertMatch(savedDb.dbFileHash, /^[0-9a-f]{8}$/, "DB hash");
|
||||
assertMatch(savedTranscript.transcriptHash, /^[0-9a-f]{8}$/, "transcript hash");
|
||||
|
||||
const loadedDb = await loadToolDbFile(machineId);
|
||||
const loadedTranscript = await loadToolDbTranscript(machineId, runId);
|
||||
assertEqual(loadedDb.text, dbFileText, "loaded DB file");
|
||||
assertEqual(loadedTranscript.payload.schema, "linuxcnc.toolDbProcessTranscript.v1", "transcript schema");
|
||||
assertEqual(loadedTranscript.payload.transcript.length, transcript.length, "loaded transcript length");
|
||||
|
||||
const diagnostics = createToolDbProcessDiagnostics({
|
||||
dbProgramPath: "./db_nonran.py",
|
||||
opfsDbPath: savedDb.displayPath,
|
||||
transcript,
|
||||
dbFileText,
|
||||
startupToolCount: 10,
|
||||
mutationCount: 3,
|
||||
runtimeMode: "wasm-tool-db-protocol-worker",
|
||||
});
|
||||
assertEqual(diagnostics.runtimeMode, "wasm-tool-db-protocol-worker", "diagnostics runtime mode");
|
||||
assertEqual(diagnostics.dbProgramPath, "./db_nonran.py", "diagnostics DB_PROGRAM");
|
||||
assertEqual(diagnostics.protocolTranscriptReady, true, "diagnostics transcript");
|
||||
assertEqual(diagnostics.opfsPersistenceReady, true, "diagnostics OPFS");
|
||||
assertEqual(diagnostics.tblFallbackSufficient, false, "diagnostics tbl fallback guard");
|
||||
assertEqual(diagnostics.runtimeExecutionReady, false, "diagnostics runtime execution");
|
||||
assertEqual(diagnostics.executionEnabled, false, "diagnostics execution");
|
||||
assertEqual(diagnostics.promotionAllowed, false, "diagnostics promotion");
|
||||
|
||||
status.textContent = "browser_tool_db_process_smoke=ok";
|
||||
} catch (error) {
|
||||
status.textContent = `browser_tool_db_process_smoke=fail\n${error.stack || error}`;
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
74
wasm-port/tests/browser/verify_tool_db_process_browser.sh
Normal file
74
wasm-port/tests/browser/verify_tool_db_process_browser.sh
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/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
|
||||
|
||||
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 "Tool DB process 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/tool_db_process_browser_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_tool_db_process_smoke=ok" "$OUT"; then
|
||||
echo "Tool DB process browser smoke failed" >&2
|
||||
sed -n '1,220p' "$OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_tool_db_process_smoke=ok"
|
||||
Reference in New Issue
Block a user