Port Orca 50 presentation and preferences layer
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
# Unit tests for profile_manager.py methods.
|
||||
#
|
||||
# Copyright 2025-2026 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=protected-access
|
||||
|
||||
"""Unit tests for profile_manager.py methods."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
import pytest
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cthulhu_test_context import CthulhuTestContext
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestProfileManager:
|
||||
"""Test ProfileManager methods."""
|
||||
|
||||
def _setup_dependencies(self, test_context: CthulhuTestContext) -> dict[str, MagicMock]:
|
||||
"""Set up mocks for profile_manager dependencies."""
|
||||
|
||||
additional_modules = [
|
||||
"cthulhu.braille",
|
||||
"cthulhu.cthulhu",
|
||||
"cthulhu.cthulhu_modifier_manager",
|
||||
"cthulhu.speech_manager",
|
||||
"cthulhu.braille_presenter",
|
||||
"cthulhu.presentation_manager",
|
||||
]
|
||||
essential_modules = test_context.setup_shared_dependencies(additional_modules)
|
||||
|
||||
from cthulhu import gsettings_registry
|
||||
|
||||
registry = gsettings_registry.get_registry()
|
||||
registry.set_active_profile("default")
|
||||
|
||||
test_context.patch(
|
||||
"cthulhu.profile_manager.ProfileManager._get_stored_profiles",
|
||||
return_value=[
|
||||
["Default", "default"],
|
||||
["Spanish", "spanish"],
|
||||
["Work", "work"],
|
||||
],
|
||||
)
|
||||
|
||||
speech_manager_mock = essential_modules["cthulhu.speech_manager"]
|
||||
speech_manager_mock.get_manager.return_value.refresh_speech.return_value = None
|
||||
|
||||
braille_mock = essential_modules["cthulhu.braille"]
|
||||
braille_mock.check_braille_setting.return_value = None
|
||||
|
||||
cthulhu_mock = essential_modules["cthulhu.cthulhu"]
|
||||
cthulhu_mock.load_user_settings.return_value = None
|
||||
|
||||
messages_mock = essential_modules["cthulhu.messages"]
|
||||
messages_mock.PROFILE_NOT_FOUND = "No profiles found."
|
||||
messages_mock.PROFILE_CHANGED = "Profile set to %s."
|
||||
messages_mock.PROFILE_CURRENT = "Current profile is %s."
|
||||
|
||||
guilabels_mock = essential_modules["cthulhu.guilabels"]
|
||||
guilabels_mock.PROFILE_DEFAULT = "Default"
|
||||
|
||||
return essential_modules
|
||||
|
||||
def test_get_active_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test getting active profile."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
profile = manager.get_active_profile()
|
||||
|
||||
assert profile == "default"
|
||||
|
||||
def test_set_active_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test setting active profile."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu import gsettings_registry
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
manager.set_active_profile("spanish")
|
||||
|
||||
assert gsettings_registry.get_registry().get_active_profile() == "spanish"
|
||||
|
||||
def test_load_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test loading a profile calls set_active_profile and load_user_settings."""
|
||||
|
||||
essential_modules = self._setup_dependencies(test_context)
|
||||
from cthulhu import gsettings_registry
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
manager.load_profile("spanish")
|
||||
|
||||
assert gsettings_registry.get_registry().get_active_profile() == "spanish"
|
||||
essential_modules["cthulhu.cthulhu"].load_user_settings.assert_called_once_with(
|
||||
skip_reload_message=True,
|
||||
)
|
||||
|
||||
def test_remove_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test removing a profile."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
mock_run = test_context.patch("subprocess.run")
|
||||
manager = ProfileManager()
|
||||
manager.remove_profile("spanish")
|
||||
|
||||
mock_run.assert_called_once_with(
|
||||
["dconf", "reset", "-f", "/org/gnome/cthulhu/spanish/"],
|
||||
check=True,
|
||||
)
|
||||
|
||||
def test_remove_profile_dconf_failure(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test removing a profile when dconf reset fails."""
|
||||
|
||||
import subprocess
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
mock_run = test_context.patch("subprocess.run")
|
||||
mock_run.side_effect = subprocess.CalledProcessError(1, "dconf")
|
||||
manager = ProfileManager()
|
||||
manager.remove_profile("spanish")
|
||||
|
||||
mock_run.assert_called_once()
|
||||
|
||||
def test_remove_profile_dconf_not_found(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test removing a profile when dconf is not installed."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
mock_run = test_context.patch("subprocess.run")
|
||||
mock_run.side_effect = FileNotFoundError("dconf not found")
|
||||
manager = ProfileManager()
|
||||
manager.remove_profile("spanish")
|
||||
|
||||
mock_run.assert_called_once()
|
||||
|
||||
def test_remove_profile_sanitizes_name(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test removing a profile sanitizes the name for the dconf path."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
mock_run = test_context.patch("subprocess.run")
|
||||
manager = ProfileManager()
|
||||
manager.remove_profile("My Profile")
|
||||
|
||||
mock_run.assert_called_once_with(
|
||||
["dconf", "reset", "-f", "/org/gnome/cthulhu/my-profile/"],
|
||||
check=True,
|
||||
)
|
||||
|
||||
def test_rename_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test renaming a profile."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu import gsettings_registry
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
registry = gsettings_registry.get_registry()
|
||||
mock_rename = test_context.patch_object(registry, "rename_profile")
|
||||
|
||||
manager = ProfileManager()
|
||||
manager.rename_profile("spanish", ["Espanol", "espanol"])
|
||||
|
||||
mock_rename.assert_called_once_with("spanish", "Espanol", "espanol")
|
||||
|
||||
def test_commands_registered(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test that profile manager commands are registered with CommandManager."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu import command_manager
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
manager.set_up_commands()
|
||||
cmd_manager = command_manager.get_manager()
|
||||
|
||||
assert cmd_manager.get_keyboard_command("cycleSettingsProfileHandler") is not None
|
||||
assert cmd_manager.get_keyboard_command("presentCurrentProfileHandler") is not None
|
||||
|
||||
def test_cycle_settings_profile_cycles_to_next(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test cycle_settings_profile cycles to next profile."""
|
||||
|
||||
essential_modules = self._setup_dependencies(test_context)
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cthulhu import gsettings_registry
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
mock_script = MagicMock()
|
||||
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
|
||||
pres_manager.present_message.reset_mock()
|
||||
|
||||
result = manager.cycle_settings_profile(script=mock_script)
|
||||
|
||||
assert result is True
|
||||
assert gsettings_registry.get_registry().get_active_profile() == "spanish"
|
||||
pres_manager.present_message.assert_called()
|
||||
|
||||
def test_cycle_settings_profile_wraps_around(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test cycle_settings_profile wraps to first profile at end."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cthulhu import gsettings_registry
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
gsettings_registry.get_registry().set_active_profile("work")
|
||||
|
||||
manager = ProfileManager()
|
||||
mock_script = MagicMock()
|
||||
|
||||
result = manager.cycle_settings_profile(script=mock_script)
|
||||
|
||||
assert result is True
|
||||
assert gsettings_registry.get_registry().get_active_profile() == "default"
|
||||
|
||||
def test_cycle_settings_profile_no_profiles(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test cycle_settings_profile handles no profiles."""
|
||||
|
||||
essential_modules = self._setup_dependencies(test_context)
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
mock_script = MagicMock()
|
||||
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
|
||||
pres_manager.present_message.reset_mock()
|
||||
|
||||
result = manager.cycle_settings_profile(script=mock_script)
|
||||
|
||||
assert result is True
|
||||
pres_manager.present_message.assert_called()
|
||||
|
||||
def test_present_current_profile(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test present_current_profile presents the current profile name."""
|
||||
|
||||
essential_modules = self._setup_dependencies(test_context)
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cthulhu.profile_manager import ProfileManager
|
||||
|
||||
manager = ProfileManager()
|
||||
mock_script = MagicMock()
|
||||
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
|
||||
pres_manager.present_message.reset_mock()
|
||||
|
||||
result = manager.present_current_profile(script=mock_script)
|
||||
|
||||
assert result is True
|
||||
pres_manager.present_message.assert_called()
|
||||
call_args = pres_manager.present_message.call_args
|
||||
assert "Default" in call_args[0][0]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestProfilePreferencesGridUI:
|
||||
"""Test ProfilePreferencesGrid UI creation."""
|
||||
|
||||
def _setup_dependencies(self, test_context: CthulhuTestContext) -> dict[str, MagicMock]:
|
||||
"""Set up mocks for ProfilePreferencesGrid dependencies."""
|
||||
|
||||
additional_modules = [
|
||||
"cthulhu.braille",
|
||||
"cthulhu.cthulhu",
|
||||
"cthulhu.speech_manager",
|
||||
"cthulhu.braille_presenter",
|
||||
"cthulhu.presentation_manager",
|
||||
]
|
||||
essential_modules = test_context.setup_shared_dependencies(additional_modules)
|
||||
|
||||
from cthulhu import gsettings_registry
|
||||
|
||||
registry = gsettings_registry.get_registry()
|
||||
registry.set_active_profile("default")
|
||||
|
||||
test_context.patch(
|
||||
"cthulhu.profile_manager.ProfileManager._get_stored_profiles",
|
||||
return_value=[
|
||||
["Default", "default"],
|
||||
["Spanish", "spanish"],
|
||||
],
|
||||
)
|
||||
|
||||
guilabels_mock = essential_modules["cthulhu.guilabels"]
|
||||
guilabels_mock.GENERAL_PROFILES = "Profiles"
|
||||
guilabels_mock.GENERAL_START_UP_PROFILE = "Start-up profile"
|
||||
guilabels_mock.PROFILE_DEFAULT = "Default"
|
||||
guilabels_mock.PROFILE_CONFLICT_MESSAGE = "Profile %s already exists"
|
||||
guilabels_mock.PROFILE_REMOVE_LABEL = "Remove Profile"
|
||||
guilabels_mock.PROFILE_REMOVE_MESSAGE = "Remove profile %s?"
|
||||
guilabels_mock.MENU_REMOVE_PROFILE = "Remove"
|
||||
guilabels_mock.MENU_RENAME = "Rename"
|
||||
guilabels_mock.PROFILE_SAVE_AS_TITLE = "Save Profile As"
|
||||
guilabels_mock.PROFILE_NAME_LABEL = "Profile name:"
|
||||
guilabels_mock.DIALOG_CANCEL = "Cancel"
|
||||
guilabels_mock.DIALOG_APPLY = "Apply"
|
||||
guilabels_mock.DIALOG_ADD = "Add"
|
||||
guilabels_mock.PROFILES_INFO = "Select a profile to edit or create a new one."
|
||||
guilabels_mock.CURRENT_PROFILE = "Current Profile"
|
||||
guilabels_mock.PROFILE_CREATE_NEW = "_Create New Profile"
|
||||
|
||||
return essential_modules
|
||||
|
||||
def test_grid_creates_successfully(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test ProfilePreferencesGrid creates without error."""
|
||||
|
||||
from gi.repository import Gtk # pylint: disable=no-name-in-module
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
assert isinstance(grid, Gtk.Grid)
|
||||
|
||||
def test_grid_has_auto_grid(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test ProfilePreferencesGrid has auto_grid with controls."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
assert grid._auto_grid is not None
|
||||
|
||||
def test_grid_save_settings_returns_dict(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test save_settings returns a dictionary."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
result = grid.save_settings()
|
||||
|
||||
assert isinstance(result, dict)
|
||||
|
||||
def test_grid_has_changes_initially_false(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test has_changes returns False initially."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
assert grid.has_changes() is False
|
||||
|
||||
def test_grid_reload_clears_pending_renames(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test reload clears pending renames."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
grid._pending_renames["old"] = ["New", "new"]
|
||||
assert len(grid._pending_renames) == 1
|
||||
|
||||
grid.reload()
|
||||
|
||||
assert len(grid._pending_renames) == 0
|
||||
|
||||
def test_grid_app_specific_disables_startup_setter(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test app-specific grid disables startup profile setter."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback, is_app_specific=True)
|
||||
|
||||
assert grid._is_app_specific is True
|
||||
|
||||
def test_validate_profile_name_detects_conflict(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test _validate_profile_name detects existing profile names."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
is_valid, error_msg = grid._validate_profile_name("Default")
|
||||
|
||||
assert is_valid is False
|
||||
assert "Default" in error_msg
|
||||
|
||||
def test_validate_profile_name_allows_unique(self, test_context: CthulhuTestContext) -> None:
|
||||
"""Test _validate_profile_name allows unique names."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
is_valid, error_msg = grid._validate_profile_name("NewProfile")
|
||||
|
||||
assert is_valid is True
|
||||
assert error_msg == ""
|
||||
|
||||
def test_get_available_profiles_includes_pending_renames(
|
||||
self,
|
||||
test_context: CthulhuTestContext,
|
||||
) -> None:
|
||||
"""Test _get_available_profiles includes pending renames."""
|
||||
|
||||
self._setup_dependencies(test_context)
|
||||
from cthulhu.profile_manager import ProfileManager, ProfilePreferencesGrid
|
||||
|
||||
manager = ProfileManager()
|
||||
|
||||
def callback(_profile):
|
||||
return None
|
||||
|
||||
grid = ProfilePreferencesGrid(manager, callback)
|
||||
|
||||
grid._pending_renames["spanish"] = ["Espanol", "spanish"]
|
||||
profiles = grid._get_available_profiles()
|
||||
|
||||
profile_names = [p[0] for p in profiles]
|
||||
assert "Espanol" in profile_names
|
||||
assert "Spanish" not in profile_names
|
||||
Reference in New Issue
Block a user