按text25.txt规划,实现L4-PYTHON-REMAP接入数控系统仿真系统
结论:已接入Python remap runtime proof chain,覆盖native、WASM、browser与release gate证据链;继续保持promotion_allowed=0,不批量解锁L4-PYTHON-REMAP。
This commit is contained in:
@@ -986,7 +986,7 @@ TOOL_TABLE = browser-shell-tool.tbl
|
||||
);
|
||||
assertEqual(
|
||||
workflowOverviewReleaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||
"evidence-ready=8 inventory-ready=20",
|
||||
"evidence-ready=8 inventory-ready=19",
|
||||
"shell workflow overview release artifact URL workflow candidate layers",
|
||||
);
|
||||
assertEqual(
|
||||
|
||||
@@ -421,7 +421,7 @@
|
||||
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
blockedCandidateIds: [],
|
||||
explicitBrowserDiagnosticsCount: 8,
|
||||
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
inventoryBaseline: "executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||
},
|
||||
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
|
||||
manifestEntries: [
|
||||
@@ -637,7 +637,7 @@
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "promotion-candidate-layers")?.value,
|
||||
"evidence-ready=8 inventory-ready=20",
|
||||
"evidence-ready=8 inventory-ready=19",
|
||||
"workflow overview release artifact URL workflow candidate layers",
|
||||
);
|
||||
assertEqual(
|
||||
@@ -772,7 +772,7 @@
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
|
||||
"executed=28 passed=28 skipped=131 unexpected_fail=0",
|
||||
"executed=29 passed=29 skipped=130 unexpected_fail=0",
|
||||
"workflow overview release artifact URL workflow candidate baseline",
|
||||
);
|
||||
assertEqual(
|
||||
@@ -948,7 +948,7 @@
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-workflow-overview-release-readiness-artifact-url-workflow-value="promotion-candidate-layers"]').textContent,
|
||||
"evidence-ready=8 inventory-ready=20",
|
||||
"evidence-ready=8 inventory-ready=19",
|
||||
"workflow overview fixed URL workflow form candidate layer row",
|
||||
);
|
||||
assertEqual(
|
||||
|
||||
45
wasm-port/tests/browser/python_remap_fake_runtime_worker.js
Normal file
45
wasm-port/tests/browser/python_remap_fake_runtime_worker.js
Normal file
@@ -0,0 +1,45 @@
|
||||
self.linuxCncPythonRemapRuntimeProvider = {
|
||||
async start() {},
|
||||
|
||||
async initializePython() {
|
||||
return { value: "python-runtime-initialized" };
|
||||
},
|
||||
|
||||
async applyIniPythonPath(message) {
|
||||
return { value: message.pythonPathPrepend };
|
||||
},
|
||||
|
||||
async executeTopLevel(message) {
|
||||
return { value: message.topLevelPath };
|
||||
},
|
||||
|
||||
async importModule(message) {
|
||||
return { value: message.modulePath };
|
||||
},
|
||||
|
||||
async lookupCallable(message) {
|
||||
return { value: message.callableName };
|
||||
},
|
||||
|
||||
async invokeGenerator() {
|
||||
return { value: "generator-returned" };
|
||||
},
|
||||
|
||||
async observeFirstYield(message) {
|
||||
return { value: message.expectedYield };
|
||||
},
|
||||
|
||||
async finishGenerator() {
|
||||
return { value: "generator-finished" };
|
||||
},
|
||||
|
||||
async exportInterpreterState() {
|
||||
return { value: "interpreter-state-bound" };
|
||||
},
|
||||
|
||||
async exportDiagnostics() {
|
||||
return { value: "diagnostics-exported" };
|
||||
},
|
||||
|
||||
async close() {},
|
||||
};
|
||||
114
wasm-port/tests/browser/python_remap_runtime_browser_smoke.html
Normal file
114
wasm-port/tests/browser/python_remap_runtime_browser_smoke.html
Normal file
@@ -0,0 +1,114 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC Python Remap Runtime Browser Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
import {
|
||||
createLinuxCncPythonRemapBrowserWorkerAdapter,
|
||||
createLinuxCncPythonRemapRuntimePort,
|
||||
createPythonRemapRuntimeDiagnostics,
|
||||
validatePythonRemapLifecycleTranscript,
|
||||
} 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}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const contractOnlyPort = createLinuxCncPythonRemapRuntimePort({
|
||||
runtimeMode: "contract-only",
|
||||
});
|
||||
const contractOnlyStart = await contractOnlyPort.start();
|
||||
assertEqual(contractOnlyStart.runtimeExecutionReady, false, "contract-only runtime execution");
|
||||
const contractOnlyPlan = await contractOnlyPort.runLifecyclePlan();
|
||||
assertEqual(contractOnlyPlan.status, "blocked_runtime_adapter_required", "contract-only lifecycle plan");
|
||||
assertEqual(contractOnlyPlan.promotionAllowed, false, "contract-only promotion guard");
|
||||
await contractOnlyPort.close();
|
||||
|
||||
const workerAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||
});
|
||||
const workerPort = createLinuxCncPythonRemapRuntimePort({
|
||||
runtimeMode: workerAdapter.runtimeMode,
|
||||
runtimeAdapter: workerAdapter,
|
||||
});
|
||||
const workerStart = await workerPort.start();
|
||||
assertEqual(workerStart.runtimeMode, "browser-python-wasm-worker", "worker runtime mode");
|
||||
assertEqual(workerStart.runtimeExecutionReady, false, "worker Python runtime readiness");
|
||||
assertEqual(workerStart.status, "blocked_browser_python_wasm_runtime_missing", "worker runtime status");
|
||||
const workerPlan = await workerPort.runLifecyclePlan();
|
||||
assertEqual(workerPlan.status, "blocked_browser_python_wasm_runtime_missing", "worker plan blocked");
|
||||
assertEqual(workerPlan.promotionAllowed, false, "worker promotion guard");
|
||||
await workerPort.close();
|
||||
|
||||
const missingModuleAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||
pythonRuntimeModuleUrl: new URL("./missing_python_remap_runtime_provider.js", import.meta.url),
|
||||
});
|
||||
const missingModulePort = createLinuxCncPythonRemapRuntimePort({
|
||||
runtimeMode: missingModuleAdapter.runtimeMode,
|
||||
runtimeAdapter: missingModuleAdapter,
|
||||
});
|
||||
const missingModuleStart = await missingModulePort.start();
|
||||
assertEqual(missingModuleStart.runtimeExecutionReady, false, "missing module runtime readiness");
|
||||
assertEqual(
|
||||
missingModuleStart.status,
|
||||
"blocked_browser_python_wasm_runtime_load_failed",
|
||||
"missing module runtime status",
|
||||
);
|
||||
await missingModulePort.close();
|
||||
|
||||
const fakeRuntimeAdapter = createLinuxCncPythonRemapBrowserWorkerAdapter({
|
||||
workerUrl: new URL("../../runtime/workers/python-remap-worker.js", import.meta.url),
|
||||
pythonRuntimeModuleUrl: new URL("./python_remap_fake_runtime_worker.js", import.meta.url),
|
||||
});
|
||||
const fakeRuntimePort = createLinuxCncPythonRemapRuntimePort({
|
||||
runtimeMode: fakeRuntimeAdapter.runtimeMode,
|
||||
runtimeAdapter: fakeRuntimeAdapter,
|
||||
});
|
||||
const fakeRuntimeStart = await fakeRuntimePort.start();
|
||||
assertEqual(fakeRuntimeStart.runtimeExecutionReady, true, "fake provider runtime readiness");
|
||||
assertEqual(fakeRuntimeStart.status, "browser_python_remap_wasm_runtime_ready", "fake provider runtime status");
|
||||
const fakeRuntimePlan = await fakeRuntimePort.runLifecyclePlan();
|
||||
assertEqual(fakeRuntimePlan.status, "runtime_adapter_lifecycle_plan_executed", "fake provider lifecycle plan");
|
||||
assertEqual(fakeRuntimePlan.promotionAllowed, false, "fake provider promotion guard");
|
||||
assertEqual(fakeRuntimePlan.bulkPromotionAllowed, false, "fake provider bulk promotion guard");
|
||||
const transcript = fakeRuntimePort.exportTranscript();
|
||||
assertEqual(validatePythonRemapLifecycleTranscript(transcript).ready, true, "lifecycle transcript readiness");
|
||||
const diagnostics = createPythonRemapRuntimeDiagnostics({
|
||||
runtimeMode: fakeRuntimeAdapter.runtimeMode,
|
||||
runtimeExecutionReady: fakeRuntimeStart.runtimeExecutionReady,
|
||||
transcript,
|
||||
});
|
||||
assertEqual(diagnostics.fixtureFamily, "axis/remap/stop-lookahead/nc_files", "diagnostics fixture family");
|
||||
assertEqual(diagnostics.iniPath, "axis/remap/stop-lookahead/demo.ini", "diagnostics INI path");
|
||||
assertEqual(diagnostics.pythonPathPrepend, "python", "diagnostics Python path");
|
||||
assertEqual(diagnostics.topLevelPath, "python/toplevel.py", "diagnostics TOPLEVEL");
|
||||
assertEqual(diagnostics.lifecycleTranscriptReady, true, "diagnostics transcript");
|
||||
assertEqual(diagnostics.callableLookupReady, true, "diagnostics callable lookup");
|
||||
assertEqual(diagnostics.generatorLifecycleReady, true, "diagnostics generator lifecycle");
|
||||
assertEqual(diagnostics.interpreterStateBindingReady, true, "diagnostics interpreter binding");
|
||||
assertEqual(diagnostics.linuxCncOwnedLifecycle, true, "diagnostics LinuxCNC lifecycle owner");
|
||||
assertEqual(diagnostics.jsCncSemantics, false, "diagnostics JS CNC semantic guard");
|
||||
assertEqual(diagnostics.ngcOnlySubroutinePromoted, false, "diagnostics NGC-only guard");
|
||||
assertEqual(diagnostics.executionEnabled, false, "diagnostics execution guard");
|
||||
assertEqual(diagnostics.promotionAllowed, false, "diagnostics promotion guard");
|
||||
assertEqual(diagnostics.bulkPromotionAllowed, false, "diagnostics bulk promotion guard");
|
||||
await fakeRuntimePort.close();
|
||||
|
||||
status.textContent = "browser_python_remap_runtime_smoke=ok";
|
||||
} catch (error) {
|
||||
status.textContent = `browser_python_remap_runtime_smoke=fail\n${error.stack || error}`;
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -521,13 +521,13 @@
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.candidateCount !== 8 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.readyCandidateCount !== 8 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.sourceFileCount !== 17 ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=28 passed=28 skipped=131 unexpected_fail=0" ||
|
||||
diagnosticsArtifact.virtualHalPromotionCandidateSummary?.inventoryBaseline !== "executed=29 passed=29 skipped=130 unexpected_fail=0" ||
|
||||
promotionCandidateSummary.ready !== true ||
|
||||
promotionCandidateSummary.preferredIniPath !== "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini" ||
|
||||
doc.body.dataset.promotionCandidateReady !== "true" ||
|
||||
doc.body.dataset.promotionCandidatePreferred !== "qtdragon-multi-joint-on-abort" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="preferred-candidate"]')?.textContent !== "qtdragon-multi-joint-on-abort" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=20" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-layers"]')?.textContent !== "evidence-ready=8 inventory-ready=19" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="inventory-baseline"]')?.textContent !== "28/28 pass; 131 skip unchanged" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="promotion-allowed"]')?.textContent !== "0 baseline changes" ||
|
||||
doc.querySelector('[data-promotion-candidate-summary-value="candidate-artifact"]')?.textContent !== "promotion-candidates.tsv" ||
|
||||
|
||||
74
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
Executable file
74
wasm-port/tests/browser/verify_python_remap_runtime_browser.sh
Executable 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 "Python remap runtime 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/python_remap_runtime_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_python_remap_runtime_smoke=ok" "$OUT"; then
|
||||
echo "Python remap runtime browser smoke failed" >&2
|
||||
sed -n '1,220p' "$OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_python_remap_runtime_smoke=ok"
|
||||
Reference in New Issue
Block a user