2026-04-06 20:29:45 +02:00
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from whisper_local.__main__ import App
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestApp:
|
2026-04-10 21:21:29 +02:00
|
|
|
@patch("whisper_local.__main__.create_tray")
|
2026-04-06 20:29:45 +02:00
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
2026-04-08 10:38:56 +02:00
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
2026-04-10 21:21:29 +02:00
|
|
|
def test_app_init(self, mock_inserter_factory, mock_listener_factory, mock_transcriber_class, mock_tray_factory):
|
2026-04-06 20:29:45 +02:00
|
|
|
app = App()
|
|
|
|
|
assert app.recorder is not None
|
|
|
|
|
mock_transcriber_class.assert_called_once()
|
2026-04-08 10:38:56 +02:00
|
|
|
mock_listener_factory.assert_called_once()
|
|
|
|
|
mock_inserter_factory.assert_called_once()
|
2026-04-06 20:29:45 +02:00
|
|
|
|
2026-04-10 21:21:29 +02:00
|
|
|
@patch("whisper_local.__main__.create_tray")
|
2026-04-06 20:29:45 +02:00
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
2026-04-08 10:38:56 +02:00
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
2026-04-10 21:21:29 +02:00
|
|
|
def test_on_press_starts_recording(self, mock_inserter_factory, mock_listener_factory, mock_transcriber_class, mock_tray_factory):
|
2026-04-06 20:29:45 +02:00
|
|
|
app = App()
|
|
|
|
|
app.recorder = MagicMock()
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
asyncio.run(app.on_press())
|
|
|
|
|
|
|
|
|
|
app.recorder.start.assert_called_once()
|
|
|
|
|
|
2026-04-10 21:21:29 +02:00
|
|
|
@patch("whisper_local.__main__.create_tray")
|
2026-04-06 20:29:45 +02:00
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
2026-04-08 10:38:56 +02:00
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
2026-04-10 21:21:29 +02:00
|
|
|
def test_on_release_stops_and_transcribes(self, mock_inserter_factory, mock_listener_factory, mock_transcriber_class, mock_tray_factory):
|
2026-04-06 20:29:45 +02:00
|
|
|
mock_transcriber = MagicMock()
|
|
|
|
|
mock_transcriber.transcribe.return_value = "Hallo"
|
|
|
|
|
mock_transcriber_class.return_value = mock_transcriber
|
|
|
|
|
|
|
|
|
|
app = App()
|
|
|
|
|
app.recorder = MagicMock()
|
|
|
|
|
audio = np.zeros(16000, dtype=np.float32)
|
|
|
|
|
app.recorder.stop.return_value = audio
|
|
|
|
|
app.inserter = MagicMock()
|
|
|
|
|
app.inserter.insert = AsyncMock()
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
asyncio.run(app.on_release())
|
|
|
|
|
|
|
|
|
|
app.recorder.stop.assert_called_once()
|
|
|
|
|
mock_transcriber.transcribe.assert_called_once_with(audio)
|
|
|
|
|
app.inserter.insert.assert_awaited_once_with("Hallo")
|
|
|
|
|
|
2026-04-10 21:21:29 +02:00
|
|
|
@patch("whisper_local.__main__.create_tray")
|
2026-04-06 20:29:45 +02:00
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
2026-04-08 10:38:56 +02:00
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
2026-04-10 21:21:29 +02:00
|
|
|
def test_on_release_no_audio_skips(self, mock_inserter_factory, mock_listener_factory, mock_transcriber_class, mock_tray_factory):
|
2026-04-06 20:29:45 +02:00
|
|
|
mock_transcriber = MagicMock()
|
|
|
|
|
mock_transcriber_class.return_value = mock_transcriber
|
|
|
|
|
|
|
|
|
|
app = App()
|
|
|
|
|
app.recorder = MagicMock()
|
|
|
|
|
app.recorder.stop.return_value = None
|
|
|
|
|
app.inserter = MagicMock()
|
|
|
|
|
app.inserter.insert = AsyncMock()
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
asyncio.run(app.on_release())
|
|
|
|
|
|
|
|
|
|
mock_transcriber.transcribe.assert_not_called()
|
|
|
|
|
app.inserter.insert.assert_not_awaited()
|
2026-04-10 21:21:29 +02:00
|
|
|
|
|
|
|
|
@patch("whisper_local.__main__.create_tray")
|
|
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
|
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
|
|
|
|
def test_on_press_sets_recording_state(
|
|
|
|
|
self, mock_inserter_factory, mock_listener_factory,
|
|
|
|
|
mock_transcriber_class, mock_tray_factory
|
|
|
|
|
):
|
|
|
|
|
from whisper_local.tray import AppState
|
|
|
|
|
mock_tray = MagicMock()
|
|
|
|
|
mock_tray_factory.return_value = mock_tray
|
|
|
|
|
|
|
|
|
|
app = App()
|
|
|
|
|
app.recorder = MagicMock()
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
asyncio.run(app.on_press())
|
|
|
|
|
|
|
|
|
|
mock_tray.set_state.assert_called_with(AppState.RECORDING)
|
|
|
|
|
|
|
|
|
|
@patch("whisper_local.__main__.create_tray")
|
|
|
|
|
@patch("whisper_local.__main__.Transcriber")
|
|
|
|
|
@patch("whisper_local.__main__.create_listener")
|
|
|
|
|
@patch("whisper_local.__main__.create_inserter")
|
|
|
|
|
def test_on_release_sets_waiting_state_after_transcription(
|
|
|
|
|
self, mock_inserter_factory, mock_listener_factory,
|
|
|
|
|
mock_transcriber_class, mock_tray_factory
|
|
|
|
|
):
|
|
|
|
|
from whisper_local.tray import AppState
|
|
|
|
|
mock_tray = MagicMock()
|
|
|
|
|
mock_tray_factory.return_value = mock_tray
|
|
|
|
|
|
|
|
|
|
mock_transcriber = MagicMock()
|
|
|
|
|
mock_transcriber.transcribe.return_value = "Text"
|
|
|
|
|
mock_transcriber_class.return_value = mock_transcriber
|
|
|
|
|
|
|
|
|
|
app = App()
|
|
|
|
|
app.recorder = MagicMock()
|
|
|
|
|
app.recorder.stop.return_value = np.zeros(16000, dtype=np.float32)
|
|
|
|
|
app.inserter = MagicMock()
|
|
|
|
|
app.inserter.insert = AsyncMock()
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
asyncio.run(app.on_release())
|
|
|
|
|
|
|
|
|
|
calls = [c.args[0] for c in mock_tray.set_state.call_args_list]
|
|
|
|
|
assert AppState.TRANSCRIBING in calls
|
|
|
|
|
assert calls[-1] == AppState.WAITING
|