mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
- Replace get_pipeline() closure with PipelinePool from domain/pipeline_factory - Replace resolve_voice_target() closure with domain function from voice_utils - Remove dead _load_pipeline() function and unused is_plugin_registered import - Add 33 tests for resolve_voice_target and PipelinePool - Add 10 regression tests verifying domain extraction preserves behavior - 1131 tests pass (+61 new)
146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
"""Regression tests: domain extraction must not break webui conversion_runner.
|
|
|
|
These tests verify that the refactored WebUI code paths still call into the
|
|
correct domain functions and produce the same results as the old inline logic.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from abogen.domain.voice_utils import resolve_voice_target
|
|
from abogen.domain.pipeline_factory import PipelinePool
|
|
|
|
|
|
class TestResolveVoiceTargetRegression:
|
|
"""Verify that the domain resolve_voice_target produces the same results
|
|
as the old closure in conversion_runner.py."""
|
|
|
|
def test_empty_spec_returns_kokoro_default(self):
|
|
provider, spec, speed, steps = resolve_voice_target(
|
|
"", {}, job_voice="af_sarah", job_tts_provider="kokoro",
|
|
)
|
|
assert provider == "kokoro"
|
|
assert spec == ""
|
|
|
|
def test_speaker_in_profile_kokoro(self):
|
|
profiles = {
|
|
"narrator": {
|
|
"provider": "kokoro",
|
|
"voices": [["af_sarah", 0.7], ["bf_emma", 0.3]],
|
|
},
|
|
}
|
|
provider, spec, speed, steps = resolve_voice_target(
|
|
"speaker:narrator", profiles,
|
|
)
|
|
assert provider == "kokoro"
|
|
assert "af_sarah" in spec
|
|
assert speed is None
|
|
assert steps is None
|
|
|
|
def test_speaker_in_profile_supertonic(self):
|
|
profiles = {
|
|
"narrator": {
|
|
"provider": "supertonic",
|
|
"voice": "F1",
|
|
"speed": 1.2,
|
|
"total_steps": 10,
|
|
},
|
|
}
|
|
provider, spec, speed, steps = resolve_voice_target(
|
|
"speaker:narrator", profiles,
|
|
job_voice="M1", job_speed=1.0, job_supertonic_total_steps=5,
|
|
)
|
|
assert provider == "supertonic"
|
|
assert spec == "F1"
|
|
assert speed == 1.2
|
|
assert steps == 10
|
|
|
|
def test_unknown_speaker_infers_from_spec(self):
|
|
with patch("abogen.domain.voice_utils.get_voices", return_value=["af_sarah"]):
|
|
provider, spec, speed, steps = resolve_voice_target(
|
|
"af_sarah", {}, job_tts_provider="kokoro",
|
|
)
|
|
assert provider == "kokoro"
|
|
assert spec == "af_sarah"
|
|
|
|
def test_uppercase_spec_infers_supertonic(self):
|
|
with patch("abogen.domain.voice_utils.get_voices", return_value=["af_sarah"]):
|
|
provider, spec, speed, steps = resolve_voice_target(
|
|
"M1", {}, job_voice="M1",
|
|
)
|
|
assert provider == "supertonic"
|
|
assert spec == "M1"
|
|
|
|
|
|
class TestPipelinePoolRegression:
|
|
"""Verify that PipelinePool behaves like the old inline get_pipeline closure."""
|
|
|
|
@patch("abogen.domain.pipeline_factory.create_pipeline_for_job")
|
|
@patch("abogen.domain.pipeline_factory.initialize_voice_cache")
|
|
def test_same_provider_returns_cached_pipeline(self, _cache, mock_create):
|
|
mock_pipeline = MagicMock()
|
|
mock_create.return_value = mock_pipeline
|
|
pool = PipelinePool()
|
|
|
|
r1 = pool.get("kokoro", "en", use_gpu=True)
|
|
r2 = pool.get("kokoro", "en", use_gpu=True)
|
|
assert r1 is r2
|
|
assert mock_create.call_count == 1
|
|
|
|
@patch("abogen.domain.pipeline_factory.create_pipeline_for_job")
|
|
@patch("abogen.domain.pipeline_factory.initialize_voice_cache")
|
|
def test_different_providers_get_separate_pipelines(self, _cache, mock_create):
|
|
p1 = MagicMock(name="kokoro")
|
|
p2 = MagicMock(name="supertonic")
|
|
mock_create.side_effect = [p1, p2]
|
|
pool = PipelinePool()
|
|
|
|
r1 = pool.get("kokoro", "en", use_gpu=True)
|
|
r2 = pool.get("supertonic", "en", use_gpu=True)
|
|
assert r1 is p1
|
|
assert r2 is p2
|
|
|
|
@patch("abogen.domain.pipeline_factory.create_pipeline_for_job")
|
|
@patch("abogen.domain.pipeline_factory.initialize_voice_cache")
|
|
def test_dispose_all_cleans_up(self, _cache, mock_create):
|
|
p1 = MagicMock()
|
|
p2 = MagicMock()
|
|
mock_create.side_effect = [p1, p2]
|
|
pool = PipelinePool()
|
|
|
|
pool.get("kokoro", "en", use_gpu=True)
|
|
pool.get("supertonic", "en", use_gpu=True)
|
|
pool.dispose_all()
|
|
|
|
p1.dispose.assert_called_once()
|
|
p2.dispose.assert_called_once()
|
|
assert pool._pipelines == {}
|
|
|
|
@patch("abogen.domain.pipeline_factory.create_pipeline_for_job")
|
|
@patch("abogen.domain.pipeline_factory.initialize_voice_cache")
|
|
def test_voice_cache_initialized_only_once(self, mock_cache, mock_create):
|
|
mock_create.return_value = MagicMock()
|
|
pool = PipelinePool()
|
|
job = MagicMock()
|
|
|
|
pool.get("kokoro", "en", use_gpu=True, job=job)
|
|
pool.get("kokoro", "en", use_gpu=True, job=job)
|
|
assert mock_cache.call_count == 1
|
|
|
|
@patch("abogen.domain.pipeline_factory.create_pipeline_for_job")
|
|
@patch("abogen.domain.pipeline_factory.initialize_voice_cache")
|
|
def test_after_dispose_voice_cache_can_reinitialize(self, mock_cache, mock_create):
|
|
mock_create.return_value = MagicMock()
|
|
pool = PipelinePool()
|
|
job = MagicMock()
|
|
|
|
pool.get("kokoro", "en", use_gpu=True, job=job)
|
|
assert mock_cache.call_count == 1
|
|
|
|
pool.dispose_all()
|
|
assert pool._voice_cache_initialized is False
|
|
|
|
pool.get("kokoro", "en", use_gpu=True, job=job)
|
|
assert mock_cache.call_count == 2
|