fix: use ydotool + wl-copy for text insertion on KDE Wayland
Replace wtype (unsupported on KDE) with ydotool key for Ctrl+V simulation. Use wl-copy for clipboard-based insertion to avoid ydotool type's QWERTZ/QWERTY layout mismatch. Use DEVNULL instead of PIPE for wl-copy to prevent hanging on its forked background process. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+17
-17
@@ -1,5 +1,5 @@
|
||||
import asyncio
|
||||
from unittest.mock import AsyncMock, MagicMock, patch, call
|
||||
from unittest.mock import AsyncMock, patch, call
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -11,22 +11,25 @@ class TestInserter:
|
||||
@patch("whisper_local.inserter.asyncio.sleep", new_callable=AsyncMock)
|
||||
@patch("whisper_local.inserter.asyncio.create_subprocess_exec")
|
||||
async def test_insert_text_calls_tools_in_order(self, mock_exec, mock_sleep):
|
||||
# Mock für alle subprocess-Aufrufe
|
||||
mock_proc = AsyncMock()
|
||||
mock_proc.communicate.return_value = (b"alter clipboard", b"")
|
||||
mock_proc.returncode = 0
|
||||
mock_proc.wait = AsyncMock()
|
||||
mock_exec.return_value = mock_proc
|
||||
|
||||
inserter = Inserter()
|
||||
await inserter.insert("Hallo Welt")
|
||||
|
||||
# Prüfe dass wl-paste, wl-copy, wtype, wl-copy aufgerufen werden
|
||||
calls = mock_exec.call_args_list
|
||||
assert len(calls) == 4
|
||||
assert calls[0][0][0] == "wl-paste" # Clipboard sichern
|
||||
assert calls[1][0] == ("wl-copy", "Hallo Welt") # Text in Clipboard
|
||||
assert calls[2][0][0] == "wtype" # Ctrl+V simulieren
|
||||
assert calls[3][0] == ("wl-copy", "alter clipboard") # Clipboard wiederherstellen
|
||||
# wl-paste mit PIPE (braucht stdout)
|
||||
assert calls[0][0] == ("wl-paste", "--no-newline")
|
||||
# wl-copy mit DEVNULL (forkt Hintergrundprozess)
|
||||
assert calls[1][0] == ("wl-copy", "--", "Hallo Welt")
|
||||
# ydotool key
|
||||
assert calls[2][0][0] == "ydotool"
|
||||
# wl-copy restore
|
||||
assert calls[3][0] == ("wl-copy", "--", "alter clipboard")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("whisper_local.inserter.asyncio.create_subprocess_exec")
|
||||
@@ -46,24 +49,21 @@ class TestInserter:
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
mock_proc = AsyncMock()
|
||||
mock_proc.wait = AsyncMock()
|
||||
if call_count == 1: # wl-paste
|
||||
mock_proc.communicate.return_value = (b"original", b"")
|
||||
mock_proc.returncode = 0
|
||||
elif call_count == 2: # wl-copy
|
||||
mock_proc.communicate.return_value = (b"", b"")
|
||||
mock_proc.returncode = 0
|
||||
elif call_count == 3: # wtype — fails
|
||||
mock_proc.communicate.return_value = (b"", b"error")
|
||||
mock_proc.returncode = 1
|
||||
else: # wl-copy restore
|
||||
mock_proc.communicate.return_value = (b"", b"")
|
||||
elif call_count == 3: # ydotool — fails
|
||||
mock_proc.wait.side_effect = OSError("ydotool not found")
|
||||
else: # wl-copy calls
|
||||
mock_proc.returncode = 0
|
||||
return mock_proc
|
||||
|
||||
mock_exec.side_effect = mock_create_proc
|
||||
|
||||
inserter = Inserter()
|
||||
await inserter.insert("Test")
|
||||
with pytest.raises(OSError):
|
||||
await inserter.insert("Test")
|
||||
|
||||
# Clipboard muss trotzdem wiederhergestellt werden
|
||||
# Clipboard muss trotzdem wiederhergestellt werden (finally-Block)
|
||||
assert call_count == 4
|
||||
|
||||
Reference in New Issue
Block a user