66 lines
3.7 KiB
Python
66 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Aggregate real Worker runtime extension regression evidence for S8-09."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
REPORT = Path("coverage/runtime-extensions-s8-09.report.json")
|
|
RUNNERS = [
|
|
"web/worker/test_source_interpreter_s5.mjs",
|
|
"web/worker/test_source_do_s7_01.mjs",
|
|
"web/worker/test_source_g6164_s7_02.mjs",
|
|
"web/worker/test_python_api_s3.mjs",
|
|
"web/worker/test_python_callbacks_s3.mjs",
|
|
"web/worker/test_python_bridge.mjs",
|
|
"web/worker/test_device_io.mjs",
|
|
"web/worker/test_io_ack.mjs",
|
|
"web/worker/test_virtual_devices.mjs",
|
|
"web/worker/test_plc_runtime.mjs",
|
|
]
|
|
|
|
|
|
def canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
def digest(value: bytes) -> str:
|
|
return hashlib.sha256(value).hexdigest()
|
|
|
|
|
|
def generate(output: Path) -> dict[str, Any]:
|
|
records = []
|
|
for runner in RUNNERS:
|
|
result = subprocess.run(["node", runner], cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
|
|
records.append({"command": ["node", runner], "returncode": result.returncode, "status": "passed" if result.returncode == 0 else "failed", "stdout_sha256": digest(result.stdout), "stderr_sha256": digest(result.stderr), "stdout_tail": result.stdout.decode("utf-8", errors="replace")[-1200:]})
|
|
passed = all(record["status"] == "passed" for record in records)
|
|
report: dict[str, Any] = {
|
|
"schema": "runtime-extensions-s8-09-report.v1",
|
|
"task_id": "S8-09",
|
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
|
"status": "verified" if passed else "blocked",
|
|
"implementation_status": "verified" if passed else "blocked",
|
|
"counts": {"runners": len(records), "passed": sum(record["status"] == "passed" for record in records), "failed": sum(record["status"] != "passed" for record in records)},
|
|
"domains": {"remap_oword_filter": ["test_source_interpreter_s5.mjs", "test_source_do_s7_01.mjs", "test_source_g6164_s7_02.mjs"], "python_callbacks": ["test_python_api_s3.mjs", "test_python_callbacks_s3.mjs", "test_python_bridge.mjs"], "io_tool_probe": ["test_device_io.mjs", "test_io_ack.mjs", "test_virtual_devices.mjs"], "plc": ["test_plc_runtime.mjs"]},
|
|
"execution": records,
|
|
"checks": {"fixed_tick_extension_paths": {"status": "verified" if passed else "blocked"}, "capability_boundaries": {"status": "verified" if passed else "blocked"}, "io_ack_and_recovery": {"status": "verified" if passed else "blocked"}, "source_fixture_classification": {"status": "verified" if passed else "blocked"}},
|
|
"boundaries": ["host Python/dynamic loader/device paths remain explicit capability boundaries", "official LinuxCNC canonical oracle remains separate", "case-specific GUI plugin closure remains S8-10/browser work"],
|
|
}
|
|
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
|
|
destination = output if output.is_absolute() else ROOT / output; destination.parent.mkdir(parents=True, exist_ok=True); destination.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
return report
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(); parser.add_argument("--report", type=Path, default=REPORT); args = parser.parse_args(); report = generate(args.report); print(f"runtime_extensions_s8_09={report['status']} passed={report['counts']['passed']}/{report['counts']['runners']} report_sha256={report['report_sha256']}"); return 0 if report["status"] == "verified" else 1
|
|
|
|
|
|
if __name__ == "__main__": raise SystemExit(main())
|