Refactor KVM switch logic to enforce input source based on auxiliary monitor presence and update related documentation and tests

This commit is contained in:
Lago
2026-03-27 16:05:20 +01:00
parent 3f7c5a0677
commit dc3b67d00a
4 changed files with 111 additions and 42 deletions
+94 -7
View File
@@ -266,7 +266,7 @@ def test_polling_uses_configured_auxiliary_monitor() -> None:
assert not status["errors"]
def test_polling_does_not_switch_when_trigger_does_not_match_device_port() -> None:
def test_polling_switches_even_when_trigger_input_does_not_match_device_port() -> None:
service_module = _require_module("app.service")
hardware_module = _require_module("app.hardware")
ddm_module = _require_module("app.ddm")
@@ -278,12 +278,17 @@ def test_polling_does_not_switch_when_trigger_does_not_match_device_port() -> No
pytest.skip("Service backend interfaces are not available yet.")
class FakeMonitorBackend:
def __init__(self) -> None:
self.call_count = 0
def scan(self):
self.call_count += 1
alienware_input = 15 if self.call_count == 1 else 19
return HardwareScan(
samsung_present=True,
trigger_input_code=15,
alienware_detected=True,
alienware_input_code=15,
alienware_input_code=alienware_input,
errors=[],
)
@@ -314,9 +319,9 @@ def test_polling_does_not_switch_when_trigger_does_not_match_device_port() -> No
retry_wait_seconds=0.0,
)
status = service.save_settings(device_port="DP2")
assert status["trigger_matches_device_port"] is False
assert status["last_switch_result"] == "waiting_for_trigger_match"
assert backend.calls == []
assert status["trigger_matches_device_port"] is True
assert status["last_switch_result"] == "switched"
assert backend.calls == [(1, "DP2")]
def test_polling_switches_with_monitor_targeting_when_slot_is_unavailable() -> None:
@@ -443,7 +448,7 @@ def test_polling_switches_when_alienware_input_is_unreadable() -> None:
assert backend.calls == [(1, "DP1")]
def test_polling_resets_session_when_trigger_changes_without_disconnect() -> None:
def test_polling_does_not_reswitch_after_manual_change_while_monitor_stays_connected() -> None:
service_module = _require_module("app.service")
hardware_module = _require_module("app.hardware")
ddm_module = _require_module("app.ddm")
@@ -521,7 +526,89 @@ def test_polling_resets_session_when_trigger_changes_without_disconnect() -> Non
first = service.save_settings(device_port="DP2")
assert first["last_switch_result"] == "noop"
second = service.poll_once()
assert second["last_switch_result"] == "waiting_for_trigger_match"
assert second["last_switch_result"] == "waiting_for_disconnect"
assert backend.calls == []
def test_polling_switches_again_after_aux_monitor_disconnect_and_reconnect() -> None:
service_module = _require_module("app.service")
hardware_module = _require_module("app.hardware")
ddm_module = _require_module("app.ddm")
KvmSwitcherService = getattr(service_module, "KvmSwitcherService", None)
HardwareScan = getattr(hardware_module, "HardwareScan", None)
DDMCommandResult = getattr(ddm_module, "DDMCommandResult", None)
if KvmSwitcherService is None or HardwareScan is None or DDMCommandResult is None:
pytest.skip("Service backend interfaces are not available yet.")
class FakeMonitorBackend:
def __init__(self) -> None:
self.call_count = 0
def scan(self):
self.call_count += 1
if self.call_count == 1:
return HardwareScan(
samsung_present=True,
trigger_input_code=19,
alienware_detected=True,
alienware_input_code=19,
errors=[],
)
if self.call_count == 2:
return HardwareScan(
samsung_present=False,
trigger_input_code=None,
alienware_detected=True,
alienware_input_code=15,
errors=[],
)
if self.call_count == 3:
return HardwareScan(
samsung_present=True,
trigger_input_code=19,
alienware_detected=True,
alienware_input_code=15,
errors=[],
)
return HardwareScan(
samsung_present=True,
trigger_input_code=19,
alienware_detected=True,
alienware_input_code=19,
errors=[],
)
class FakeDDMBackend:
def __init__(self) -> None:
self.calls: list[tuple[int, str]] = []
def is_available(self) -> bool:
return True
def resolve_alienware_slot(self, force: bool = False) -> int | None:
return 1
def invalidate_slot(self) -> None:
return None
def switch_to_port(self, slot: int, port_name: str):
self.calls.append((slot, port_name))
return DDMCommandResult(True, "ok")
config_path = Path(tempfile.mkdtemp()) / "config.json"
backend = FakeDDMBackend()
service = KvmSwitcherService(
config_store=ConfigStore(config_path),
monitor_backend=FakeMonitorBackend(),
ddm_backend=backend,
poll_interval_seconds=0.01,
retry_wait_seconds=0.0,
)
first = service.save_settings(device_port="DP2")
assert first["last_switch_result"] == "noop"
second = service.poll_once()
assert second["last_switch_result"] == "idle"
third = service.poll_once()
assert third["last_switch_result"] == "switched"
assert backend.calls == [(1, "DP2")]