summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py123
1 files changed, 123 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py
new file mode 100644
index 0000000..808c227
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+"""Run every value-validation demo and emit one machine-readable results JSON.
+
+Usage (from the tool root, ``open-iov/debug/risc-v-gsp-rm-vm``):
+
+ python3 harness/value_demos/run_all.py
+ python3 harness/value_demos/run_all.py --only s01,s03,s05,s09
+ VALUE_DEMO_BACKEND=qemu python3 harness/value_demos/run_all.py
+
+Each demo runs in its own subprocess with a hard timeout, so one wedged demo
+cannot take the suite down. The aggregate lands in
+``harness/value_demos/artifacts/results.json``.
+"""
+from __future__ import annotations
+
+import argparse
+import json
+import os
+import subprocess
+import sys
+import time
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+TOOL_ROOT = os.path.dirname(os.path.dirname(HERE))
+sys.path.insert(0, HERE)
+
+from _common import ARTIFACT_ROOT, SUITE_BUDGET_S, write_json # noqa: E402
+
+DEMOS = [
+ ("s01", "s01_reply_consumer.py"),
+ ("s02", "s02_counter_publication.py"),
+ ("s03", "s03_unknown_command.py"),
+ ("s04", "s04_run_until_function.py"),
+ ("s05", "s05_snapshot_diff.py"),
+ ("s06", "s06_inject_service.py"),
+ ("s07", "s07_version_diff.py"),
+ ("s08", "s08_mutation_triage.py"),
+ ("s09", "s09_grabbing_a_ghidra_address.py"),
+ ("s10", "s10_full_session_evidence.py"),
+]
+
+
+def _run_one(key: str, script: str, timeout: float) -> dict:
+ path = os.path.join(HERE, script)
+ start = time.monotonic()
+ try:
+ proc = subprocess.run(
+ [sys.executable, path],
+ cwd=TOOL_ROOT,
+ capture_output=True,
+ text=True,
+ timeout=timeout,
+ )
+ stdout, stderr, returncode = proc.stdout, proc.stderr, proc.returncode
+ except subprocess.TimeoutExpired as exc:
+ stdout = (exc.stdout or b"").decode("utf-8", "replace") if isinstance(exc.stdout, bytes) else (exc.stdout or "")
+ stderr = (exc.stderr or b"").decode("utf-8", "replace") if isinstance(exc.stderr, bytes) else (exc.stderr or "")
+ returncode = -9
+ elapsed = time.monotonic() - start
+ result = None
+ for line in reversed(stdout.splitlines()):
+ if line.startswith("RESULT_JSON: "):
+ try:
+ result = json.loads(line[len("RESULT_JSON: ") :])
+ except json.JSONDecodeError:
+ result = None
+ break
+ if result is None:
+ result = {
+ "success": False,
+ "failure": f"no RESULT_JSON (exit {returncode})",
+ "stderr_tail": stderr[-800:],
+ }
+ result["demo_key"] = key
+ result["runner_elapsed_s"] = round(elapsed, 4)
+ result["exit_code"] = returncode
+ return result
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--only", default="", help="comma-separated demo keys (e.g. s01,s03)")
+ parser.add_argument("--timeout", type=float, default=45.0, help="per-demo timeout seconds")
+ parser.add_argument("--out", default=os.path.join(ARTIFACT_ROOT, "results.json"))
+ args = parser.parse_args()
+
+ selected = [item for item in DEMOS if not args.only or item[0] in args.only.split(",")]
+ if args.only and not selected:
+ print(f"no demos match --only {args.only!r}", file=sys.stderr)
+ return 2
+
+ started = time.monotonic()
+ results = []
+ for key, script in selected:
+ print(f"--- running {script} ---", flush=True)
+ result = _run_one(key, script, args.timeout)
+ results.append(result)
+ status = "PASS" if result.get("success") else "FAIL"
+ print(f"--- {key}: {status} ({result['runner_elapsed_s']:.2f}s) ---", flush=True)
+ if time.monotonic() - started > SUITE_BUDGET_S:
+ print(f"[run_all] suite budget {SUITE_BUDGET_S:.0f}s exceeded; continuing", flush=True)
+
+ total = time.monotonic() - started
+ passed = [r for r in results if r.get("success")]
+ payload = {
+ "format": "greeneye-value-demos-results/1",
+ "tool": "harness/value_demos/run_all.py",
+ "backend_env": os.environ.get("VALUE_DEMO_BACKEND", "native (default)"),
+ "demos_selected": [k for k, _ in selected],
+ "demos_passed": [r["demo_key"] for r in passed],
+ "demos_failed": [r["demo_key"] for r in results if not r.get("success")],
+ "total_elapsed_s": round(total, 3),
+ "measured_elapsed_s": round(sum(r["runner_elapsed_s"] for r in results), 3),
+ "results": results,
+ }
+ write_json(args.out, payload)
+ print(f"\n{len(passed)}/{len(results)} demos passed in {total:.1f}s; results: {args.out}")
+ print(f"RESULT_JSON: {json.dumps({k: payload[k] for k in ('format', 'demos_passed', 'demos_failed', 'total_elapsed_s')}, sort_keys=True)}")
+ return 0 if len(passed) == len(results) else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())