feat: guided diagnostic session (CLI) — pick a game, capture, analyze — 0.11.0
The seed use case end to end, orchestrating M3 + M4 (ARCHITECTURE §7.1). - core/diagnostic.py: start(game) runs a focused, game-tagged capture into a dedicated diagnostic log (window-scoped report, separate from the always-on crash log); finish() stops it and combines the capture summary (M3) with the health findings (M4). Game recorded as a log event so it survives crash+reboot. - CLI: rigdoctor diagnose start --game/--appid | status | finish. - recorder/record run gained an optional --game tag; reccontrol passes it through. - Tests for game recovery + the finish() combination. GUI/tray "Run Diagnostic" button and auto start/stop (D12 wrapper) come next. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""Tests for the guided diagnostic orchestration (M3+M4 glue)."""
|
||||
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from rigdoctor.core import diagnostic
|
||||
from rigdoctor.core.crashlog import CrashLogWriter, summarize
|
||||
from rigdoctor.core.health import Finding
|
||||
from rigdoctor.core.sample import Reading, Sample
|
||||
|
||||
|
||||
def _write_log(path: str, game: str) -> None:
|
||||
w = CrashLogWriter(path)
|
||||
w.write_event("session-start", "interval=1s")
|
||||
w.write_event("game", game)
|
||||
for temp in (60.0, 72.0, 81.0):
|
||||
w.write_sample(Sample(ts=time.time(), readings=[Reading("gpu", "temp", temp, "°C", "")]))
|
||||
w.write_event("gpu-lost", "nvidia-smi query timed out")
|
||||
w.close()
|
||||
|
||||
|
||||
class GameRecoveryTests(unittest.TestCase):
|
||||
def test_game_recovered_from_log_event(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
log = str(Path(d) / "capture.jsonl")
|
||||
_write_log(log, "Path of Exile 2")
|
||||
summary = summarize(log)
|
||||
self.assertEqual(diagnostic._game_from_summary(summary), "Path of Exile 2")
|
||||
|
||||
def test_no_game_event_returns_none(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
log = str(Path(d) / "capture.jsonl")
|
||||
w = CrashLogWriter(log)
|
||||
w.write_event("session-start")
|
||||
w.close()
|
||||
self.assertIsNone(diagnostic._game_from_summary(summarize(log)))
|
||||
|
||||
|
||||
class FinishTests(unittest.TestCase):
|
||||
def test_finish_combines_summary_and_findings(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
log = Path(d) / "capture.jsonl"
|
||||
_write_log(str(log), "Satisfactory")
|
||||
fake = [Finding("warning", "GPU", "NVIDIA Xid 79 ×1", "fell off the bus")]
|
||||
with mock.patch("rigdoctor.core.health.run_health_checks", return_value=fake), \
|
||||
mock.patch.object(diagnostic.reccontrol, "stop_background", return_value=False), \
|
||||
mock.patch.object(diagnostic.reccontrol, "running_pid", return_value=None):
|
||||
result = diagnostic.finish(log_path=log)
|
||||
self.assertEqual(result.game, "Satisfactory")
|
||||
self.assertEqual(result.summary.samples, 3)
|
||||
self.assertEqual(result.findings, fake)
|
||||
# peak GPU temp captured in the window, GPU-lost event recorded
|
||||
self.assertEqual(result.summary.maxima["gpu.temp"][0], 81.0)
|
||||
self.assertTrue(any(kind == "gpu-lost" for _ts, kind, _d in result.summary.events))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user