975 lines
38 KiB
Python
975 lines
38 KiB
Python
# Unit tests for script_manager.py methods.
|
|
#
|
|
# Copyright 2025 Igalia, S.L.
|
|
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the
|
|
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
|
# Boston MA 02110-1301 USA.
|
|
|
|
# pylint: disable=wrong-import-position
|
|
# pylint: disable=import-outside-toplevel
|
|
# pylint: disable=too-many-public-methods
|
|
# pylint: disable=too-many-statements
|
|
# pylint: disable=protected-access
|
|
# pylint: disable=too-many-arguments
|
|
# pylint: disable=too-many-positional-arguments
|
|
# pylint: disable=too-many-locals
|
|
# pylint: disable=too-many-instance-attributes
|
|
# pylint: disable=too-many-lines
|
|
|
|
"""Unit tests for script_manager.py methods."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from unittest.mock import MagicMock
|
|
|
|
from .cthulhu_test_context import CthulhuTestContext
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestScriptManager:
|
|
"""Test ScriptManager class methods."""
|
|
|
|
def _setup_dependencies(self, test_context: CthulhuTestContext) -> dict[str, MagicMock]:
|
|
"""Returns dependencies for script_manager module testing."""
|
|
|
|
modules_to_clean = ["cthulhu.script_manager"]
|
|
for module_name in modules_to_clean:
|
|
if module_name in sys.modules:
|
|
del sys.modules[module_name]
|
|
|
|
additional_modules = [
|
|
"gi",
|
|
"gi.repository",
|
|
"gi.repository.Atspi",
|
|
"cthulhu.ax_utilities",
|
|
"cthulhu.command_manager",
|
|
"cthulhu.scripts",
|
|
"cthulhu.scripts.apps",
|
|
"cthulhu.scripts.default",
|
|
"cthulhu.scripts.sleepmode",
|
|
"cthulhu.scripts.toolkits",
|
|
"cthulhu.sleep_mode_manager",
|
|
"cthulhu.speech_manager",
|
|
]
|
|
essential_modules = test_context.setup_shared_dependencies(additional_modules)
|
|
|
|
if "cthulhu.script_manager" in essential_modules:
|
|
del essential_modules["cthulhu.script_manager"]
|
|
if "cthulhu.script_manager" in sys.modules:
|
|
del sys.modules["cthulhu.script_manager"]
|
|
|
|
gi_repository_mock = essential_modules["gi.repository"]
|
|
atspi_mock = essential_modules["gi.repository.Atspi"]
|
|
|
|
class UnionSupportingMock(Mock):
|
|
"""Mock that supports union operations for Atspi.Accessible."""
|
|
|
|
def __or__(self, other):
|
|
return UnionSupportingMock()
|
|
|
|
atspi_mock.Accessible = UnionSupportingMock()
|
|
gi_repository_mock.Atspi = atspi_mock
|
|
|
|
ax_object_mock = essential_modules["cthulhu.ax_object"]
|
|
ax_object_class_mock = test_context.Mock()
|
|
ax_object_class_mock.get_name = test_context.Mock(return_value="test-app")
|
|
ax_object_class_mock.get_attribute = test_context.Mock(return_value="GTK")
|
|
ax_object_mock.AXObject = ax_object_class_mock
|
|
|
|
ax_utilities_mock = essential_modules["cthulhu.ax_utilities"]
|
|
ax_utilities_class_mock = test_context.Mock()
|
|
ax_utilities_class_mock.is_terminal = test_context.Mock(return_value=False)
|
|
ax_utilities_class_mock.get_application_toolkit_name = test_context.Mock(return_value="gtk")
|
|
ax_utilities_class_mock.is_application_in_desktop = test_context.Mock(return_value=True)
|
|
ax_utilities_class_mock.is_frame = test_context.Mock(return_value=False)
|
|
ax_utilities_class_mock.is_status_bar = test_context.Mock(return_value=False)
|
|
ax_utilities_mock.AXUtilities = ax_utilities_class_mock
|
|
|
|
braille_mock = essential_modules["cthulhu.braille"]
|
|
braille_mock.check_braille_setting = test_context.Mock()
|
|
braille_mock.setup_key_ranges = test_context.Mock()
|
|
|
|
debug_mock = essential_modules["cthulhu.debug"]
|
|
debug_mock.print_message = test_context.Mock()
|
|
debug_mock.LEVEL_INFO = 800
|
|
|
|
speech_verbosity_mock = essential_modules["cthulhu.speech_manager"]
|
|
speech_manager_instance = test_context.Mock()
|
|
speech_manager_instance.check_speech_setting = test_context.Mock()
|
|
speech_verbosity_mock.get_manager = test_context.Mock(return_value=speech_manager_instance)
|
|
|
|
command_manager_mock = essential_modules["cthulhu.command_manager"]
|
|
command_manager_instance = test_context.Mock()
|
|
command_manager_instance.check_keyboard_settings = test_context.Mock()
|
|
command_manager_mock.get_manager = test_context.Mock(return_value=command_manager_instance)
|
|
|
|
scripts_mock = essential_modules["cthulhu.scripts"]
|
|
apps_mock = essential_modules["cthulhu.scripts.apps"]
|
|
toolkits_mock = essential_modules["cthulhu.scripts.toolkits"]
|
|
default_mock = essential_modules["cthulhu.scripts.default"]
|
|
sleepmode_mock = essential_modules["cthulhu.scripts.sleepmode"]
|
|
|
|
apps_mock.__all__ = ["evolution", "gnome-shell"]
|
|
toolkits_mock.__all__ = ["gtk", "Gecko", "Qt"]
|
|
|
|
class MockScript:
|
|
"""Mock script class for testing."""
|
|
|
|
def __init__(self, app=None):
|
|
self.app = app
|
|
self.register_event_listeners = test_context.Mock()
|
|
self.deregister_event_listeners = test_context.Mock()
|
|
self.activate = test_context.Mock()
|
|
self.deactivate = test_context.Mock()
|
|
|
|
def __or__(self, other):
|
|
return MockScript
|
|
|
|
default_script_constructor = test_context.Mock(return_value=MockScript())
|
|
default_script_constructor.__or__ = lambda self, other: MockScript()
|
|
sleepmode_script_constructor = test_context.Mock(return_value=MockScript())
|
|
sleepmode_script_constructor.__or__ = lambda self, other: MockScript()
|
|
|
|
default_mock.Script = default_script_constructor
|
|
sleepmode_mock.Script = sleepmode_script_constructor
|
|
|
|
scripts_mock.apps = apps_mock
|
|
scripts_mock.default = default_mock
|
|
scripts_mock.sleepmode = sleepmode_mock
|
|
scripts_mock.toolkits = toolkits_mock
|
|
|
|
essential_modules["speech_manager_instance"] = speech_manager_instance
|
|
|
|
default_script = test_context.Mock()
|
|
default_script.app = test_context.Mock()
|
|
default_script.register_event_listeners = test_context.Mock()
|
|
default_script.deregister_event_listeners = test_context.Mock()
|
|
default_script.activate = test_context.Mock()
|
|
default_script.deactivate = test_context.Mock()
|
|
|
|
sleepmode_script = test_context.Mock()
|
|
sleepmode_script.app = test_context.Mock()
|
|
sleepmode_script.register_event_listeners = test_context.Mock()
|
|
sleepmode_script.deregister_event_listeners = test_context.Mock()
|
|
sleepmode_script.activate = test_context.Mock()
|
|
sleepmode_script.deactivate = test_context.Mock()
|
|
|
|
sleep_mode_manager_instance = test_context.Mock()
|
|
sleep_mode_manager_instance.is_active_for_app = test_context.Mock(return_value=False)
|
|
|
|
# Set up the module-level get_manager mock
|
|
sleep_mode_manager_mock = essential_modules["cthulhu.sleep_mode_manager"]
|
|
sleep_mode_manager_mock.get_manager = test_context.Mock(
|
|
return_value=sleep_mode_manager_instance,
|
|
)
|
|
|
|
essential_modules["default_script"] = default_script
|
|
essential_modules["sleepmode_script"] = sleepmode_script
|
|
essential_modules["sleep_mode_manager"] = sleep_mode_manager_instance
|
|
|
|
default_module = test_context.Mock()
|
|
default_module.Script = test_context.Mock(return_value=test_context.Mock())
|
|
|
|
sleepmode_module = test_context.Mock()
|
|
sleepmode_module.Script = test_context.Mock(return_value=test_context.Mock())
|
|
|
|
deps_speech_manager = test_context.Mock()
|
|
deps_speech_manager_instance = test_context.Mock()
|
|
deps_speech_manager_instance.check_speech_setting = test_context.Mock()
|
|
deps_speech_manager.get_manager = test_context.Mock(
|
|
return_value=deps_speech_manager_instance,
|
|
)
|
|
|
|
braille_module = test_context.Mock()
|
|
braille_module.check_braille_setting = test_context.Mock()
|
|
braille_module.setup_key_ranges = test_context.Mock()
|
|
|
|
essential_modules["default_module"] = default_module
|
|
essential_modules["sleepmode_module"] = sleepmode_module
|
|
essential_modules["deps_speech_manager"] = deps_speech_manager
|
|
essential_modules["braille_module"] = braille_module
|
|
|
|
return essential_modules
|
|
|
|
def test_init(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test ScriptManager.__init__."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
assert not manager.app_scripts
|
|
assert not manager.toolkit_scripts
|
|
assert not manager.custom_scripts
|
|
assert not manager._sleep_mode_scripts
|
|
assert manager._default_script is None
|
|
assert manager._active_script is None
|
|
assert manager._active is False
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "inactive_to_active",
|
|
"initially_active": False,
|
|
"expects_script_creation": True,
|
|
},
|
|
{"id": "already_active", "initially_active": True, "expects_script_creation": False},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_activate(self, test_context: CthulhuTestContext, case: dict) -> None:
|
|
"""Test ScriptManager.activate with different initial states."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
|
|
if case["expects_script_creation"]:
|
|
mock_default_script = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.default.Script", new=mock_default_script)
|
|
mock_default_script.return_value = default_script
|
|
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
manager._active = case["initially_active"]
|
|
manager.activate()
|
|
|
|
assert manager._active is True
|
|
|
|
if case["expects_script_creation"]:
|
|
assert manager._default_script is not None
|
|
assert manager._active_script is not None
|
|
default_script.register_event_listeners.assert_called_once()
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "active_to_inactive", "initially_active": True, "expects_cleanup": True},
|
|
{"id": "already_inactive", "initially_active": False, "expects_cleanup": False},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_deactivate(self, test_context: CthulhuTestContext, case: dict) -> None:
|
|
"""Test ScriptManager.deactivate with different initial states."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
manager._active = case["initially_active"]
|
|
|
|
if case["expects_cleanup"]:
|
|
mock_script = test_context.Mock()
|
|
mock_script.deregister_event_listeners = test_context.Mock()
|
|
manager._default_script = mock_script
|
|
manager.app_scripts = {"test": "script"}
|
|
manager.toolkit_scripts = {"test": "script"}
|
|
manager.custom_scripts = {"test": "script"}
|
|
|
|
manager.deactivate()
|
|
assert manager._active is False
|
|
|
|
if case["expects_cleanup"]:
|
|
assert manager._default_script is None
|
|
assert manager._active_script is None
|
|
assert not manager.app_scripts
|
|
assert not manager.toolkit_scripts
|
|
assert not manager.custom_scripts
|
|
mock_script.deregister_event_listeners.assert_called_once()
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "app_in_apps_list",
|
|
"app_name": "evolution",
|
|
"expected_result": "evolution",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "toolkit_in_toolkits_list",
|
|
"app_name": "gtk",
|
|
"expected_result": "gtk",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "mapped_app_name",
|
|
"app_name": "mate-notification-daemon",
|
|
"expected_result": "notification-daemon",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "steam_alias",
|
|
"app_name": "Steam Web Helper",
|
|
"expected_result": "steamwebhelper",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "python_extension",
|
|
"app_name": "test-app.py",
|
|
"expected_result": "test-app",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "bin_extension",
|
|
"app_name": "test-app.bin",
|
|
"expected_result": "test-app",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "reverse_domain",
|
|
"app_name": "org.gnome.TestApp",
|
|
"expected_result": "TestApp",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "reverse_domain_com",
|
|
"app_name": "com.example.TestApp",
|
|
"expected_result": "TestApp",
|
|
"use_null_app": False,
|
|
},
|
|
{
|
|
"id": "unknown_app",
|
|
"app_name": "unknown-app",
|
|
"expected_result": "unknown-app",
|
|
"use_null_app": False,
|
|
},
|
|
{"id": "null_app", "app_name": None, "expected_result": None, "use_null_app": True},
|
|
{"id": "nameless_app", "app_name": "", "expected_result": None, "use_null_app": False},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_get_module_name_scenarios(self, test_context, case: dict) -> None:
|
|
"""Test ScriptManager.get_module_name with various scenarios."""
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
|
|
if case["use_null_app"]:
|
|
result = manager.get_module_name(None)
|
|
else:
|
|
mock_app = test_context.Mock()
|
|
mock_ax_object = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.AXObject", new=mock_ax_object)
|
|
mock_ax_object.get_name.return_value = case["app_name"]
|
|
result = manager.get_module_name(mock_app)
|
|
|
|
assert result == case["expected_result"]
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "gtk_toolkit", "toolkit_attribute": "GTK", "expected_result": "gtk"},
|
|
{"id": "gail_mapped_to_gtk", "toolkit_attribute": "GAIL", "expected_result": "gtk"},
|
|
{
|
|
"id": "webkitgtk_mapped_to_local_dir",
|
|
"toolkit_attribute": "WebKitGTK",
|
|
"expected_result": "WebKitGtk",
|
|
},
|
|
{"id": "qt_toolkit", "toolkit_attribute": "Qt", "expected_result": "Qt"},
|
|
{"id": "empty_toolkit", "toolkit_attribute": "", "expected_result": ""},
|
|
{"id": "none_toolkit", "toolkit_attribute": None, "expected_result": None},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_toolkit_for_object(self, test_context, case: dict) -> None:
|
|
"""Test ScriptManager._toolkit_for_object."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_obj = test_context.Mock()
|
|
|
|
mock_ax_object = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.AXObject", new=mock_ax_object)
|
|
mock_ax_object.get_attribute.return_value = case["toolkit_attribute"]
|
|
result = manager._toolkit_for_object(mock_obj)
|
|
assert result == case["expected_result"]
|
|
mock_ax_object.get_attribute.assert_called_once_with(mock_obj, "toolkit")
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "terminal_role", "is_terminal": True, "expected_result": "terminal"},
|
|
{"id": "non_terminal_role", "is_terminal": False, "expected_result": ""},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_script_for_role(self, test_context, case: dict) -> None:
|
|
"""Test ScriptManager._script_for_role."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_obj = test_context.Mock()
|
|
|
|
mock_ax_utilities = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.AXUtilities", new=mock_ax_utilities)
|
|
mock_ax_utilities.is_terminal.return_value = case["is_terminal"]
|
|
result = manager._script_for_role(mock_obj)
|
|
assert result == case["expected_result"]
|
|
mock_ax_utilities.is_terminal.assert_called_once_with(mock_obj)
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "null_app_empty_name",
|
|
"app": None,
|
|
"name": "",
|
|
"has_module": False,
|
|
"has_get_script": False,
|
|
"has_script_class": False,
|
|
"should_succeed": False,
|
|
},
|
|
{
|
|
"id": "empty_name",
|
|
"app": "app",
|
|
"name": "",
|
|
"has_module": False,
|
|
"has_get_script": False,
|
|
"has_script_class": False,
|
|
"should_succeed": False,
|
|
},
|
|
{
|
|
"id": "no_module",
|
|
"app": "app",
|
|
"name": "test",
|
|
"has_module": False,
|
|
"has_get_script": False,
|
|
"has_script_class": False,
|
|
"should_succeed": False,
|
|
},
|
|
{
|
|
"id": "has_get_script",
|
|
"app": "app",
|
|
"name": "test",
|
|
"has_module": True,
|
|
"has_get_script": True,
|
|
"has_script_class": False,
|
|
"should_succeed": True,
|
|
},
|
|
{
|
|
"id": "has_script_class",
|
|
"app": "app",
|
|
"name": "test",
|
|
"has_module": True,
|
|
"has_get_script": False,
|
|
"has_script_class": True,
|
|
"should_succeed": True,
|
|
},
|
|
{
|
|
"id": "no_script_creation",
|
|
"app": "app",
|
|
"name": "test",
|
|
"has_module": True,
|
|
"has_get_script": False,
|
|
"has_script_class": False,
|
|
"should_succeed": False,
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_new_named_script(self, test_context, case: dict) -> None:
|
|
"""Test ScriptManager._new_named_script."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock() if case["app"] else None
|
|
mock_script = test_context.Mock()
|
|
mock_import = test_context.Mock()
|
|
test_context.patch("importlib.import_module", side_effect=mock_import)
|
|
if case["has_module"]:
|
|
mock_module = type("MockModule", (), {})()
|
|
if case["has_get_script"]:
|
|
mock_module.get_script = test_context.Mock(return_value=mock_script)
|
|
elif case["has_script_class"]:
|
|
mock_module.Script = test_context.Mock(return_value=mock_script)
|
|
mock_import.return_value = mock_module
|
|
else:
|
|
mock_import.side_effect = ImportError("Module not found")
|
|
result = manager._new_named_script(mock_app, case["name"])
|
|
if case["should_succeed"]:
|
|
assert result == mock_script
|
|
else:
|
|
assert result is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "os_error_returns_none",
|
|
"exception_type": "OSError",
|
|
},
|
|
{
|
|
"id": "script_creation_error",
|
|
"exception_type": "AttributeError",
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_new_named_script_error_handling(
|
|
self,
|
|
test_context: CthulhuTestContext,
|
|
case: dict,
|
|
) -> None:
|
|
"""Test ScriptManager._new_named_script handles various errors."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
mock_import = test_context.Mock()
|
|
test_context.patch("importlib.import_module", side_effect=mock_import)
|
|
|
|
if case["exception_type"] == "OSError":
|
|
mock_import.side_effect = OSError("Permission denied")
|
|
result = manager._new_named_script(mock_app, "test")
|
|
assert result is None
|
|
else:
|
|
mock_module = type("MockModule", (), {})()
|
|
mock_module.Script = test_context.Mock(
|
|
side_effect=AttributeError("Script class not found"),
|
|
)
|
|
mock_import.return_value = mock_module
|
|
result = manager._new_named_script(mock_app, "test")
|
|
assert result is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "no_module_name", "module_name": None, "expected_result_type": "default_script"},
|
|
{
|
|
"id": "with_module_name",
|
|
"module_name": "test_script",
|
|
"expected_result_type": "named_script",
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_create_script(self, test_context: CthulhuTestContext, case: dict) -> None:
|
|
"""Test ScriptManager._create_script with different module name scenarios."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
mock_obj = test_context.Mock()
|
|
mock_script = test_context.Mock()
|
|
|
|
test_context.patch_object(manager, "get_module_name", return_value=case["module_name"])
|
|
test_context.patch_object(manager, "_toolkit_for_object", return_value=None)
|
|
test_context.patch_object(manager, "get_default_script", return_value=default_script)
|
|
|
|
test_context.patch(
|
|
"cthulhu.script_manager.AXUtilities.get_application_toolkit_name",
|
|
return_value=None,
|
|
)
|
|
|
|
if case["expected_result_type"] == "default_script":
|
|
mock_new_named_script = test_context.Mock(return_value=None)
|
|
else:
|
|
mock_new_named_script = test_context.Mock(return_value=mock_script)
|
|
test_context.patch_object(manager, "_new_named_script", side_effect=mock_new_named_script)
|
|
|
|
result = manager._create_script(mock_app, mock_obj)
|
|
|
|
if case["expected_result_type"] == "default_script":
|
|
assert result == default_script
|
|
else:
|
|
assert result == mock_script
|
|
mock_new_named_script.assert_called_once_with(mock_app, case["module_name"])
|
|
|
|
def test_get_default_script(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test ScriptManager.get_default_script."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
mock_default_script = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.default.Script", new=mock_default_script)
|
|
mock_default_script.return_value = default_script
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
|
|
result = manager.get_default_script(mock_app)
|
|
assert result is not None
|
|
mock_default_script.assert_called_with(mock_app)
|
|
|
|
manager._default_script = default_script
|
|
result = manager.get_default_script(None)
|
|
assert result == default_script
|
|
|
|
manager._default_script = None
|
|
result = manager.get_default_script(None)
|
|
assert result is not None
|
|
assert manager._default_script is not None
|
|
|
|
def test_get_or_create_sleep_mode_script(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test ScriptManager.get_or_create_sleep_mode_script."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
sleepmode_script = essential_modules["sleepmode_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
mock_sleepmode_script = test_context.Mock()
|
|
test_context.patch("cthulhu.script_manager.sleepmode.Script", new=mock_sleepmode_script)
|
|
mock_sleepmode_script.return_value = sleepmode_script
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
|
|
result = manager.get_or_create_sleep_mode_script(mock_app)
|
|
assert result is not None
|
|
assert result == sleepmode_script
|
|
assert manager._sleep_mode_scripts[mock_app] == result
|
|
|
|
result2 = manager.get_or_create_sleep_mode_script(mock_app)
|
|
assert result2 == result
|
|
assert len(manager._sleep_mode_scripts) == 1
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "null_app_and_obj",
|
|
"app": None,
|
|
"obj": None,
|
|
"sleep_mode_active": False,
|
|
"has_custom_script": False,
|
|
"has_toolkit_script": False,
|
|
"expected_script_type": "default",
|
|
},
|
|
{
|
|
"id": "sleep_mode_active",
|
|
"app": "app",
|
|
"obj": "obj",
|
|
"sleep_mode_active": True,
|
|
"has_custom_script": False,
|
|
"has_toolkit_script": False,
|
|
"expected_script_type": "sleep",
|
|
},
|
|
{
|
|
"id": "custom_script",
|
|
"app": "app",
|
|
"obj": "obj",
|
|
"sleep_mode_active": False,
|
|
"has_custom_script": True,
|
|
"has_toolkit_script": False,
|
|
"expected_script_type": "custom",
|
|
},
|
|
{
|
|
"id": "toolkit_script",
|
|
"app": "app",
|
|
"obj": "obj",
|
|
"sleep_mode_active": False,
|
|
"has_custom_script": False,
|
|
"has_toolkit_script": True,
|
|
"expected_script_type": "toolkit",
|
|
},
|
|
{
|
|
"id": "app_script",
|
|
"app": "app",
|
|
"obj": "obj",
|
|
"sleep_mode_active": False,
|
|
"has_custom_script": False,
|
|
"has_toolkit_script": False,
|
|
"expected_script_type": "app",
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_get_script(self, test_context, case: dict) -> None:
|
|
"""Test ScriptManager.get_script."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
sleepmode_script = essential_modules["sleepmode_script"]
|
|
sleep_mode_manager = essential_modules["sleep_mode_manager"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock() if case["app"] else None
|
|
mock_obj = test_context.Mock() if case["obj"] else None
|
|
mock_sleep_script = sleepmode_script
|
|
mock_custom_script = test_context.Mock()
|
|
|
|
class ToolkitScript:
|
|
"""Mock toolkit script class."""
|
|
|
|
class AppScript:
|
|
"""Mock app script class."""
|
|
|
|
mock_toolkit_script = test_context.Mock(spec=ToolkitScript)
|
|
mock_app_script = test_context.Mock(spec=AppScript)
|
|
sleep_mode_manager.is_active_for_app = test_context.Mock(
|
|
return_value=case["sleep_mode_active"],
|
|
)
|
|
test_context.patch_object(
|
|
manager,
|
|
"get_or_create_sleep_mode_script",
|
|
return_value=mock_sleep_script,
|
|
)
|
|
test_context.patch_object(
|
|
manager,
|
|
"_script_for_role",
|
|
return_value="terminal" if case["has_custom_script"] else "",
|
|
)
|
|
test_context.patch_object(
|
|
manager,
|
|
"_toolkit_for_object",
|
|
return_value="gtk" if case["has_toolkit_script"] else None,
|
|
)
|
|
|
|
def create_script_side_effect(_app, obj):
|
|
if obj and case["has_toolkit_script"]:
|
|
return mock_toolkit_script
|
|
return mock_app_script
|
|
|
|
test_context.patch_object(manager, "_create_script", side_effect=create_script_side_effect)
|
|
if case["has_custom_script"]:
|
|
test_context.patch_object(manager, "_new_named_script", return_value=mock_custom_script)
|
|
result = manager.get_script(mock_app, mock_obj)
|
|
if case["expected_script_type"] == "default":
|
|
assert result is not None
|
|
assert hasattr(result, "register_event_listeners")
|
|
elif case["expected_script_type"] == "sleep":
|
|
assert result == mock_sleep_script
|
|
elif case["expected_script_type"] == "custom":
|
|
assert result == mock_custom_script
|
|
elif case["expected_script_type"] == "toolkit":
|
|
assert result == mock_toolkit_script
|
|
elif case["expected_script_type"] == "app":
|
|
assert result == mock_app_script
|
|
|
|
def test_get_script_exception_handling(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test ScriptManager.get_script handles exceptions."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
mock_obj = test_context.Mock()
|
|
test_context.patch_object(manager, "_script_for_role", return_value="")
|
|
test_context.patch_object(manager, "_toolkit_for_object", return_value=None)
|
|
|
|
def create_script_side_effect(app, obj):
|
|
raise KeyError("Script creation failed")
|
|
|
|
test_context.patch_object(manager, "_create_script", side_effect=create_script_side_effect)
|
|
result = manager.get_script(mock_app, mock_obj)
|
|
assert result is not None
|
|
assert hasattr(result, "register_event_listeners")
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "no_active_script", "has_active_script": False, "test_type": "script"},
|
|
{"id": "has_active_script", "has_active_script": True, "test_type": "script"},
|
|
{"id": "no_active_script_app", "has_active_script": False, "test_type": "app"},
|
|
{"id": "has_active_script_app", "has_active_script": True, "test_type": "app"},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_get_active_script_and_app(self, test_context: CthulhuTestContext, case: dict) -> None:
|
|
"""Test ScriptManager.get_active_script and get_active_script_app."""
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
mock_app = test_context.Mock()
|
|
|
|
if case["has_active_script"]:
|
|
default_script.app = mock_app
|
|
manager._active_script = default_script
|
|
|
|
if case["test_type"] == "script":
|
|
result = manager.get_active_script()
|
|
expected = default_script if case["has_active_script"] else None
|
|
else:
|
|
result = manager.get_active_script_app()
|
|
expected = mock_app if case["has_active_script"] else None
|
|
|
|
assert result == expected
|
|
|
|
def test_set_active_script(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test ScriptManager.set_active_script."""
|
|
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
mock_get_speech_manager = test_context.Mock()
|
|
test_context.patch(
|
|
"cthulhu.script_manager.speech_manager.get_manager",
|
|
new=mock_get_speech_manager,
|
|
)
|
|
mock_speech_manager_instance = test_context.Mock()
|
|
mock_get_speech_manager.return_value = mock_speech_manager_instance
|
|
|
|
manager = ScriptManager()
|
|
old_script = test_context.Mock()
|
|
old_script.app = test_context.Mock()
|
|
old_script.deactivate = test_context.Mock()
|
|
new_script = default_script
|
|
new_script.app = test_context.Mock()
|
|
new_script.activate = test_context.Mock()
|
|
|
|
manager._active_script = old_script
|
|
manager.set_active_script(new_script, "test reason")
|
|
old_script.deactivate.assert_called_once()
|
|
new_script.activate.assert_called_once()
|
|
assert manager._active_script == new_script
|
|
|
|
mock_speech_manager_instance.check_speech_setting.assert_called_once()
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{
|
|
"id": "same_script_no_change",
|
|
"new_script_type": "same",
|
|
"expects_deactivate": False,
|
|
"expects_activate": False,
|
|
},
|
|
{
|
|
"id": "set_to_none",
|
|
"new_script_type": "none",
|
|
"expects_deactivate": True,
|
|
"expects_activate": False,
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_set_active_script_special_cases(
|
|
self,
|
|
test_context: CthulhuTestContext,
|
|
case: dict,
|
|
) -> None:
|
|
"""Test ScriptManager.set_active_script special cases."""
|
|
essential_modules = self._setup_dependencies(test_context)
|
|
default_script = essential_modules["default_script"]
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
manager = ScriptManager()
|
|
old_script = test_context.Mock()
|
|
old_script.deactivate = test_context.Mock()
|
|
manager._active_script = old_script
|
|
|
|
if case["new_script_type"] == "same":
|
|
manager._active_script = default_script
|
|
manager.set_active_script(default_script, "same script")
|
|
if case["expects_deactivate"]:
|
|
default_script.deactivate.assert_called_once()
|
|
else:
|
|
default_script.deactivate.assert_not_called()
|
|
default_script.activate.assert_not_called()
|
|
else:
|
|
manager.set_active_script(None)
|
|
if case["expects_deactivate"]:
|
|
old_script.deactivate.assert_called_once()
|
|
assert manager._active_script is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"case",
|
|
[
|
|
{"id": "mixed_apps_normal", "has_app_in_desktop": True, "has_key_error": False},
|
|
{
|
|
"id": "no_desktop_apps_with_key_error",
|
|
"has_app_in_desktop": False,
|
|
"has_key_error": True,
|
|
},
|
|
],
|
|
ids=lambda case: case["id"],
|
|
)
|
|
def test_reclaim_scripts(self, test_context: CthulhuTestContext, case: dict) -> None:
|
|
"""Test ScriptManager.reclaim_scripts with various scenarios."""
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu.script_manager import ScriptManager
|
|
|
|
desktop_patch = "cthulhu.script_manager.AXUtilities.is_application_in_desktop"
|
|
mock_is_in_desktop = test_context.Mock()
|
|
test_context.patch(desktop_patch, new=mock_is_in_desktop)
|
|
manager = ScriptManager()
|
|
app_not_in_desktop = test_context.Mock()
|
|
|
|
if case["has_app_in_desktop"]:
|
|
app_in_desktop = test_context.Mock()
|
|
manager.app_scripts = {
|
|
app_in_desktop: test_context.Mock(),
|
|
app_not_in_desktop: test_context.Mock(),
|
|
}
|
|
manager.toolkit_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
manager.custom_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
manager._sleep_mode_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
|
|
def mock_is_in_desktop_func(app):
|
|
return app == app_in_desktop
|
|
|
|
mock_is_in_desktop.side_effect = mock_is_in_desktop_func
|
|
manager.reclaim_scripts()
|
|
assert app_in_desktop in manager.app_scripts
|
|
assert app_not_in_desktop not in manager.app_scripts
|
|
assert app_not_in_desktop not in manager.toolkit_scripts
|
|
assert app_not_in_desktop not in manager.custom_scripts
|
|
assert app_not_in_desktop not in manager._sleep_mode_scripts
|
|
else:
|
|
manager.app_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
if case["has_key_error"]:
|
|
manager.toolkit_scripts = {}
|
|
manager.custom_scripts = {}
|
|
manager._sleep_mode_scripts = {}
|
|
else:
|
|
manager.toolkit_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
manager.custom_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
manager._sleep_mode_scripts = {app_not_in_desktop: test_context.Mock()}
|
|
|
|
mock_is_in_desktop.return_value = False
|
|
manager.reclaim_scripts()
|
|
assert app_not_in_desktop not in manager.app_scripts
|
|
if not case["has_key_error"]:
|
|
assert app_not_in_desktop not in manager.toolkit_scripts
|
|
assert app_not_in_desktop not in manager.custom_scripts
|
|
assert app_not_in_desktop not in manager._sleep_mode_scripts
|
|
|
|
def test_get_manager(self, test_context: CthulhuTestContext) -> None:
|
|
"""Test script_manager.get_manager."""
|
|
|
|
self._setup_dependencies(test_context)
|
|
from cthulhu import script_manager
|
|
|
|
manager1 = script_manager.get_manager()
|
|
manager2 = script_manager.get_manager()
|
|
assert manager1 is manager2
|
|
assert isinstance(manager1, script_manager.ScriptManager)
|