"""Live integration smoke test: boots the demo uproc in QEMU. Skipped cleanly when qemu-system-riscv64 or the demo firmware is absent. """ from __future__ import annotations import os import shutil import unittest import greeneye as ge from greeneye.errors import GreenEyeError from greeneye.probes import compile_condition, install HERE = os.path.dirname(os.path.abspath(__file__)) DEMO_ELF = os.path.join(HERE, "..", "firmware", "uproc_demo", "build", "uproc_demo.elf") def qemu_available() -> bool: return bool(os.environ.get("GREENEYE_QEMU") or shutil.which("qemu-system-riscv64")) @unittest.skipUnless(qemu_available(), "qemu-system-riscv64 not installed") @unittest.skipUnless(os.path.exists(DEMO_ELF), "demo firmware not built (make demo)") class QemuSmokeTests(unittest.TestCase): def test_boot_hook_watch_snapshot_analyze(self): session = ge.launch(kernel=DEMO_ELF, record=True, name="unittest-smoke") hits = [] try: ctrl = session.symbols.lookup("gsp_ctrl") session.hook("rpc_sum", on_enter=lambda hit: hits.append(hit)) session.watch(ctrl + 16, size=8, kind="w") session.run(duration=0.6) self.assertTrue(session.target.wait_for_uart("RISC-V GSP-RM DEBUGGER UPROC DEMO", timeout=1.0)) self.assertGreaterEqual(len(hits), 1, "rpc_sum hook never fired") after = session.snapshot(ctrl, 0x188, "ctrl") self.assertEqual(int.from_bytes(after[0:4], "little"), 0x47535031) self.assertGreaterEqual(int.from_bytes(after[16:24], "little"), 7) finally: directory = getattr(session.recorder, "dir", None) session.close() if directory: from greeneye.analyze import render_summary report = render_summary(directory) self.assertIn("rpc_sum", report) def test_probe_spec_install(self): session = ge.launch(kernel=DEMO_ELF, name="unittest-probes") try: spec = { "probes": [ { "name": "sum", "symbol": "rpc_sum", "when": "a1 > 0", "on_enter": {"log": ["a0", "a1"], "note": "sum called"}, } ] } handles = install(session, spec) self.assertEqual(len(handles), 1) session.run(duration=0.6) self.assertEqual(handles[0].hits, 1) # self-test calls rpc_sum once finally: session.close() def test_condition_guard(self): self.assertIsNone(compile_condition(None)) check = compile_condition("a0 == 0x2a and a1 < 4") self.assertTrue(check({"a0": 0x2A, "a1": 2})) self.assertFalse(check({"a0": 0x2B, "a1": 2})) with self.assertRaises(GreenEyeError): compile_condition("__import__('os').system('true')") if __name__ == "__main__": unittest.main()