Files
cthulhu/tests/test_screen_lock_monitor.py

212 lines
8.0 KiB
Python

import sys
import types
import unittest
from unittest import mock
import cthulhu as cthulhu_package
from cthulhu import cthulhu_state
stubCthulhu = types.ModuleType("cthulhu.cthulhu")
stubCthulhu.cthulhuApp = mock.Mock()
stubCthulhu.cthulhuApp.getPluginSystemManager.return_value = None
previousCthulhuModule = sys.modules.get("cthulhu.cthulhu")
sys.modules.setdefault("cthulhu.cthulhu", stubCthulhu)
from cthulhu import event_manager
from cthulhu import input_event_manager
from cthulhu.screen_lock_monitor import ScreenLockMonitor
if previousCthulhuModule is None:
sys.modules.pop("cthulhu.cthulhu", None)
if getattr(cthulhu_package, "cthulhu", None) is stubCthulhu:
delattr(cthulhu_package, "cthulhu")
else:
sys.modules["cthulhu.cthulhu"] = previousCthulhuModule
class ScreenLockMonitorTests(unittest.TestCase):
def setUp(self):
self.app = mock.Mock()
self.monitor = ScreenLockMonitor(self.app)
def test_name_appearance_enters_secure_lock_mode(self):
with mock.patch.object(self.monitor, "_is_trusted_owner", return_value=True):
self.monitor._on_name_appeared(mock.Mock(), self.monitor.BUS_NAME, ":1.42")
self.app.eventManager.set_screen_lock_active.assert_called_once_with(
True,
schedule_focus_resync=True,
)
self.assertTrue(self.monitor.is_active())
def test_untrusted_name_owner_does_not_enter_secure_lock_mode(self):
with mock.patch.object(self.monitor, "_is_trusted_owner", return_value=False):
self.monitor._on_name_appeared(mock.Mock(), self.monitor.BUS_NAME, ":1.42")
self.app.eventManager.set_screen_lock_active.assert_not_called()
self.assertFalse(self.monitor.is_active())
def test_name_disappearance_leaves_secure_lock_mode(self):
with mock.patch.object(self.monitor, "_is_trusted_owner", return_value=True):
self.monitor._on_name_appeared(mock.Mock(), self.monitor.BUS_NAME, ":1.42")
self.monitor._on_name_vanished(mock.Mock(), self.monitor.BUS_NAME)
self.assertEqual(
self.app.eventManager.set_screen_lock_active.call_args_list,
[
mock.call(True, schedule_focus_resync=True),
mock.call(False, schedule_focus_resync=True),
],
)
self.assertFalse(self.monitor.is_active())
def test_start_watches_i38lock_bus_name(self):
connection = mock.Mock()
with (
mock.patch("cthulhu.screen_lock_monitor.Gio.bus_get_sync", return_value=connection),
mock.patch(
"cthulhu.screen_lock_monitor.Gio.bus_watch_name_on_connection",
return_value=23,
) as watch_name,
):
self.monitor.start()
watch_name.assert_called_once_with(
connection,
self.monitor.BUS_NAME,
mock.ANY,
self.monitor._on_name_appeared,
self.monitor._on_name_vanished,
)
self.assertTrue(self.monitor.is_running())
def test_stop_releases_secure_lock_without_focus_resync(self):
with mock.patch.object(self.monitor, "_is_trusted_owner", return_value=True):
self.monitor._on_name_appeared(mock.Mock(), self.monitor.BUS_NAME, ":1.42")
with mock.patch("cthulhu.screen_lock_monitor.Gio.bus_unwatch_name"):
self.monitor.stop()
self.assertEqual(
self.app.eventManager.set_screen_lock_active.call_args_list,
[
mock.call(True, schedule_focus_resync=True),
mock.call(False, schedule_focus_resync=False),
],
)
self.assertFalse(self.monitor.is_active())
def test_i38lock_process_owner_is_trusted(self):
connection = mock.Mock()
result = mock.Mock()
result.unpack.return_value = (1234,)
connection.call_sync.return_value = result
with mock.patch("cthulhu.screen_lock_monitor.os.readlink", return_value="/usr/bin/i38lock"):
self.assertTrue(self.monitor._is_trusted_owner(connection, ":1.42"))
def test_non_i38lock_process_owner_is_not_trusted(self):
connection = mock.Mock()
result = mock.Mock()
result.unpack.return_value = (1234,)
connection.call_sync.return_value = result
with mock.patch("cthulhu.screen_lock_monitor.os.readlink", return_value="/usr/bin/python"):
self.assertFalse(self.monitor._is_trusted_owner(connection, ":1.42"))
class SecureScreenLockStateTests(unittest.TestCase):
def setUp(self):
self.original_screen_lock_active = cthulhu_state.screenLockActive
cthulhu_state.screenLockActive = False
def tearDown(self):
cthulhu_state.screenLockActive = self.original_screen_lock_active
def test_entering_secure_lock_clears_context_and_pauses_input(self):
app = mock.Mock()
active_script = mock.Mock()
app.scriptManager.get_active_script.return_value = active_script
manager = event_manager.EventManager(app, asyncMode=True)
keyboard_manager = mock.Mock()
focus_manager = mock.Mock()
with (
mock.patch.object(input_event_manager, "get_manager", return_value=keyboard_manager),
mock.patch("cthulhu.event_manager.focus_manager.get_manager", return_value=focus_manager),
mock.patch("cthulhu.event_manager.braille.clear") as clear_braille,
):
manager.set_screen_lock_active(True)
self.assertTrue(cthulhu_state.screenLockActive)
active_script.presentationInterrupt.assert_called_once_with()
app.scriptManager.set_active_script.assert_called_once_with(
None,
"I38Lock secure screen lock active",
)
focus_manager.clear_state.assert_called_once_with(
"I38Lock secure screen lock active"
)
keyboard_manager.set_secure_input_active.assert_called_once_with(
True,
"I38Lock secure screen lock active",
)
clear_braille.assert_called_once_with()
def test_object_events_are_dropped_without_inspecting_locked_content(self):
app = mock.Mock()
manager = event_manager.EventManager(app, asyncMode=True)
cthulhu_state.screenLockActive = True
event = mock.Mock()
with (
mock.patch.object(manager, "_addToQueue") as add_to_queue,
mock.patch("cthulhu.event_manager.AXObject.get_application") as get_application,
):
manager._enqueue(event)
add_to_queue.assert_not_called()
get_application.assert_not_called()
def test_unlock_resumes_input_and_schedules_focus_resync(self):
app = mock.Mock()
manager = event_manager.EventManager(app, asyncMode=True)
keyboard_manager = mock.Mock()
focus_manager = mock.Mock()
cthulhu_state.screenLockActive = True
with (
mock.patch.object(input_event_manager, "get_manager", return_value=keyboard_manager),
mock.patch("cthulhu.event_manager.focus_manager.get_manager", return_value=focus_manager),
mock.patch("cthulhu.event_manager.GLib.idle_add") as idle_add,
):
manager.set_screen_lock_active(False)
self.assertFalse(cthulhu_state.screenLockActive)
keyboard_manager.set_secure_input_active.assert_called_once_with(
False,
"I38Lock secure screen lock released",
)
idle_add.assert_called_once_with(manager._sync_focus_on_startup)
def test_secure_input_blocks_key_echo_before_event_creation(self):
manager = input_event_manager.InputEventManager()
manager.set_secure_input_active(True, "test lock")
with mock.patch("cthulhu.input_event_manager.input_event.KeyboardEvent") as event_class:
consumed = manager.process_keyboard_event(
mock.Mock(),
True,
25,
112,
0,
"p",
)
self.assertFalse(consumed)
event_class.assert_not_called()
if __name__ == "__main__":
unittest.main()