Files
cthulhu/tests/test_compositor_state_adapter_regressions.py
T

137 lines
5.5 KiB
Python

import sys
import unittest
from pathlib import Path
from unittest import mock
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cthulhu import cthulhu_state
from cthulhu import cthulhu
from cthulhu import compositor_state_adapter
from cthulhu import compositor_state_types
class FakeWorkspaceBackend:
def __init__(self, available: bool, name: str) -> None:
self.available = available
self.name = name
self.activate_calls = []
self.deactivate_calls = []
def is_available(self, session_type: str | None = None) -> bool:
return self.available
def activate(self, adapter=None) -> None:
self.activate_calls.append(adapter)
def deactivate(self, adapter=None) -> None:
self.deactivate_calls.append(adapter)
class CompositorStateAdapterRegressionTests(unittest.TestCase):
def setUp(self) -> None:
cthulhu_state.compositorSnapshot = None
cthulhu_state.pauseAtspiChurn = False
cthulhu_state.prioritizedDesktopContextToken = None
def test_activate_selects_first_available_backend(self) -> None:
unavailableBackend = FakeWorkspaceBackend(False, "unavailable")
selectedBackend = FakeWorkspaceBackend(True, "selected")
adapter = compositor_state_adapter.CompositorStateAdapter(
workspace_backends=[unavailableBackend, selectedBackend]
)
adapter.activate()
self.assertEqual(unavailableBackend.activate_calls, [])
self.assertEqual(selectedBackend.activate_calls, [adapter])
self.assertEqual(adapter.get_snapshot().backend_name, "selected")
def test_activate_is_idempotent_and_deactivates_previous_backend(self) -> None:
backend = FakeWorkspaceBackend(True, "selected")
adapter = compositor_state_adapter.CompositorStateAdapter(workspace_backends=[backend])
adapter.activate()
adapter.activate()
self.assertEqual(backend.activate_calls, [adapter, adapter])
self.assertEqual(backend.deactivate_calls, [adapter])
self.assertEqual(adapter.get_snapshot().backend_name, "selected")
def test_sync_accessible_context_emits_focus_context_changed_when_active_window_token_changes(self) -> None:
adapter = compositor_state_adapter.CompositorStateAdapter()
events = []
adapter.add_listener(events.append)
firstWindow = object()
secondWindow = object()
def get_process_id(obj):
return 111 if obj is firstWindow else 222
def get_name(obj):
return "Terminal"
with (
mock.patch.object(compositor_state_adapter.AXObject, "get_process_id", side_effect=get_process_id),
mock.patch.object(compositor_state_adapter.AXObject, "get_name", side_effect=get_name),
):
cthulhu_state.activeWindow = firstWindow
cthulhu_state.locusOfFocus = firstWindow
adapter.sync_accessible_context("startup")
events.clear()
cthulhu_state.activeWindow = secondWindow
cthulhu_state.locusOfFocus = secondWindow
adapter.sync_accessible_context("workspace transition")
self.assertIn(
compositor_state_types.DESKTOP_FOCUS_CONTEXT_CHANGED,
[event.type for event in events],
)
self.assertIn(
compositor_state_types.PRIORITIZE_FOCUS,
[event.type for event in events],
)
def test_sync_accessible_context_builds_stable_active_window_tokens(self) -> None:
adapter = compositor_state_adapter.CompositorStateAdapter()
window = object()
with (
mock.patch.object(compositor_state_adapter.AXObject, "get_process_id", return_value=4242),
mock.patch.object(compositor_state_adapter.AXObject, "get_name", return_value="Terminal"),
):
cthulhu_state.activeWindow = window
cthulhu_state.locusOfFocus = window
snapshot = adapter.sync_accessible_context("startup")
self.assertEqual(snapshot.active_window_token, "4242:Terminal")
self.assertEqual(cthulhu_state.compositorSnapshot.active_window_token, "4242:Terminal")
def test_event_manager_startup_resyncs_adapter_after_focus_recovery(self) -> None:
adapter = mock.Mock()
adapter.sync_accessible_context = mock.Mock(return_value=None)
listener = mock.Mock()
window = object()
focusedObject = object()
with (
mock.patch.object(cthulhu.event_manager.Atspi.EventListener, "new", return_value=listener),
mock.patch.object(cthulhu.event_manager.AXUtilities, "can_be_active_window", return_value=False),
mock.patch.object(cthulhu.event_manager.AXUtilities, "find_active_window", return_value=window),
mock.patch.object(cthulhu.event_manager.AXUtilities, "get_focused_object", return_value=focusedObject),
mock.patch.object(cthulhu.event_manager.cthulhu, "setActiveWindow") as setActiveWindow,
mock.patch.object(cthulhu.event_manager.cthulhu, "setLocusOfFocus") as setLocusOfFocus,
):
manager = cthulhu.event_manager.EventManager(mock.Mock())
manager.set_compositor_state_adapter(adapter)
manager._sync_focus_on_startup()
setActiveWindow.assert_called_once_with(window, alsoSetLocusOfFocus=True, notifyScript=False)
setLocusOfFocus.assert_called_once_with(None, focusedObject, notifyScript=True, force=True)
adapter.sync_accessible_context.assert_called_once_with("event-manager-startup")
if __name__ == "__main__":
unittest.main()