* fix(inject): skip leftover IBus on KDE so dictation actually types Test Dictation and clipboard paste worked. Inject claimed success and nothing showed up in Kate, Obsidian, terminals, or browsers. We started ibus-daemon on X11 even when the session never opted into IBus, then treated that daemon plus a bare xkb engine as active. commit_text() returns True. Qt/GTK on KDE never hear it. Do not spawn the daemon unless GTK_IM_MODULE / QT_IM_MODULE / XMODIFIERS already name IBus. On KDE, a running daemon without those env vars is not the session IM. Prefer a live WAYLAND_DISPLAY over a stale XDG_SESSION_TYPE=x11, and do not take the GNOME scoped-IBus shortcut on KDE. See #752 * style: wrap IBus helpers so black is happy CI lint failed on two lines in the #753 inject fix. Same behavior. * test(inject): leftover IBus on KDE should be skipped The CheckDependencies case still expected IBus for a leftover daemon on KDE. That is the #752 bug. It now expects skip and wtype instead.
129 lines
5.7 KiB
Python
129 lines
5.7 KiB
Python
"""Regression tests for issue #478.
|
|
|
|
On Wayland, IBus only delivers commit_text() to applications through a *real*
|
|
IM engine. A bare ``xkb:*`` engine (or none) is a layout passthrough, so
|
|
selecting the IBus backend there is a silent no-op. is_ibus_active_input_method()
|
|
must require a real engine on Wayland regardless of how IBus was detected, while
|
|
leaving X11/XWayland (XIM) behavior unchanged.
|
|
|
|
Note: we patch via ``patch.object`` on a held module reference (and call the
|
|
function through that same reference) rather than by dotted string, so the
|
|
patched ``get_current_engine`` / ``is_ibus_daemon_running`` and the function
|
|
under test always share one module object even when other test files re-import
|
|
``ibus_engine`` during a full-suite run.
|
|
"""
|
|
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
# Mock GI so importing ibus_engine does not require a real IBus/GTK stack.
|
|
_mock_gi = MagicMock()
|
|
_mock_gi_repo = MagicMock()
|
|
_mock_gi_repo.IBus = MagicMock()
|
|
_mock_gi_repo.GLib = MagicMock()
|
|
_mock_gi_repo.GObject = MagicMock()
|
|
_mock_gi_repo.IBus.Engine = MagicMock
|
|
_mock_gi_repo.GLib.MainLoop = MagicMock
|
|
sys.modules["gi"] = _mock_gi
|
|
sys.modules["gi.repository"] = _mock_gi_repo
|
|
|
|
for _key in list(sys.modules.keys()):
|
|
if "vocalinux" in _key and "ibus_engine" in _key:
|
|
del sys.modules[_key]
|
|
|
|
import vocalinux.text_injection.ibus_engine as ibus_engine # noqa: E402
|
|
|
|
|
|
class TestIsRealIBusEngine(unittest.TestCase):
|
|
def test_real_engines(self):
|
|
for engine in ("anthy", "libpinyin", "hangul", "mozc-jp"):
|
|
self.assertTrue(ibus_engine._is_real_ibus_engine(engine), engine)
|
|
|
|
def test_layout_only_or_missing_engines(self):
|
|
for engine in ("xkb:us::eng", "xkb:de::ger", "xkb:gb::eng", "", None, "No global engine"):
|
|
self.assertFalse(ibus_engine._is_real_ibus_engine(engine), repr(engine))
|
|
|
|
|
|
class TestWaylandRequiresRealEngine(unittest.TestCase):
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="xkb:us::eng")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "wayland", "QT_IM_MODULE": "ibus"}, clear=True)
|
|
def test_bare_xkb_engine_inactive_even_with_env_var(self, *_):
|
|
# GNOME/Mutter: QT_IM_MODULE=ibus but only a bare xkb engine -> no-op.
|
|
self.assertFalse(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="anthy")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "wayland"}, clear=True)
|
|
def test_real_engine_is_active(self, *_):
|
|
self.assertTrue(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value=None)
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "wayland", "GTK_IM_MODULE": "ibus"}, clear=True)
|
|
def test_no_engine_is_inactive(self, *_):
|
|
self.assertFalse(ibus_engine.is_ibus_active_input_method())
|
|
|
|
|
|
class TestX11BehaviorUnchanged(unittest.TestCase):
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="xkb:us::eng")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "x11"}, clear=True)
|
|
def test_daemon_with_xkb_engine_still_active_on_x11(self, *_):
|
|
# XIM delivers even with a bare xkb engine, so the daemon fallback stands.
|
|
self.assertTrue(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value=None)
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=False)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "x11", "GTK_IM_MODULE": "ibus"}, clear=True)
|
|
def test_env_var_is_active_on_x11(self, *_):
|
|
self.assertTrue(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value=None)
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=False)
|
|
@patch.dict("os.environ", {"XDG_SESSION_TYPE": "x11", "GTK_IM_MODULE": "fcitx"}, clear=True)
|
|
def test_other_im_configured_is_inactive(self, *_):
|
|
self.assertFalse(ibus_engine.is_ibus_active_input_method())
|
|
|
|
|
|
class TestKdeDoesNotTreatLeftoverDaemonAsIm(unittest.TestCase):
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="xkb:us::eng")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict(
|
|
"os.environ",
|
|
{"XDG_SESSION_TYPE": "x11", "XDG_CURRENT_DESKTOP": "KDE"},
|
|
clear=True,
|
|
)
|
|
def test_kde_x11_daemon_without_im_env_is_inactive(self, *_):
|
|
# CachyOS/KDE: we started (or found) ibus-daemon, but Qt/GTK never use it (#752).
|
|
self.assertFalse(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="xkb:us::eng")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict(
|
|
"os.environ",
|
|
{
|
|
"XDG_SESSION_TYPE": "x11",
|
|
"XDG_CURRENT_DESKTOP": "KDE",
|
|
"GTK_IM_MODULE": "ibus",
|
|
},
|
|
clear=True,
|
|
)
|
|
def test_kde_x11_with_ibus_env_still_active(self, *_):
|
|
self.assertTrue(ibus_engine.is_ibus_active_input_method())
|
|
|
|
@patch.object(ibus_engine, "get_current_engine", return_value="xkb:us::eng")
|
|
@patch.object(ibus_engine, "is_ibus_daemon_running", return_value=True)
|
|
@patch.dict(
|
|
"os.environ",
|
|
{"XDG_SESSION_TYPE": "x11", "KDE_FULL_SESSION": "true"},
|
|
clear=True,
|
|
)
|
|
def test_kde_full_session_without_im_env_is_inactive(self, *_):
|
|
self.assertFalse(ibus_engine.is_ibus_active_input_method())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|