| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- # -*- coding: utf-8 -*-
- """
- Tests für das Coqui TTS Plugin.
- """
- import pytest
- from pathlib import Path
- from unittest.mock import MagicMock, AsyncMock, patch
- import importlib.util
- # Plugin-main.py direkt laden
- plugin_path = Path(__file__).parent.parent / "main.py"
- spec = importlib.util.spec_from_file_location("coqui_tts_plugin", plugin_path)
- coqui_module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(coqui_module)
- CoquiTTSPlugin = coqui_module.CoquiTTSPlugin
- CoquiTTSProvider = coqui_module.CoquiTTSProvider
- COQUI_MODELS = coqui_module.COQUI_MODELS
- from trixy_core.audio.tts import TTSConfig, TTSState
- class TestCoquiModelsCatalog:
- """Tests für den Coqui-Modell-Katalog."""
- def test_models_catalog_not_empty(self):
- """Prüft, dass der Modellkatalog nicht leer ist."""
- assert len(COQUI_MODELS) > 0
- def test_models_have_required_fields(self):
- """Prüft, dass alle Modelle die erforderlichen Felder haben."""
- required_fields = ["name", "language", "gender", "description", "sample_rate"]
- for model_id, model_info in COQUI_MODELS.items():
- for field in required_fields:
- assert field in model_info, f"Modell {model_id} fehlt Feld '{field}'"
- def test_german_models_available(self):
- """Prüft, dass deutsche Modelle verfügbar sind."""
- german_models = [m for m, info in COQUI_MODELS.items() if info["language"] == "de-DE"]
- assert len(german_models) >= 1
- def test_thorsten_models_available(self):
- """Prüft, dass Thorsten-Modelle (deutsche Stimme) verfügbar sind."""
- thorsten_models = [m for m in COQUI_MODELS.keys() if "thorsten" in m]
- assert len(thorsten_models) >= 1
- def test_model_ids_are_valid_paths(self):
- """Prüft, dass Modell-IDs gültige Coqui-Pfade sind."""
- for model_id in COQUI_MODELS.keys():
- assert model_id.startswith("tts_models/"), f"Modell {model_id} hat ungültiges Präfix"
- class TestCoquiTTSProvider:
- """Tests für den CoquiTTSProvider."""
- @pytest.fixture
- def provider(self, tmp_path):
- """Erstellt einen Provider für Tests."""
- config = TTSConfig(language="de-DE", voice_id="tts_models/de/thorsten/tacotron2-DDC")
- return CoquiTTSProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="tts_models/de/thorsten/tacotron2-DDC",
- use_cuda=False,
- )
- def test_provider_name(self, provider):
- """Prüft den Provider-Namen."""
- assert provider.name == "coqui"
- def test_supported_languages(self, provider):
- """Prüft die unterstützten Sprachen."""
- languages = provider.supported_languages
- assert "de-DE" in languages
- assert "en-US" in languages
- assert "fr-FR" in languages
- def test_supports_streaming(self, provider):
- """Prüft, dass Streaming nicht unterstützt wird."""
- assert provider.supports_streaming is False
- def test_initial_state(self, provider):
- """Prüft den initialen Zustand."""
- assert provider._model_loaded is False
- assert provider._tts is None
- def test_cuda_config(self):
- """Prüft die CUDA-Konfiguration."""
- config = TTSConfig(language="de-DE")
- provider = CoquiTTSProvider(
- config=config,
- models_dir=Path("/tmp/models"),
- model_name="tts_models/de/thorsten/tacotron2-DDC",
- use_cuda=True,
- )
- assert provider._use_cuda is True
- @pytest.mark.asyncio
- async def test_synthesize_without_init_raises(self, provider):
- """Prüft, dass synthesize ohne Initialisierung fehlschlägt."""
- with pytest.raises(RuntimeError, match="Coqui TTS nicht geladen"):
- await provider.synthesize("Test")
- @pytest.mark.asyncio
- async def test_get_voices(self, provider):
- """Prüft die get_voices Methode (vor Initialisierung)."""
- # Vor Initialisierung ist der Voice-Katalog basierend auf COQUI_MODELS
- await provider._load_voices()
- voices = await provider.get_voices()
- assert len(voices) > 0
- class TestCoquiTTSPlugin:
- """Tests für das CoquiTTSPlugin."""
- @pytest.fixture
- def plugin(self, mock_application, tmp_path):
- """Erstellt ein Plugin für Tests."""
- plugin_path = tmp_path / "tts_coqui"
- plugin_path.mkdir()
- (plugin_path / "models").mkdir()
- config = {
- "name": "Coqui TTS",
- "enabled": True,
- "model": "tts_models/de/thorsten/tacotron2-DDC",
- "language": "de-DE",
- "use_cuda": False,
- }
- return CoquiTTSPlugin(mock_application, plugin_path, config)
- def test_plugin_initialization(self, plugin):
- """Prüft die Plugin-Initialisierung."""
- assert plugin._provider is None
- assert plugin.config.get("model") == "tts_models/de/thorsten/tacotron2-DDC"
- def test_plugin_path(self, plugin):
- """Prüft den Plugin-Pfad."""
- assert plugin.plugin_path.exists()
- @pytest.mark.asyncio
- async def test_on_unload_without_provider(self, plugin):
- """Prüft on_unload ohne initialisierten Provider."""
- await plugin.on_unload()
- assert plugin._provider is None
- class TestCoquiMultiSpeaker:
- """Tests für Multi-Speaker-Funktionalität."""
- def test_voice_id_with_speaker(self):
- """Prüft Voice-ID mit Speaker-Extraktion."""
- voice_id = "tts_models/multilingual/multi-dataset/your_tts:speaker_name"
- if ":" in voice_id:
- model_id, speaker = voice_id.split(":", 1)
- assert model_id == "tts_models/multilingual/multi-dataset/your_tts"
- assert speaker == "speaker_name"
- def test_multilingual_model_exists(self):
- """Prüft, dass ein multilinguales Modell verfügbar ist."""
- multilingual = [m for m, info in COQUI_MODELS.items() if info["language"] == "multi"]
- assert len(multilingual) >= 1
- # Integration Tests (benötigen echte Installation)
- @pytest.mark.integration
- class TestCoquiTTSIntegration:
- """Integrationstests für Coqui TTS (benötigen Installation)."""
- @pytest.fixture(autouse=True)
- def skip_if_not_installed(self):
- """Überspringt Tests wenn TTS nicht installiert ist."""
- pytest.importorskip("TTS", reason="Coqui TTS nicht installiert")
- @pytest.fixture
- def provider_with_model(self, tmp_path):
- """Erstellt einen Provider mit echtem Modell."""
- config = TTSConfig(language="de-DE", voice_id="tts_models/de/thorsten/tacotron2-DDC")
- return CoquiTTSProvider(
- config=config,
- models_dir=tmp_path / "models",
- model_name="tts_models/de/thorsten/tacotron2-DDC",
- use_cuda=False,
- )
- @pytest.mark.asyncio
- @pytest.mark.slow
- async def test_full_synthesis_flow(self, provider_with_model):
- """Testet den vollständigen Synthese-Flow."""
- await provider_with_model.initialize()
- result = await provider_with_model.synthesize("Hallo Welt")
- assert result.audio_data is not None
- assert len(result.audio_data) > 0
- assert result.sample_rate > 0
- assert result.duration_seconds > 0
- assert result.provider == "coqui"
- await provider_with_model.shutdown()
|