mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 21:34:05 +01:00
systemd user service watches RandR output events and puts every connected monitor at native resolution + max refresh, DP-0 primary on the left, unplugged outputs switched off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWXa4ytX9adN7qpMAop
190 lines
6.7 KiB
Python
190 lines
6.7 KiB
Python
"""Tests for linux_configuration/features/monitor_autoconfig.py (I/O shell)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from types import SimpleNamespace
|
|
from typing import TYPE_CHECKING
|
|
|
|
import monitor_autoconfig
|
|
from monitor_autoconfig import apply_once, main, watch
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
|
|
OFF = """\
|
|
DP-0 connected primary (normal left inverted right x axis y axis)
|
|
3840x2160 60.00 + 144.00
|
|
HDMI-0 connected 2560x1440+3840+0 (normal left inverted right x axis y axis)
|
|
2560x1440 59.95 + 143.98*
|
|
"""
|
|
BEST = """\
|
|
DP-0 connected primary 3840x2160+0+0 (normal left inverted right x axis y axis)
|
|
3840x2160 60.00 + 144.00*
|
|
HDMI-0 connected 2560x1440+3840+0 (normal left inverted right x axis y axis)
|
|
2560x1440 59.95 + 143.98*
|
|
"""
|
|
WANT = [
|
|
"xrandr", "--output", "DP-0", "--mode", "3840x2160", "--rate", "144.00",
|
|
"--pos", "0x0", "--primary", "--output", "HDMI-0", "--mode", "2560x1440",
|
|
"--rate", "143.98", "--pos", "3840x0",
|
|
] # fmt: skip
|
|
|
|
|
|
NO_MODES_YET = """\
|
|
DP-0 connected primary (normal left inverted right x axis y axis)
|
|
HDMI-0 connected 2560x1440+3840+0 (normal left inverted right x axis y axis)
|
|
2560x1440 59.95 + 143.98*
|
|
"""
|
|
|
|
|
|
class FakeXrandr:
|
|
"""Serves queued ``xrandr --query`` texts and records apply calls."""
|
|
|
|
def __init__(self, *queries: str, fail_apply: bool = False) -> None:
|
|
self.queries = list(queries)
|
|
self.applied: list[list[str]] = []
|
|
self.fail_apply = fail_apply
|
|
|
|
def __call__(self, argv: list[str]) -> str:
|
|
if argv == ["xrandr", "--query"]:
|
|
return self.queries.pop(0)
|
|
self.applied.append(argv)
|
|
if self.fail_apply:
|
|
raise subprocess.CalledProcessError(1, argv, stderr="crtc failed")
|
|
return ""
|
|
|
|
|
|
class FakeDisplay:
|
|
"""Xlib display double: ``events`` batches, then the X server goes away."""
|
|
|
|
def __init__(self, batches: list[int]) -> None:
|
|
self.batches = batches
|
|
self.pending = 0
|
|
self.selected: list[int] = []
|
|
self.handshakes = 0
|
|
root = type("Root", (), {"xrandr_select_input": self.selected.append})()
|
|
self.screen: Callable[[], object] = type("S", (), {"root": root})
|
|
|
|
def next_event(self) -> None:
|
|
if self.pending:
|
|
self.pending -= 1
|
|
return
|
|
if not self.batches:
|
|
raise ConnectionError
|
|
self.pending = self.batches.pop(0) - 1
|
|
|
|
def pending_events(self) -> int:
|
|
return self.pending
|
|
|
|
def xrandr_query_version(self) -> None:
|
|
"""X only sends RandR 1.2+ output events after this handshake."""
|
|
self.handshakes += 1
|
|
|
|
|
|
def test_apply_once_runs_planned_xrandr() -> None:
|
|
"""An off monitor gets switched on with one xrandr call."""
|
|
fake = FakeXrandr(OFF)
|
|
assert apply_once("DP-0", run=fake) is True
|
|
assert fake.applied == [WANT]
|
|
|
|
|
|
def test_apply_once_noop_when_best() -> None:
|
|
"""Already-best layout means no xrandr call (no flicker on stray events)."""
|
|
fake = FakeXrandr(BEST)
|
|
assert apply_once("DP-0", run=fake) is False
|
|
assert fake.applied == []
|
|
|
|
|
|
def test_watch_applies_at_start_and_once_per_burst() -> None:
|
|
"""A burst of 3 hotplug events is drained and handled by one apply."""
|
|
fake = FakeXrandr(OFF, BEST)
|
|
display = FakeDisplay([3])
|
|
with pytest.raises(ConnectionError):
|
|
watch("DP-0", display=display, run=fake, settle=lambda: None)
|
|
assert display.handshakes == 1
|
|
assert display.selected == [monitor_autoconfig.OUTPUT_CHANGE_MASK]
|
|
assert fake.applied == [WANT]
|
|
assert fake.queries == []
|
|
|
|
|
|
def test_main_dry_run_prints_command(capsys: pytest.CaptureFixture[str]) -> None:
|
|
"""``--dry-run`` prints the xrandr command and changes nothing."""
|
|
fake = FakeXrandr(OFF)
|
|
assert main(["--dry-run"], run=fake) == 0
|
|
assert capsys.readouterr().out.split() == WANT
|
|
assert fake.applied == []
|
|
|
|
|
|
def test_main_once_applies(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""``--once`` applies the layout and exits without watching."""
|
|
fake = FakeXrandr(OFF)
|
|
monkeypatch.setattr(monitor_autoconfig, "watch", pytest.fail)
|
|
assert main(["--once", "--primary", "DP-0"], run=fake) == 0
|
|
assert fake.applied == [WANT]
|
|
|
|
|
|
def test_main_default_watches(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""With no mode flag, ``main`` hands off to ``watch``."""
|
|
seen: list[str] = []
|
|
monkeypatch.setattr(monitor_autoconfig, "watch", seen.append)
|
|
assert main([]) == 0
|
|
assert seen == ["DP-0"]
|
|
|
|
|
|
def test_apply_once_waits_for_late_edid_modes() -> None:
|
|
"""A just-plugged monitor with no modes yet is re-queried, then applied."""
|
|
fake = FakeXrandr(NO_MODES_YET, OFF)
|
|
settles: list[None] = []
|
|
assert apply_once("DP-0", run=fake, settle=lambda: settles.append(None))
|
|
assert settles == [None]
|
|
assert fake.applied == [WANT]
|
|
|
|
|
|
def test_apply_once_gives_up_waiting_after_tries() -> None:
|
|
"""Modes that never appear stop the retry; the rest is still laid out."""
|
|
fake = FakeXrandr(NO_MODES_YET, NO_MODES_YET)
|
|
assert apply_once("DP-0", run=fake, settle=lambda: None, tries=2) is True
|
|
assert fake.applied[0][:3] == ["xrandr", "--output", "HDMI-0"]
|
|
|
|
|
|
def test_watch_survives_failed_xrandr(capsys: pytest.CaptureFixture[str]) -> None:
|
|
"""A failing apply is logged and the watcher keeps handling events."""
|
|
fake = FakeXrandr(OFF, OFF, fail_apply=True)
|
|
with pytest.raises(ConnectionError):
|
|
watch("DP-0", display=FakeDisplay([1]), run=fake, settle=lambda: None)
|
|
assert fake.applied == [WANT, WANT]
|
|
assert "crtc failed" in capsys.readouterr().err
|
|
|
|
|
|
def test_run_returns_stdout(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""``_run`` is a checked subprocess call returning stdout."""
|
|
calls: list[tuple[list[str], dict[str, object]]] = []
|
|
|
|
def fake_run(argv: list[str], **kwargs: object) -> SimpleNamespace:
|
|
calls.append((argv, kwargs))
|
|
return SimpleNamespace(stdout="out")
|
|
|
|
monkeypatch.setattr(monitor_autoconfig.subprocess, "run", fake_run)
|
|
assert monitor_autoconfig._run(["xrandr"]) == "out"
|
|
assert calls[0][1]["check"] is True
|
|
|
|
|
|
def test_open_display_uses_xlib(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""The real display comes from ``Xlib.display.Display()``."""
|
|
sentinel = object()
|
|
fake_module = SimpleNamespace(Display=lambda: sentinel)
|
|
monkeypatch.setattr(
|
|
monitor_autoconfig.importlib, "import_module", lambda name: fake_module
|
|
)
|
|
assert monitor_autoconfig._open_display() is sentinel
|
|
|
|
|
|
def test_settle_sleeps_settle_seconds(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""The real settle waits ``SETTLE_SECONDS`` for EDID to arrive."""
|
|
slept: list[float] = []
|
|
monkeypatch.setattr(monitor_autoconfig.time, "sleep", slept.append)
|
|
monitor_autoconfig._settle()
|
|
assert slept == [monitor_autoconfig.SETTLE_SECONDS]
|