Prepare test harness for Orca 50 rebase
This commit is contained in:
+10
-5
@@ -6,15 +6,15 @@ project('cthulhu',
|
|||||||
python = import('python')
|
python = import('python')
|
||||||
i18n = import('i18n')
|
i18n = import('i18n')
|
||||||
|
|
||||||
python_minimum_version = '3.9'
|
python_minimum_version = '3.10'
|
||||||
python3 = python.find_installation('python3', required: true)
|
python3 = python.find_installation('python3', required: true)
|
||||||
if not python3.language_version().version_compare(f'>= @python_minimum_version@')
|
if not python3.language_version().version_compare(f'>= @python_minimum_version@')
|
||||||
error(f'Python @python_minimum_version@ or newer is required.')
|
error(f'Python @python_minimum_version@ or newer is required.')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Hard dependencies (checked via pkg-config)
|
# Hard dependencies (checked via pkg-config)
|
||||||
dependency('atspi-2', version: '>= 2.52.0')
|
dependency('atspi-2', version: '>= 2.56.0')
|
||||||
dependency('atk-bridge-2.0', version: '>= 2.26.0')
|
dependency('atk-bridge-2.0', version: '>= 2.56.0')
|
||||||
dependency('pygobject-3.0', version: '>= 3.18')
|
dependency('pygobject-3.0', version: '>= 3.18')
|
||||||
|
|
||||||
# Hard Python module dependencies
|
# Hard Python module dependencies
|
||||||
@@ -33,14 +33,19 @@ if not pluggy_result.found()
|
|||||||
error('pluggy module is required')
|
error('pluggy module is required')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
dasbus_result = python.find_installation('python3', modules:['dasbus'], required: false)
|
||||||
|
if not dasbus_result.found()
|
||||||
|
error('dasbus is required for D-Bus remote controller interface')
|
||||||
|
endif
|
||||||
|
|
||||||
# End users might not have the Gtk development libraries installed, making pkg-config fail.
|
# End users might not have the Gtk development libraries installed, making pkg-config fail.
|
||||||
# Therefore, check this dependency via python.
|
# Therefore, check this dependency via python.
|
||||||
gtk_major_version = '3'
|
gtk_major_version = '3'
|
||||||
gtk_minor_version = '0'
|
gtk_minor_version = '24'
|
||||||
gtk_command = ' '.join([
|
gtk_command = ' '.join([
|
||||||
'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk;',
|
'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk;',
|
||||||
'print(f"{Gtk.get_major_version()}.{Gtk.get_minor_version()}.{Gtk.get_micro_version()}");',
|
'print(f"{Gtk.get_major_version()}.{Gtk.get_minor_version()}.{Gtk.get_micro_version()}");',
|
||||||
f'failed = Gtk.get_major_version() != @gtk_major_version@;',
|
f'failed = Gtk.get_major_version() != @gtk_major_version@ or Gtk.get_minor_version() < @gtk_minor_version@;',
|
||||||
'exit(failed)'
|
'exit(failed)'
|
||||||
])
|
])
|
||||||
gtk_test = run_command(python3, '-c', gtk_command, check: false)
|
gtk_test = run_command(python3, '-c', gtk_command, check: false)
|
||||||
|
|||||||
+2
-1
@@ -7,12 +7,13 @@ name = "cthulhu"
|
|||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
description = "Fork of the Orca screen reader"
|
description = "Fork of the Orca screen reader"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.10"
|
||||||
license = { text = "LGPL-2.1-or-later" }
|
license = { text = "LGPL-2.1-or-later" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pygobject>=3.18",
|
"pygobject>=3.18",
|
||||||
"pluggy",
|
"pluggy",
|
||||||
"tomlkit",
|
"tomlkit",
|
||||||
|
"dasbus",
|
||||||
"brlapi; extra == 'braille'",
|
"brlapi; extra == 'braille'",
|
||||||
"python-speechd; extra == 'speech'",
|
"python-speechd; extra == 'speech'",
|
||||||
"piper-tts; extra == 'piper'",
|
"piper-tts; extra == 'piper'",
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import importlib.util
|
import importlib.util
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
os.environ["GSETTINGS_BACKEND"] = "memory"
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
SRC_ROOT = REPO_ROOT / "src"
|
SRC_ROOT = REPO_ROOT / "src"
|
||||||
@@ -63,3 +67,27 @@ for generated_module in ("cthulhu_i18n", "cthulhu_platform"):
|
|||||||
loaded_module = _load_generated_module(generated_module)
|
loaded_module = _load_generated_module(generated_module)
|
||||||
sys.modules[f"cthulhu.{generated_module}"] = loaded_module
|
sys.modules[f"cthulhu.{generated_module}"] = loaded_module
|
||||||
setattr(cthulhu, generated_module, loaded_module)
|
setattr(cthulhu, generated_module, loaded_module)
|
||||||
|
|
||||||
|
|
||||||
|
from cthulhu_test_fixtures import test_context # noqa: E402,F401
|
||||||
|
|
||||||
|
|
||||||
|
def clean_all_cthulhu_modules() -> None:
|
||||||
|
modules_to_remove = [
|
||||||
|
module_name
|
||||||
|
for module_name in sys.modules
|
||||||
|
if module_name.startswith("cthulhu.")
|
||||||
|
and module_name not in {"cthulhu.cthulhu_i18n", "cthulhu.cthulhu_platform"}
|
||||||
|
]
|
||||||
|
for module_name in modules_to_remove:
|
||||||
|
sys.modules.pop(module_name, None)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config: pytest.Config) -> None:
|
||||||
|
config.addinivalue_line("markers", "unit: marks tests as unit tests")
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_runtest_setup(item: pytest.Item) -> None:
|
||||||
|
clean_all_cthulhu_modules()
|
||||||
|
if str(SRC_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(SRC_ROOT))
|
||||||
|
|||||||
@@ -0,0 +1,306 @@
|
|||||||
|
# Orca Test Context - Test Isolation Framework
|
||||||
|
#
|
||||||
|
# Copyright 2025 Igalia, S.L.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Test isolation framework for Cthulhu screen reader tests."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
|
gi.require_version("Atspi", "2.0")
|
||||||
|
gi.require_version("Gio", "2.0")
|
||||||
|
from gi.repository import Atspi, Gio, GLib # pylint: disable=wrong-import-position
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
|
||||||
|
class CthulhuTestContext:
|
||||||
|
"""Test isolation framework for Cthulhu tests."""
|
||||||
|
|
||||||
|
def __init__(self, mocker: MockerFixture, monkeypatch: MonkeyPatch):
|
||||||
|
"""Initialize the test context."""
|
||||||
|
|
||||||
|
self.mocker: MockerFixture = mocker
|
||||||
|
self.monkeypatch: MonkeyPatch = monkeypatch
|
||||||
|
self.patches: dict[str, Any] = {}
|
||||||
|
self.mocks: dict[str, MagicMock] = {}
|
||||||
|
|
||||||
|
def patch(self, target: str, **kwargs) -> MagicMock:
|
||||||
|
"""Convenience method for creating patches."""
|
||||||
|
|
||||||
|
return self.mocker.patch(target, **kwargs)
|
||||||
|
|
||||||
|
def patch_object(self, target: object, attribute: str, **kwargs) -> MagicMock:
|
||||||
|
"""Convenience method for patching object attributes."""
|
||||||
|
|
||||||
|
return self.mocker.patch.object(target, attribute, **kwargs)
|
||||||
|
|
||||||
|
def Mock(self, **kwargs) -> MagicMock: # pylint: disable=invalid-name
|
||||||
|
"""Convenience method for creating Mock objects."""
|
||||||
|
|
||||||
|
return self.mocker.Mock(**kwargs)
|
||||||
|
|
||||||
|
def patch_env(
|
||||||
|
self,
|
||||||
|
env_vars: dict[str, str],
|
||||||
|
remove_vars: list[str] | None = None,
|
||||||
|
) -> MagicMock | None:
|
||||||
|
"""Convenience method for patching environment variables."""
|
||||||
|
|
||||||
|
if remove_vars:
|
||||||
|
for var in remove_vars:
|
||||||
|
if var in os.environ:
|
||||||
|
del os.environ[var]
|
||||||
|
|
||||||
|
if env_vars:
|
||||||
|
return self.mocker.patch.dict(os.environ, env_vars)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def patch_module(self, module_name: str, mock_module: Any) -> MagicMock:
|
||||||
|
"""Convenience method for patching sys.modules entries."""
|
||||||
|
|
||||||
|
return self.mocker.patch.dict(sys.modules, {module_name: mock_module})
|
||||||
|
|
||||||
|
def patch_modules(self, modules: dict[str, Any]) -> MagicMock:
|
||||||
|
"""Convenience method for patching multiple sys.modules entries."""
|
||||||
|
|
||||||
|
return self.mocker.patch.dict(sys.modules, modules)
|
||||||
|
|
||||||
|
def _setup_required_imports(self) -> None:
|
||||||
|
"""Sets up commonly required module imports that real modules need."""
|
||||||
|
|
||||||
|
required_modules = [
|
||||||
|
"cthulhu.cthulhu_i18n",
|
||||||
|
"cthulhu.cmdnames",
|
||||||
|
"cthulhu.input_event",
|
||||||
|
"cthulhu.keybindings",
|
||||||
|
"cthulhu.messages",
|
||||||
|
"cthulhu.text_attribute_names",
|
||||||
|
]
|
||||||
|
|
||||||
|
for module_name in required_modules:
|
||||||
|
if module_name not in self.mocks:
|
||||||
|
mock_module = self.mocker.patch(module_name, create=True)
|
||||||
|
if module_name == "cthulhu.cthulhu_i18n":
|
||||||
|
mock_module._ = lambda x: x
|
||||||
|
self.mocks[module_name] = mock_module
|
||||||
|
|
||||||
|
def _setup_essential_modules(self, module_names: list[str]) -> dict[str, MagicMock]:
|
||||||
|
"""Returns dictionary mapping module names to mock objects."""
|
||||||
|
|
||||||
|
essential_modules = {}
|
||||||
|
for module_name in module_names:
|
||||||
|
mock_module = self.mocker.Mock()
|
||||||
|
self.patch_module(module_name, mock_module)
|
||||||
|
essential_modules[module_name] = mock_module
|
||||||
|
return essential_modules
|
||||||
|
|
||||||
|
def setup_shared_dependencies(
|
||||||
|
self,
|
||||||
|
additional_modules: list[str] | None = None,
|
||||||
|
) -> dict[str, MagicMock]:
|
||||||
|
"""Returns common/shared dependencies used across most Orca test modules."""
|
||||||
|
|
||||||
|
core_modules = [
|
||||||
|
"cthulhu.debug",
|
||||||
|
"cthulhu.messages",
|
||||||
|
"cthulhu.input_event",
|
||||||
|
"cthulhu.keybindings",
|
||||||
|
"cthulhu.cmdnames",
|
||||||
|
"cthulhu.ax_object",
|
||||||
|
"cthulhu.dbus_service",
|
||||||
|
"cthulhu.script_manager",
|
||||||
|
"cthulhu.cthulhu_i18n",
|
||||||
|
"cthulhu.guilabels",
|
||||||
|
"cthulhu.text_attribute_names",
|
||||||
|
"cthulhu.focus_manager",
|
||||||
|
"cthulhu.braille",
|
||||||
|
"cthulhu.cthulhu_platform",
|
||||||
|
]
|
||||||
|
|
||||||
|
if additional_modules:
|
||||||
|
core_modules.extend(additional_modules)
|
||||||
|
|
||||||
|
essential_modules = self._setup_essential_modules(core_modules)
|
||||||
|
self.configure_shared_module_behaviors(essential_modules)
|
||||||
|
return essential_modules
|
||||||
|
|
||||||
|
# pylint: disable-next=too-many-locals, too-many-statements, too-many-branches
|
||||||
|
def configure_shared_module_behaviors(self, essential_modules: dict[str, MagicMock]) -> None:
|
||||||
|
"""Configure standard behaviors for shared modules to reduce duplication."""
|
||||||
|
|
||||||
|
if "cthulhu.cthulhu_i18n" in essential_modules:
|
||||||
|
i18n_mock = essential_modules["cthulhu.cthulhu_i18n"]
|
||||||
|
i18n_mock._ = lambda x: x
|
||||||
|
i18n_mock.C_ = lambda c, x: x
|
||||||
|
i18n_mock.ngettext = lambda s, p, n: s if n == 1 else p
|
||||||
|
|
||||||
|
if "cthulhu.debug" in essential_modules:
|
||||||
|
debug_mock = essential_modules["cthulhu.debug"]
|
||||||
|
debug_mock.LEVEL_INFO = 800
|
||||||
|
debug_mock.LEVEL_SEVERE = 1000
|
||||||
|
debug_mock.print_message = self.mocker.Mock()
|
||||||
|
debug_mock.print_tokens = self.mocker.Mock()
|
||||||
|
debug_mock.println = self.mocker.Mock()
|
||||||
|
|
||||||
|
if "cthulhu.keybindings" in essential_modules:
|
||||||
|
keybindings_mock = essential_modules["cthulhu.keybindings"]
|
||||||
|
bindings_instance = self.mocker.Mock()
|
||||||
|
bindings_instance.is_empty = self.mocker.Mock(return_value=True)
|
||||||
|
bindings_instance.add = self.mocker.Mock()
|
||||||
|
keybindings_mock.KeyBindings = self.mocker.Mock(return_value=bindings_instance)
|
||||||
|
keybindings_mock.KeyBinding = self.mocker.Mock(return_value=self.mocker.Mock())
|
||||||
|
keybindings_mock.DEFAULT_MODIFIER_MASK = 1
|
||||||
|
keybindings_mock.CTHULHU_SHIFT_MODIFIER_MASK = 2
|
||||||
|
|
||||||
|
if "cthulhu.focus_manager" in essential_modules:
|
||||||
|
focus_manager_mock = essential_modules["cthulhu.focus_manager"]
|
||||||
|
manager_instance = self.mocker.Mock()
|
||||||
|
manager_instance.get_locus_of_focus = self.mocker.Mock(return_value=None)
|
||||||
|
manager_instance.set_locus_of_focus = self.mocker.Mock()
|
||||||
|
manager_instance.in_say_all = self.mocker.Mock(return_value=False)
|
||||||
|
manager_instance.is_in_preferences_window = self.mocker.Mock(return_value=False)
|
||||||
|
manager_instance.get_active_mode_and_object_of_interest = self.mocker.Mock(
|
||||||
|
return_value=(None, None),
|
||||||
|
)
|
||||||
|
focus_manager_mock.get_manager = self.mocker.Mock(return_value=manager_instance)
|
||||||
|
focus_manager_mock.OBJECT_NAVIGATOR = "object-navigator"
|
||||||
|
essential_modules["focus_manager_instance"] = manager_instance
|
||||||
|
|
||||||
|
if "cthulhu.dbus_service" in essential_modules:
|
||||||
|
dbus_service_mock = essential_modules["cthulhu.dbus_service"]
|
||||||
|
controller_mock = self.mocker.Mock()
|
||||||
|
controller_mock.register_decorated_module = self.mocker.Mock()
|
||||||
|
dbus_service_mock.get_remote_controller = self.mocker.Mock(return_value=controller_mock)
|
||||||
|
dbus_service_mock.command = lambda func: func
|
||||||
|
dbus_service_mock.getter = lambda func: func
|
||||||
|
dbus_service_mock.setter = lambda func: func
|
||||||
|
dbus_service_mock.parameterized_command = lambda func: func
|
||||||
|
|
||||||
|
if "cthulhu.script_manager" in essential_modules:
|
||||||
|
script_manager_mock = essential_modules["cthulhu.script_manager"]
|
||||||
|
manager_instance = self.mocker.Mock()
|
||||||
|
script_instance = self.mocker.Mock()
|
||||||
|
script_instance.present_message = self.mocker.Mock()
|
||||||
|
script_instance.present_object = self.mocker.Mock()
|
||||||
|
script_instance.speak_message = self.mocker.Mock()
|
||||||
|
script_instance.update_braille = self.mocker.Mock()
|
||||||
|
speech_gen = self.mocker.Mock()
|
||||||
|
braille_gen = self.mocker.Mock()
|
||||||
|
script_instance.get_speech_generator = self.mocker.Mock(return_value=speech_gen)
|
||||||
|
script_instance.get_braille_generator = self.mocker.Mock(return_value=braille_gen)
|
||||||
|
manager_instance.get_active_script = self.mocker.Mock(return_value=script_instance)
|
||||||
|
manager_instance.get_script = self.mocker.Mock(return_value=script_instance)
|
||||||
|
manager_instance.get_manager = self.mocker.Mock(return_value=manager_instance)
|
||||||
|
script_manager_mock.get_manager = self.mocker.Mock(return_value=manager_instance)
|
||||||
|
|
||||||
|
if "cthulhu.ax_object" in essential_modules:
|
||||||
|
ax_object_mock = essential_modules["cthulhu.ax_object"]
|
||||||
|
ax_object_class_mock = self.mocker.Mock()
|
||||||
|
ax_object_class_mock.is_valid = self.mocker.Mock(return_value=True)
|
||||||
|
ax_object_class_mock.is_dead = self.mocker.Mock(return_value=False)
|
||||||
|
ax_object_class_mock.get_name = self.mocker.Mock(return_value="")
|
||||||
|
ax_object_class_mock.get_role = self.mocker.Mock(return_value=Atspi.Role.PANEL)
|
||||||
|
ax_object_class_mock.get_parent = self.mocker.Mock(return_value=None)
|
||||||
|
ax_object_class_mock.find_ancestor = self.mocker.Mock(return_value=None)
|
||||||
|
ax_object_class_mock.clear_cache = self.mocker.Mock()
|
||||||
|
ax_object_mock.AXObject = ax_object_class_mock
|
||||||
|
|
||||||
|
if "cthulhu.ax_utilities" in essential_modules:
|
||||||
|
ax_utilities_mock = essential_modules["cthulhu.ax_utilities"]
|
||||||
|
ax_utilities_class_mock = self.mocker.Mock()
|
||||||
|
ax_utilities_class_mock.is_focused = self.mocker.Mock(return_value=True)
|
||||||
|
ax_utilities_class_mock.is_table_cell_or_header = self.mocker.Mock(return_value=False)
|
||||||
|
ax_utilities_class_mock.is_list_item = self.mocker.Mock(return_value=False)
|
||||||
|
ax_utilities_class_mock.is_layout_only = self.mocker.Mock(return_value=False)
|
||||||
|
ax_utilities_class_mock.get_status_bar = self.mocker.Mock(return_value=None)
|
||||||
|
ax_utilities_class_mock.get_info_bar = self.mocker.Mock(return_value=None)
|
||||||
|
ax_utilities_class_mock.is_showing = self.mocker.Mock(return_value=True)
|
||||||
|
ax_utilities_class_mock.is_visible = self.mocker.Mock(return_value=True)
|
||||||
|
ax_utilities_class_mock.is_sensitive = self.mocker.Mock(return_value=True)
|
||||||
|
ax_utilities_mock.AXUtilities = ax_utilities_class_mock
|
||||||
|
|
||||||
|
if "cthulhu.input_event" in essential_modules:
|
||||||
|
input_event_mock = essential_modules["cthulhu.input_event"]
|
||||||
|
input_event_mock.KEY_PRESSED_EVENT = "key-pressed"
|
||||||
|
input_event_mock.KEY_RELEASED_EVENT = "key-released"
|
||||||
|
input_event_mock.MOUSE_BUTTON_CLICKED_EVENT = "mouse-clicked"
|
||||||
|
|
||||||
|
if "cthulhu.cthulhu_platform" in essential_modules:
|
||||||
|
cthulhu_platform_mock = essential_modules["cthulhu.cthulhu_platform"]
|
||||||
|
cthulhu_platform_mock.tablesdir = "/usr/share/liblouis/tables"
|
||||||
|
|
||||||
|
if "cthulhu.braille_presenter" in essential_modules:
|
||||||
|
braille_presenter_mock = essential_modules["cthulhu.braille_presenter"]
|
||||||
|
presenter_instance = self.mocker.Mock()
|
||||||
|
presenter_instance.use_braille = self.mocker.Mock(return_value=False)
|
||||||
|
presenter_instance.get_braille_is_enabled = self.mocker.Mock(return_value=False)
|
||||||
|
presenter_instance.get_flash_messages_are_enabled = self.mocker.Mock(return_value=False)
|
||||||
|
presenter_instance.get_flash_messages_are_detailed = self.mocker.Mock(
|
||||||
|
return_value=False,
|
||||||
|
)
|
||||||
|
presenter_instance.get_flashtime_from_settings = self.mocker.Mock(return_value=5000)
|
||||||
|
braille_presenter_mock.get_presenter = self.mocker.Mock(return_value=presenter_instance)
|
||||||
|
|
||||||
|
if "cthulhu.presentation_manager" in essential_modules:
|
||||||
|
presentation_manager_mock = essential_modules["cthulhu.presentation_manager"]
|
||||||
|
manager_instance = self.mocker.Mock()
|
||||||
|
manager_instance.interrupt_presentation = self.mocker.Mock()
|
||||||
|
manager_instance.present_message = self.mocker.Mock()
|
||||||
|
manager_instance.speak_message = self.mocker.Mock()
|
||||||
|
manager_instance.speak_character = self.mocker.Mock()
|
||||||
|
manager_instance.present_braille_message = self.mocker.Mock()
|
||||||
|
manager_instance.spell_item = self.mocker.Mock()
|
||||||
|
manager_instance.spell_phonetically = self.mocker.Mock()
|
||||||
|
manager_instance.play_sound = self.mocker.Mock()
|
||||||
|
manager_instance.speak_contents = self.mocker.Mock()
|
||||||
|
manager_instance.display_contents = self.mocker.Mock()
|
||||||
|
presentation_manager_mock.get_manager = self.mocker.Mock(return_value=manager_instance)
|
||||||
|
|
||||||
|
if "gi.repository" in essential_modules:
|
||||||
|
gi_repo_mock = essential_modules["gi.repository"]
|
||||||
|
gi_repo_mock.Gio = Gio
|
||||||
|
gi_repo_mock.GLib = GLib
|
||||||
|
|
||||||
|
def get_mock(self, name: str) -> MagicMock | None:
|
||||||
|
"""Returns mock object if it exists, None otherwise."""
|
||||||
|
|
||||||
|
return self.mocks.get(name)
|
||||||
|
|
||||||
|
def __enter__(self) -> CthulhuTestContext: # noqa: PYI034
|
||||||
|
"""Enter the test context."""
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||||
|
"""Exit the test context and clean up all patches."""
|
||||||
|
|
||||||
|
for patch_obj in self.patches.values():
|
||||||
|
patch_obj.stop()
|
||||||
|
|
||||||
|
self.patches.clear()
|
||||||
|
self.mocks.clear()
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Orca Test Fixtures - Pytest Integration
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Pytest fixtures for Cthulhu screen reader tests.
|
||||||
|
|
||||||
|
This module provides pytest fixtures that integrate the CthulhuTestContext
|
||||||
|
with pytest's fixture system, providing clean, isolated test environments.
|
||||||
|
|
||||||
|
The fixtures are designed to be simple to use while providing complete
|
||||||
|
test isolation and preventing cross-test contamination.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
def test_presenter(orca_test):
|
||||||
|
cthulhu_test.setup_where_am_i_presenter_dependencies()
|
||||||
|
from cthulhu.where_am_i_presenter import WhereAmIPresenter
|
||||||
|
|
||||||
|
presenter = WhereAmIPresenter()
|
||||||
|
result = presenter.some_method()
|
||||||
|
assert result is True
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cthulhu_test_context import CthulhuTestContext
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Generator
|
||||||
|
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_context(mocker: MockerFixture, monkeypatch) -> Generator[CthulhuTestContext, None, None]:
|
||||||
|
"""Provides clean, isolated Orca test environment.
|
||||||
|
|
||||||
|
This is the primary fixture for Cthulhu tests. It provides a complete
|
||||||
|
test isolation context that prevents cross-test contamination.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
class TestMyModule:
|
||||||
|
def _setup_dependencies(self, cthulhu_test):
|
||||||
|
# Set up only what your module needs
|
||||||
|
return cthulhu_test.setup_shared_dependencies(["cthulhu.debug"])
|
||||||
|
|
||||||
|
def test_my_functionality(self, cthulhu_test):
|
||||||
|
self._setup_dependencies(orca_test)
|
||||||
|
from cthulhu.my_module import MyClass
|
||||||
|
|
||||||
|
# Your test code here
|
||||||
|
instance = MyClass()
|
||||||
|
result = instance.some_method()
|
||||||
|
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mocker: pytest-mock mocker fixture (automatically injected)
|
||||||
|
monkeypatch: pytest monkeypatch fixture (automatically injected)
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
CthulhuTestContext instance with clean isolation
|
||||||
|
"""
|
||||||
|
|
||||||
|
with CthulhuTestContext(mocker, monkeypatch) as context:
|
||||||
|
yield context
|
||||||
Reference in New Issue
Block a user