225 lines
8.5 KiB
Python
225 lines
8.5 KiB
Python
import sys
|
|
import types
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
|
from cthulhu import input_event_manager
|
|
from cthulhu import mouse_review
|
|
|
|
|
|
class FakeDevice:
|
|
def __init__(self):
|
|
self.add_key_watcher_calls = []
|
|
self.connect_calls = []
|
|
self.disconnect_calls = []
|
|
self.set_capabilities_calls = []
|
|
self.capabilities = 0
|
|
self.next_handler_id = 17
|
|
|
|
def add_key_watcher(self, callback, user_data=None):
|
|
self.add_key_watcher_calls.append((callback, user_data))
|
|
|
|
def connect(self, signalName, callback):
|
|
self.connect_calls.append((signalName, callback))
|
|
return self.next_handler_id
|
|
|
|
def disconnect(self, handlerId):
|
|
self.disconnect_calls.append(handlerId)
|
|
|
|
def get_capabilities(self):
|
|
return self.capabilities
|
|
|
|
def set_capabilities(self, capabilities):
|
|
self.set_capabilities_calls.append(capabilities)
|
|
self.capabilities = capabilities
|
|
return capabilities
|
|
|
|
|
|
class FakeDeviceFactory:
|
|
def __init__(self, device):
|
|
self.device = device
|
|
self.new_calls = 0
|
|
self.new_full_calls = 0
|
|
self.app_ids = []
|
|
|
|
def new(self):
|
|
self.new_calls += 1
|
|
return self.device
|
|
|
|
def new_full(self, appId):
|
|
self.new_full_calls += 1
|
|
self.app_ids.append(appId)
|
|
return self.device
|
|
|
|
|
|
class InputEventManagerPointerMonitorTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.manager = input_event_manager.InputEventManager()
|
|
|
|
def test_activate_device_creates_the_device_only_once(self):
|
|
device = FakeDevice()
|
|
deviceFactory = FakeDeviceFactory(device)
|
|
fakeAtspi = types.SimpleNamespace(
|
|
get_version=lambda: (2, 58, 4),
|
|
Device=deviceFactory,
|
|
)
|
|
|
|
with mock.patch.object(input_event_manager, "Atspi", fakeAtspi):
|
|
firstDevice = self.manager.activate_device()
|
|
secondDevice = self.manager.activate_device()
|
|
|
|
self.assertIs(firstDevice, device)
|
|
self.assertIs(secondDevice, device)
|
|
self.assertEqual(deviceFactory.new_full_calls, 1)
|
|
self.assertEqual(deviceFactory.new_calls, 0)
|
|
self.assertEqual(deviceFactory.app_ids, ["org.stormux.Cthulhu"])
|
|
|
|
def test_enable_pointer_monitoring_returns_false_without_a_device(self):
|
|
fakeAtspi = types.SimpleNamespace()
|
|
|
|
with mock.patch.object(input_event_manager, "Atspi", fakeAtspi):
|
|
self.assertFalse(self.manager.enable_pointer_monitoring())
|
|
|
|
def test_enable_pointer_monitoring_returns_false_when_device_capability_is_missing(self):
|
|
device = FakeDevice()
|
|
deviceFactory = FakeDeviceFactory(device)
|
|
fakeAtspi = types.SimpleNamespace(
|
|
get_version=lambda: (2, 58, 4),
|
|
Device=deviceFactory,
|
|
)
|
|
|
|
with mock.patch.object(input_event_manager, "Atspi", fakeAtspi):
|
|
self.manager.activate_device()
|
|
self.assertFalse(self.manager.enable_pointer_monitoring())
|
|
|
|
self.assertEqual(device.set_capabilities_calls, [])
|
|
|
|
def test_enable_pointer_monitoring_returns_true_when_pointer_monitor_is_granted(self):
|
|
device = FakeDevice()
|
|
deviceFactory = FakeDeviceFactory(device)
|
|
fakeDeviceCapability = types.SimpleNamespace(POINTER_MONITOR=8)
|
|
fakeAtspi = types.SimpleNamespace(
|
|
get_version=lambda: (2, 58, 4),
|
|
Device=deviceFactory,
|
|
DeviceCapability=fakeDeviceCapability,
|
|
)
|
|
|
|
with mock.patch.object(input_event_manager, "Atspi", fakeAtspi):
|
|
self.manager.activate_device()
|
|
self.assertTrue(self.manager.enable_pointer_monitoring())
|
|
|
|
self.assertEqual(device.set_capabilities_calls, [8])
|
|
self.assertEqual(device.capabilities, 8)
|
|
|
|
def test_start_and_stop_pointer_watcher_connect_and_disconnect_pointer_moved(self):
|
|
device = FakeDevice()
|
|
deviceFactory = FakeDeviceFactory(device)
|
|
fakeAtspi = types.SimpleNamespace(
|
|
get_version=lambda: (2, 58, 4),
|
|
Device=deviceFactory,
|
|
)
|
|
callback = mock.Mock()
|
|
|
|
with mock.patch.object(input_event_manager, "Atspi", fakeAtspi):
|
|
self.manager.activate_device()
|
|
self.manager.start_pointer_watcher(callback)
|
|
self.manager.stop_pointer_watcher()
|
|
|
|
self.assertEqual(device.connect_calls, [("pointer-moved", callback)])
|
|
self.assertEqual(device.disconnect_calls, [17])
|
|
|
|
|
|
class MouseReviewBackendSelectionTests(unittest.TestCase):
|
|
@staticmethod
|
|
def _make_app(enabled=False):
|
|
app = mock.Mock()
|
|
settingsManager = mock.Mock()
|
|
settingsManager.getSetting.return_value = enabled
|
|
app.getSettingsManager.return_value = settingsManager
|
|
app.getScriptManager.return_value = mock.Mock()
|
|
return app
|
|
|
|
def test_prefers_atspi_backend_when_version_and_capability_are_available(self):
|
|
listener = mock.Mock()
|
|
deviceManager = mock.Mock()
|
|
deviceManager.enable_pointer_monitoring.return_value = True
|
|
|
|
with (
|
|
mock.patch.object(mouse_review.Atspi.EventListener, "new", return_value=listener),
|
|
mock.patch.object(mouse_review.Atspi, "get_version", return_value=(2, 60, 0)),
|
|
mock.patch.object(
|
|
mouse_review.Atspi,
|
|
"DeviceCapability",
|
|
new=types.SimpleNamespace(POINTER_MONITOR=8),
|
|
create=True,
|
|
),
|
|
mock.patch.object(mouse_review.input_event_manager, "get_manager", return_value=deviceManager),
|
|
mock.patch.object(mouse_review, "Wnck", None),
|
|
):
|
|
reviewer = mouse_review.MouseReviewer(self._make_app())
|
|
|
|
self.assertTrue(reviewer._useAtspi)
|
|
self.assertTrue(reviewer._mouseReviewCapable)
|
|
deviceManager.activate_device.assert_called_once_with()
|
|
deviceManager.enable_pointer_monitoring.assert_called_once_with()
|
|
|
|
def test_activate_uses_pointer_watcher_for_atspi_backend(self):
|
|
listener = mock.Mock()
|
|
deviceManager = mock.Mock()
|
|
deviceManager.enable_pointer_monitoring.return_value = True
|
|
|
|
with (
|
|
mock.patch.object(mouse_review.Atspi.EventListener, "new", return_value=listener),
|
|
mock.patch.object(mouse_review.Atspi, "get_version", return_value=(2, 60, 0)),
|
|
mock.patch.object(
|
|
mouse_review.Atspi,
|
|
"DeviceCapability",
|
|
new=types.SimpleNamespace(POINTER_MONITOR=8),
|
|
create=True,
|
|
),
|
|
mock.patch.object(mouse_review.input_event_manager, "get_manager", return_value=deviceManager),
|
|
mock.patch.object(mouse_review.cthulhu_state, "locusOfFocus", None),
|
|
mock.patch.object(mouse_review, "Wnck", None),
|
|
):
|
|
reviewer = mouse_review.MouseReviewer(self._make_app(enabled=False))
|
|
reviewer.activate()
|
|
reviewer.deactivate()
|
|
|
|
deviceManager.start_pointer_watcher.assert_called_once_with(reviewer._on_pointer_moved)
|
|
deviceManager.stop_pointer_watcher.assert_called_once_with()
|
|
listener.register.assert_not_called()
|
|
|
|
def test_keeps_legacy_backend_when_atspi_pointer_monitor_is_unavailable(self):
|
|
listener = mock.Mock()
|
|
pointer = mock.Mock()
|
|
seat = mock.Mock()
|
|
seat.get_pointer.return_value = pointer
|
|
screen = mock.Mock()
|
|
screen.get_windows_stacked.return_value = []
|
|
screen.get_active_workspace.return_value = None
|
|
fakeWnck = mock.Mock()
|
|
fakeWnck.Screen.get_default.return_value = screen
|
|
|
|
with (
|
|
mock.patch.object(mouse_review.Atspi.EventListener, "new", return_value=listener),
|
|
mock.patch.object(mouse_review.Atspi, "get_version", return_value=(2, 58, 4)),
|
|
mock.patch.object(mouse_review.input_event_manager, "get_manager"),
|
|
mock.patch.object(mouse_review.cthulhu_state, "locusOfFocus", None),
|
|
mock.patch.object(mouse_review.Gdk.Display, "get_default", return_value=mock.Mock()),
|
|
mock.patch.object(mouse_review.Gdk.Display, "get_default_seat", return_value=seat),
|
|
mock.patch.object(mouse_review, "Wnck", fakeWnck),
|
|
):
|
|
reviewer = mouse_review.MouseReviewer(self._make_app(enabled=False))
|
|
reviewer.activate()
|
|
|
|
self.assertFalse(reviewer._useAtspi)
|
|
self.assertTrue(reviewer._mouseReviewCapable)
|
|
listener.register.assert_called_once_with("mouse:abs")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|