99 lines
3.2 KiB
Python
99 lines
3.2 KiB
Python
"""Tests for keyboard layout detection and character-to-evdev-keycode mapping."""
|
|
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from evdev import ecodes
|
|
|
|
from vibe_coded_mcp_desktop_control import layout_mapper
|
|
from vibe_coded_mcp_desktop_control.layout_mapper import (
|
|
detect_keyboard_layout,
|
|
get_keycode_for_char,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def no_subprocess_layout(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Prevent subprocess-based layout detection from influencing tests."""
|
|
monkeypatch.delenv("XKB_DEFAULT_LAYOUT", raising=False)
|
|
with (
|
|
mock.patch.object(layout_mapper, "logger"),
|
|
mock.patch(
|
|
"vibe_coded_mcp_desktop_control.layout_mapper.subprocess.run",
|
|
return_value=mock.Mock(returncode=1, stdout="", stderr=""),
|
|
),
|
|
):
|
|
yield
|
|
|
|
|
|
def test_config_layout_takes_precedence(no_subprocess_layout: None) -> None:
|
|
"""An explicitly provided layout should be returned without further detection."""
|
|
assert detect_keyboard_layout("de") == "de"
|
|
|
|
|
|
def test_environment_variable_detection(
|
|
monkeypatch: pytest.MonkeyPatch, no_subprocess_layout: None
|
|
) -> None:
|
|
"""XKB_DEFAULT_LAYOUT should be used when no config layout is provided."""
|
|
monkeypatch.setenv("XKB_DEFAULT_LAYOUT", "fr,us")
|
|
assert detect_keyboard_layout(None) == "fr"
|
|
|
|
|
|
def test_environment_variable_detection_single(
|
|
monkeypatch: pytest.MonkeyPatch, no_subprocess_layout: None
|
|
) -> None:
|
|
"""A single XKB_DEFAULT_LAYOUT value should be returned as-is."""
|
|
monkeypatch.setenv("XKB_DEFAULT_LAYOUT", "si")
|
|
assert detect_keyboard_layout(None) == "si"
|
|
|
|
|
|
def test_fallback_to_us(no_subprocess_layout: None) -> None:
|
|
"""When nothing else is available, the layout should fall back to 'us'."""
|
|
assert detect_keyboard_layout(None) == "us"
|
|
|
|
|
|
def test_us_lowercase_letter() -> None:
|
|
"""A basic US lowercase letter should map to its keycode without modifiers."""
|
|
keycode, shift, altgr = get_keycode_for_char("a", "us")
|
|
assert keycode == ecodes.KEY_A
|
|
assert shift is False
|
|
assert altgr is False
|
|
|
|
|
|
def test_us_uppercase_letter() -> None:
|
|
"""A basic US uppercase letter should require Shift."""
|
|
keycode, shift, altgr = get_keycode_for_char("A", "us")
|
|
assert keycode == ecodes.KEY_A
|
|
assert shift is True
|
|
assert altgr is False
|
|
|
|
|
|
def test_us_digit_requires_shift() -> None:
|
|
"""US shifted digits should require Shift."""
|
|
keycode, shift, altgr = get_keycode_for_char("!", "us")
|
|
assert keycode == ecodes.KEY_1
|
|
assert shift is True
|
|
assert altgr is False
|
|
|
|
|
|
def test_si_altgr_character() -> None:
|
|
"""Slovenian AltGr characters should require the AltGr modifier."""
|
|
keycode, shift, altgr = get_keycode_for_char("@", "si")
|
|
assert keycode == ecodes.KEY_V
|
|
assert shift is False
|
|
assert altgr is True
|
|
|
|
|
|
def test_si_yz_swap() -> None:
|
|
"""Slovenian layout swaps the Y and Z physical keys."""
|
|
y_keycode, _, _ = get_keycode_for_char("y", "si")
|
|
z_keycode, _, _ = get_keycode_for_char("z", "si")
|
|
assert y_keycode == ecodes.KEY_Z
|
|
assert z_keycode == ecodes.KEY_Y
|
|
|
|
|
|
def test_unknown_character_raises() -> None:
|
|
"""Characters not present in any static map should raise ValueError."""
|
|
with pytest.raises(ValueError):
|
|
get_keycode_for_char("\x00", "us")
|