03b2dd8363
D12 "build first" wrapper: `rigdoctor wrap %command%` (Steam launch option / Lutris/Heroic wrapper field) auto-brackets a focused diagnostic around a game — start a game-tagged capture on launch, clean stop on exit; a hard freeze leaves it unterminated → flagged as a crash next launch. - core/wrap.py: game name from SteamAppId, PATH-proof launch_option(), run() that doesn't disturb an existing capture and returns the game's exit code. - diagnostic.start() preserves an unanalyzed crash to diagnostic-crash.jsonl before clearing, so auto-relaunch can't wipe an unseen crash; pending_crash/ analyze_crash check the archive first. - GUI: "Auto-capture…" helper dialog (copyable launch-option string). - Tests for wrap (name resolution, exit-code passthrough, no-double-start). - docs: fix stale MODULES.md status column (M1/M3/M4/M5/M8/M10/M13 → done), update ROADMAP/MODULES for the wrapper + crash detection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.8 KiB
Python
69 lines
2.8 KiB
Python
"""Tests for the D12 Steam-launch wrapper (rigdoctor wrap %command%)."""
|
|
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
from rigdoctor.core import wrap
|
|
from rigdoctor.core.steam import Game
|
|
|
|
|
|
class LaunchOptionTests(unittest.TestCase):
|
|
def test_format(self):
|
|
opt = wrap.launch_option()
|
|
self.assertTrue(opt.endswith("wrap %command%"))
|
|
self.assertIn("rigdoctor", opt)
|
|
|
|
|
|
class GameNameTests(unittest.TestCase):
|
|
def test_resolves_from_steam_appid(self):
|
|
g = Game(appid="570", name="Dota 2", library="/x", installdir="dota")
|
|
with mock.patch.dict("os.environ", {"SteamAppId": "570"}), \
|
|
mock.patch("rigdoctor.core.steam.cached_games", return_value=[g]):
|
|
self.assertEqual(wrap.game_name_from_env(), "Dota 2")
|
|
|
|
def test_unknown_appid_falls_back(self):
|
|
with mock.patch.dict("os.environ", {"SteamAppId": "999"}), \
|
|
mock.patch("rigdoctor.core.steam.cached_games", return_value=[]), \
|
|
mock.patch("rigdoctor.core.steam.scan_games", return_value=[]):
|
|
self.assertEqual(wrap.game_name_from_env(), "Steam app 999")
|
|
|
|
def test_none_without_steam_env(self):
|
|
with mock.patch.dict("os.environ", {}, clear=True):
|
|
self.assertIsNone(wrap.game_name_from_env())
|
|
|
|
|
|
class RunTests(unittest.TestCase):
|
|
def test_brackets_capture_and_returns_exit_code(self):
|
|
with mock.patch("rigdoctor.core.reccontrol.running_pid", return_value=None), \
|
|
mock.patch("rigdoctor.core.diagnostic.start", return_value=123) as start, \
|
|
mock.patch("rigdoctor.core.reccontrol.stop_background") as stop, \
|
|
mock.patch.dict("os.environ", {}, clear=True):
|
|
rc = wrap.run(["true"])
|
|
self.assertEqual(rc, 0)
|
|
start.assert_called_once()
|
|
stop.assert_called_once()
|
|
|
|
def test_propagates_game_failure(self):
|
|
with mock.patch("rigdoctor.core.reccontrol.running_pid", return_value=None), \
|
|
mock.patch("rigdoctor.core.diagnostic.start", return_value=123), \
|
|
mock.patch("rigdoctor.core.reccontrol.stop_background"), \
|
|
mock.patch.dict("os.environ", {}, clear=True):
|
|
self.assertEqual(wrap.run(["false"]), 1)
|
|
|
|
def test_does_not_touch_an_existing_capture(self):
|
|
with mock.patch("rigdoctor.core.reccontrol.running_pid", return_value=999), \
|
|
mock.patch("rigdoctor.core.diagnostic.start") as start, \
|
|
mock.patch("rigdoctor.core.reccontrol.stop_background") as stop, \
|
|
mock.patch.dict("os.environ", {}, clear=True):
|
|
rc = wrap.run(["true"])
|
|
self.assertEqual(rc, 0)
|
|
start.assert_not_called()
|
|
stop.assert_not_called()
|
|
|
|
def test_empty_command_is_usage_error(self):
|
|
self.assertEqual(wrap.run([]), 2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|