import sys import unittest from pathlib import Path from unittest.mock import MagicMock, patch ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) from desktop_x11 import X11Adapter class DesktopX11Tests(unittest.TestCase): def _adapter(self) -> X11Adapter: return object.__new__(X11Adapter) @patch("desktop_x11.display.Display") def test_paste_clipboard_closes_display_on_success(self, display_cls): adapter = self._adapter() display_obj = MagicMock() display_cls.return_value = display_obj adapter._send_combo = MagicMock() adapter._paste_clipboard() adapter._send_combo.assert_called_once_with(display_obj, ["Control_L", "Shift_L", "v"]) display_obj.close.assert_called_once_with() @patch("desktop_x11.display.Display") def test_paste_clipboard_closes_display_on_send_error(self, display_cls): adapter = self._adapter() display_obj = MagicMock() display_cls.return_value = display_obj adapter._send_combo = MagicMock(side_effect=RuntimeError("boom")) with self.assertRaisesRegex(RuntimeError, "boom"): adapter._paste_clipboard() display_obj.close.assert_called_once_with() @patch("desktop_x11.display.Display") def test_type_text_closes_display_on_success(self, display_cls): adapter = self._adapter() display_obj = MagicMock() display_cls.return_value = display_obj adapter._send_combo = MagicMock() adapter._keysym_for_char = MagicMock(return_value=(42, False)) adapter._type_text("a") adapter._send_combo.assert_called_once_with(display_obj, [42], already_keysym=True) display_obj.close.assert_called_once_with() @patch("desktop_x11.display.Display") def test_type_text_closes_display_on_send_error(self, display_cls): adapter = self._adapter() display_obj = MagicMock() display_cls.return_value = display_obj adapter._send_combo = MagicMock(side_effect=RuntimeError("boom")) adapter._keysym_for_char = MagicMock(return_value=(42, False)) with self.assertRaisesRegex(RuntimeError, "boom"): adapter._type_text("a") display_obj.close.assert_called_once_with() @patch("desktop_x11.xtest.fake_input") def test_send_combo_releases_keys_and_syncs(self, fake_input): adapter = self._adapter() dpy = MagicMock() dpy.keysym_to_keycode.side_effect = [10, 20, 30] adapter._send_combo(dpy, ["Control_L", "Shift_L", "v"]) expected_calls = [ ((dpy, 2, 10),), ((dpy, 2, 20),), ((dpy, 2, 30),), ((dpy, 3, 30),), ((dpy, 3, 20),), ((dpy, 3, 10),), ] self.assertEqual(fake_input.call_args_list, expected_calls) dpy.sync.assert_called_once_with() @patch("desktop_x11.xtest.fake_input") def test_send_combo_releases_pressed_keys_when_press_fails(self, fake_input): adapter = self._adapter() dpy = MagicMock() dpy.keysym_to_keycode.side_effect = [10, 20] def _fake_input(_dpy, event_type, code): if event_type == 2 and code == 20: raise RuntimeError("press failed") return None fake_input.side_effect = _fake_input with self.assertRaisesRegex(RuntimeError, "press failed"): adapter._send_combo(dpy, ["Control_L", "v"]) expected_calls = [ ((dpy, 2, 10),), ((dpy, 2, 20),), ((dpy, 3, 10),), ] self.assertEqual(fake_input.call_args_list, expected_calls) dpy.sync.assert_called_once_with() if __name__ == "__main__": unittest.main()