test_whisper_stt.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # -*- coding: utf-8 -*-
  2. """
  3. Tests für das Whisper STT Plugin.
  4. """
  5. import pytest
  6. from pathlib import Path
  7. from unittest.mock import MagicMock, AsyncMock, patch
  8. import importlib.util
  9. # Plugin-main.py direkt laden
  10. plugin_path = Path(__file__).parent.parent / "main.py"
  11. spec = importlib.util.spec_from_file_location("whisper_plugin", plugin_path)
  12. whisper_module = importlib.util.module_from_spec(spec)
  13. spec.loader.exec_module(whisper_module)
  14. WhisperSTTPlugin = whisper_module.WhisperSTTPlugin
  15. WhisperSTTProvider = whisper_module.WhisperSTTProvider
  16. WHISPER_MODELS = whisper_module.WHISPER_MODELS
  17. from trixy_core.audio.stt import STTConfig, STTState
  18. class TestWhisperModelsCatalog:
  19. """Tests für den Whisper-Modell-Katalog."""
  20. def test_models_catalog_not_empty(self):
  21. """Prüft, dass der Modellkatalog nicht leer ist."""
  22. assert len(WHISPER_MODELS) > 0
  23. def test_models_have_required_fields(self):
  24. """Prüft, dass alle Modelle die erforderlichen Felder haben."""
  25. required_fields = ["size", "description"]
  26. for model_id, model_info in WHISPER_MODELS.items():
  27. for field in required_fields:
  28. assert field in model_info, f"Modell {model_id} fehlt Feld '{field}'"
  29. def test_common_models_available(self):
  30. """Prüft, dass gängige Modelle verfügbar sind."""
  31. assert "tiny" in WHISPER_MODELS
  32. assert "base" in WHISPER_MODELS
  33. assert "small" in WHISPER_MODELS
  34. assert "medium" in WHISPER_MODELS
  35. assert "large" in WHISPER_MODELS
  36. def test_english_only_models_available(self):
  37. """Prüft, dass englischsprachige Modelle verfügbar sind."""
  38. assert "tiny.en" in WHISPER_MODELS
  39. assert "base.en" in WHISPER_MODELS
  40. class TestWhisperSTTProvider:
  41. """Tests für den WhisperSTTProvider."""
  42. @pytest.fixture
  43. def provider(self, tmp_path):
  44. """Erstellt einen Provider für Tests."""
  45. config = STTConfig(language="de-DE")
  46. return WhisperSTTProvider(
  47. config=config,
  48. models_dir=tmp_path / "models",
  49. model_name="base",
  50. )
  51. def test_provider_name(self, provider):
  52. """Prüft den Provider-Namen."""
  53. assert provider.name == "whisper"
  54. def test_supported_languages(self, provider):
  55. """Prüft die unterstützten Sprachen."""
  56. languages = provider.supported_languages
  57. assert "de-DE" in languages
  58. assert "en-US" in languages
  59. assert "fr-FR" in languages
  60. assert "ja-JP" in languages
  61. def test_supports_word_timings(self, provider):
  62. """Prüft Wort-Timings-Unterstützung."""
  63. assert provider.supports_word_timings is True
  64. def test_initial_state(self, provider):
  65. """Prüft den initialen Zustand."""
  66. assert provider._model_loaded is False
  67. assert provider._model is None
  68. def test_device_selection_cpu(self, tmp_path):
  69. """Prüft CPU-Auswahl."""
  70. config = STTConfig(language="de-DE", use_gpu=False)
  71. provider = WhisperSTTProvider(
  72. config=config,
  73. models_dir=tmp_path / "models",
  74. model_name="base",
  75. )
  76. assert provider._device == "cpu"
  77. def test_device_selection_gpu(self, tmp_path):
  78. """Prüft GPU-Auswahl."""
  79. config = STTConfig(language="de-DE", use_gpu=True)
  80. provider = WhisperSTTProvider(
  81. config=config,
  82. models_dir=tmp_path / "models",
  83. model_name="base",
  84. )
  85. assert provider._device == "cuda"
  86. @pytest.mark.asyncio
  87. async def test_transcribe_without_init_raises(self, provider, sample_audio_16khz):
  88. """Prüft, dass transcribe ohne Initialisierung fehlschlägt."""
  89. with pytest.raises(RuntimeError, match="Whisper-Modell nicht geladen"):
  90. await provider.transcribe(sample_audio_16khz)
  91. class TestWhisperSTTPlugin:
  92. """Tests für das WhisperSTTPlugin."""
  93. @pytest.fixture
  94. def plugin(self, mock_application, tmp_path):
  95. """Erstellt ein Plugin für Tests."""
  96. plugin_path = tmp_path / "stt_whisper"
  97. plugin_path.mkdir()
  98. (plugin_path / "models").mkdir()
  99. config = {
  100. "name": "Whisper STT",
  101. "enabled": True,
  102. "model": "base",
  103. "language": "de-DE",
  104. "use_gpu": False,
  105. }
  106. return WhisperSTTPlugin(mock_application, plugin_path, config)
  107. def test_plugin_initialization(self, plugin):
  108. """Prüft die Plugin-Initialisierung."""
  109. assert plugin._provider is None
  110. assert plugin.config.get("model") == "base"
  111. def test_plugin_path(self, plugin):
  112. """Prüft den Plugin-Pfad."""
  113. assert plugin.plugin_path.exists()
  114. @pytest.mark.asyncio
  115. async def test_on_unload_without_provider(self, plugin):
  116. """Prüft on_unload ohne initialisierten Provider."""
  117. await plugin.on_unload()
  118. assert plugin._provider is None
  119. # Integration Tests (benötigen echte Installation)
  120. @pytest.mark.integration
  121. class TestWhisperIntegration:
  122. """Integrationstests für Whisper STT (benötigen Installation)."""
  123. @pytest.fixture(autouse=True)
  124. def skip_if_not_installed(self):
  125. """Überspringt Tests wenn whisper nicht installiert ist."""
  126. pytest.importorskip("whisper", reason="openai-whisper nicht installiert")
  127. @pytest.fixture
  128. def provider_with_model(self, tmp_path):
  129. """Erstellt einen Provider mit echtem Modell."""
  130. config = STTConfig(language="de-DE", use_gpu=False)
  131. return WhisperSTTProvider(
  132. config=config,
  133. models_dir=tmp_path / "models",
  134. model_name="tiny", # Kleinstes Modell für schnelle Tests
  135. )
  136. @pytest.mark.asyncio
  137. @pytest.mark.slow
  138. async def test_full_transcription_flow(self, provider_with_model, sample_audio_with_tone):
  139. """Testet den vollständigen Transkriptions-Flow."""
  140. await provider_with_model.initialize()
  141. result = await provider_with_model.transcribe(sample_audio_with_tone)
  142. assert result.text is not None
  143. assert result.provider == "whisper"
  144. assert result.processing_time_ms > 0
  145. await provider_with_model.shutdown()