feat(displays): monitors w/ resolution+refresh in Inventory; flag sub-max refresh in Health — 0.39.0
New core/displays.py reads connected monitors via GNOME Mutter DisplayConfig over D-Bus (busctl --json; works on X11 + Wayland), falling back to xrandr on other X11 desktops. Inventory's Display section now lists each monitor's resolution + current refresh (e.g. 'DP-1 · Samsung LC34G55T: 3440x1440 @ 165 Hz'). System Health (check_displays) flags a monitor running below its max refresh AT THE CURRENT resolution (e.g. 165 Hz panel set to 60 Hz) — never suggests lowering resolution. +tests (Mutter JSON + xrandr parsers, health check). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Tests for display detection (Mutter D-Bus JSON + xrandr parsers)."""
|
||||
|
||||
import unittest
|
||||
|
||||
from rigdoctor.core import displays
|
||||
|
||||
# Minimal Mutter GetCurrentState (busctl --json) shape: current mode is 60 Hz, panel max 165 Hz.
|
||||
_MUTTER_60 = (
|
||||
'{"type":"x","data":[1,[[["DP-1","SAM","LC34G55T","S"],['
|
||||
'["3440x1440@60",3440,1440,60.0,1.0,[1.0],{"is-current":{"type":"b","data":true}}],'
|
||||
'["3440x1440@165",3440,1440,165.0,1.0,[1.0],{"is-preferred":{"type":"b","data":true}}]'
|
||||
'],{}]],[],{}]}'
|
||||
)
|
||||
_MUTTER_MAX = (
|
||||
'{"type":"x","data":[1,[[["DP-1","SAM","LC34G55T","S"],['
|
||||
'["3440x1440@165",3440,1440,165.0,1.0,[1.0],{"is-current":{"type":"b","data":true}}],'
|
||||
'["3440x1440@60",3440,1440,60.0,1.0,[1.0],{}]'
|
||||
'],{}]],[],{}]}'
|
||||
)
|
||||
|
||||
_XRANDR_60 = """Screen 0: minimum 8 x 8, current 3440 x 1440, maximum 16384 x 16384
|
||||
DP-1 connected primary 3440x1440+0+0 (normal left inverted right x axis y axis) 800mm x 335mm
|
||||
3440x1440 60.00*+ 165.00 100.00
|
||||
2560x1440 165.00 60.00
|
||||
HDMI-1 disconnected (normal left inverted right x axis y axis)
|
||||
"""
|
||||
|
||||
|
||||
class MutterParseTests(unittest.TestCase):
|
||||
def test_parses_and_flags_higher_refresh(self):
|
||||
mons = displays._parse_mutter(_MUTTER_60)
|
||||
self.assertEqual(len(mons), 1)
|
||||
m = mons[0]
|
||||
self.assertEqual(m.connector, "DP-1")
|
||||
self.assertEqual(m.name, "Samsung LC34G55T") # PNP code SAM mapped
|
||||
self.assertEqual((m.width, m.height), (3440, 1440))
|
||||
self.assertEqual(round(m.refresh), 60)
|
||||
self.assertEqual(round(m.max_refresh), 165)
|
||||
self.assertTrue(m.can_go_faster)
|
||||
|
||||
def test_at_max_is_not_flagged(self):
|
||||
m = displays._parse_mutter(_MUTTER_MAX)[0]
|
||||
self.assertEqual(round(m.refresh), 165)
|
||||
self.assertFalse(m.can_go_faster)
|
||||
|
||||
def test_garbage_returns_empty(self):
|
||||
self.assertEqual(displays._parse_mutter("not json"), [])
|
||||
self.assertEqual(displays._parse_mutter("{}"), [])
|
||||
|
||||
|
||||
class XrandrParseTests(unittest.TestCase):
|
||||
def test_current_and_max_refresh(self):
|
||||
mons = displays._parse_xrandr(_XRANDR_60)
|
||||
self.assertEqual(len(mons), 1) # disconnected output ignored
|
||||
m = mons[0]
|
||||
self.assertEqual(m.connector, "DP-1")
|
||||
self.assertEqual((m.width, m.height), (3440, 1440))
|
||||
self.assertEqual(round(m.refresh), 60)
|
||||
self.assertEqual(round(m.max_refresh), 165)
|
||||
self.assertTrue(m.can_go_faster)
|
||||
|
||||
def test_empty_returns_empty(self):
|
||||
self.assertEqual(displays._parse_xrandr(""), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+17
-1
@@ -4,11 +4,12 @@ import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from rigdoctor.core import health
|
||||
from rigdoctor.core import displays, health
|
||||
from rigdoctor.core.health import (
|
||||
CRITICAL,
|
||||
INFO,
|
||||
WARNING,
|
||||
check_displays,
|
||||
check_pcie_links,
|
||||
run_health_checks,
|
||||
scan_journal_text,
|
||||
@@ -81,5 +82,20 @@ class PcieLinkCheckTests(unittest.TestCase):
|
||||
self.assertEqual(check_pcie_links(), [])
|
||||
|
||||
|
||||
class DisplayCheckTests(unittest.TestCase):
|
||||
def test_lower_than_max_refresh_is_flagged(self):
|
||||
mon = displays.Monitor("DP-1", "Samsung LC34G55T", 3440, 1440, 60.0, 165.0)
|
||||
with mock.patch("rigdoctor.core.displays.collect", return_value=[mon]):
|
||||
findings = check_displays()
|
||||
self.assertEqual(len(findings), 1)
|
||||
self.assertEqual(findings[0].severity, INFO)
|
||||
self.assertIn("165", findings[0].title)
|
||||
|
||||
def test_at_max_refresh_no_finding(self):
|
||||
mon = displays.Monitor("DP-1", "Samsung LC34G55T", 3440, 1440, 165.0, 165.0)
|
||||
with mock.patch("rigdoctor.core.displays.collect", return_value=[mon]):
|
||||
self.assertEqual(check_displays(), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user