feat(app-binding): lier les presets aux applications
Ajoute la configuration des associations application/preset, le service de suivi du focus, l'intégration GUI et les fichiers d'installation nécessaires. Ajoute les tests unitaires ciblés des bindings, du service de focus, des migrations et des composants GUI associés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -969,3 +969,168 @@ class TestDataManager(unittest.TestCase):
|
||||
|
||||
def test_cannot_get_injector_state_without_group(self):
|
||||
self.assertRaises(DataManagementError, self.data_manager.get_state)
|
||||
|
||||
|
||||
class FakeFocusServiceClient:
|
||||
"""A FocusServiceClient stand-in that never touches the session bus."""
|
||||
|
||||
def __init__(self, status=None):
|
||||
self._status = status
|
||||
self.calls: List = []
|
||||
self.handler = None
|
||||
|
||||
def get_status(self):
|
||||
self.calls.append("get_status")
|
||||
return self._status
|
||||
|
||||
def reload(self):
|
||||
self.calls.append("reload")
|
||||
|
||||
def set_enabled(self, enabled):
|
||||
self.calls.append(("set_enabled", enabled))
|
||||
|
||||
def connect_focus_changed(self, callback):
|
||||
self.handler = callback
|
||||
self.calls.append("connect")
|
||||
return True
|
||||
|
||||
def disconnect_focus_changed(self):
|
||||
self.calls.append("disconnect")
|
||||
self.handler = None
|
||||
|
||||
|
||||
@test_setup
|
||||
class TestDataManagerAppBindings(unittest.TestCase):
|
||||
def _make_data_manager(self, status=None) -> DataManager:
|
||||
message_broker = MessageBroker()
|
||||
uinputs = GlobalUInputs(FrontendUInput)
|
||||
uinputs.prepare_all()
|
||||
self.focus_service = FakeFocusServiceClient(status=status)
|
||||
return DataManager(
|
||||
message_broker,
|
||||
GlobalConfig(),
|
||||
ReaderClient(message_broker, _Groups()),
|
||||
FakeDaemonProxy(),
|
||||
uinputs,
|
||||
keyboard_layout,
|
||||
focus_service=self.focus_service,
|
||||
)
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.data_manager = self._make_data_manager()
|
||||
self.message_broker = self.data_manager.message_broker
|
||||
|
||||
@staticmethod
|
||||
def _binding(app_id="firefox", presets=()):
|
||||
from inputremapper.configs.app_binding import AppBinding, BoundPreset
|
||||
|
||||
return AppBinding(
|
||||
app_id=app_id,
|
||||
presets=[BoundPreset(group_key=g, preset=p) for g, p in presets],
|
||||
)
|
||||
|
||||
def test_publish_app_bindings_unreachable(self):
|
||||
from inputremapper.gui.messages.message_data import AppBindingsData
|
||||
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.app_bindings, listener)
|
||||
self.data_manager.publish_app_bindings()
|
||||
|
||||
data: AppBindingsData = listener.calls[0]
|
||||
self.assertFalse(data.reachable)
|
||||
self.assertFalse(data.supported)
|
||||
self.assertIsNone(data.backend)
|
||||
self.assertFalse(data.enabled)
|
||||
self.assertEqual(data.bindings, ())
|
||||
|
||||
def test_publish_app_bindings_reachable(self):
|
||||
self.data_manager = self._make_data_manager(status={"backend": "xorg"})
|
||||
self.message_broker = self.data_manager.message_broker
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.app_bindings, listener)
|
||||
self.data_manager.publish_app_bindings()
|
||||
|
||||
data = listener.calls[0]
|
||||
self.assertTrue(data.reachable)
|
||||
self.assertTrue(data.supported)
|
||||
self.assertEqual(data.backend, "xorg")
|
||||
|
||||
def test_publish_app_bindings_reachable_but_unsupported(self):
|
||||
# service reachable but reports no backend (e.g. GNOME-Wayland / null)
|
||||
self.data_manager = self._make_data_manager(status={"backend": None})
|
||||
self.message_broker = self.data_manager.message_broker
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.app_bindings, listener)
|
||||
self.data_manager.publish_app_bindings()
|
||||
|
||||
data = listener.calls[0]
|
||||
self.assertTrue(data.reachable)
|
||||
self.assertFalse(data.supported)
|
||||
|
||||
def test_set_and_get_app_bindings(self):
|
||||
bindings = [self._binding("firefox", [("Foo Device", "preset1")])]
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.app_bindings, listener)
|
||||
|
||||
self.data_manager.set_app_bindings(bindings)
|
||||
|
||||
self.assertEqual(self.data_manager.get_app_bindings(), bindings)
|
||||
self.assertIn("reload", self.focus_service.calls)
|
||||
# publishes an app_bindings update
|
||||
self.assertEqual(listener.calls[-1].bindings, tuple(bindings))
|
||||
|
||||
def test_set_and_get_app_binding_enabled(self):
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.app_bindings, listener)
|
||||
|
||||
self.data_manager.set_app_binding_enabled(True)
|
||||
|
||||
self.assertTrue(self.data_manager.get_app_binding_enabled())
|
||||
self.assertIn(("set_enabled", True), self.focus_service.calls)
|
||||
self.assertTrue(listener.calls[-1].enabled)
|
||||
|
||||
def test_start_and_stop_app_detection(self):
|
||||
self.data_manager.start_app_detection()
|
||||
self.assertIn("connect", self.focus_service.calls)
|
||||
self.assertIsNotNone(self.focus_service.handler)
|
||||
|
||||
self.data_manager.stop_app_detection()
|
||||
self.assertIn("disconnect", self.focus_service.calls)
|
||||
self.assertIsNone(self.focus_service.handler)
|
||||
|
||||
def test_on_focus_changed_publishes_focused_app(self):
|
||||
from inputremapper.gui.messages.message_data import FocusAppData
|
||||
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.focused_app, listener)
|
||||
|
||||
self.data_manager.start_app_detection()
|
||||
# simulate the service emitting a focus_changed signal
|
||||
self.focus_service.handler({"app_id": "firefox", "title": "Mozilla"})
|
||||
|
||||
data: FocusAppData = listener.calls[0]
|
||||
self.assertEqual(data.app_id, "firefox")
|
||||
self.assertEqual(data.title, "Mozilla")
|
||||
|
||||
def test_on_focus_changed_handles_missing_fields(self):
|
||||
from inputremapper.gui.messages.message_data import FocusAppData
|
||||
|
||||
listener = Listener()
|
||||
self.message_broker.subscribe(MessageType.focused_app, listener)
|
||||
self.data_manager.start_app_detection()
|
||||
self.focus_service.handler({})
|
||||
|
||||
data: FocusAppData = listener.calls[0]
|
||||
self.assertEqual(data.app_id, "")
|
||||
self.assertEqual(data.title, "")
|
||||
|
||||
def test_get_presets_for_group(self):
|
||||
prepare_presets()
|
||||
presets = self.data_manager.get_presets_for_group("Foo Device")
|
||||
# newest first
|
||||
self.assertEqual(presets, ("preset3", "preset2", "preset1"))
|
||||
|
||||
def test_get_presets_for_unknown_group(self):
|
||||
self.assertEqual(
|
||||
self.data_manager.get_presets_for_group("Does Not Exist"), tuple()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user