from __future__ import annotations from unittest.mock import MagicMock, patch from abogen.domain.pipeline_factory import ( PipelinePool, create_pipeline_for_job, dispose_pipelines, resolve_device, ) class TestResolveDevice: @patch("abogen.utils.load_config", return_value={"use_gpu": True}) @patch("abogen.domain.pipeline_factory.select_device", return_value="cuda:0") def test_gpu_enabled(self, _sel, _cfg): assert resolve_device(use_gpu=True) == "cuda:0" @patch("abogen.utils.load_config", return_value={"use_gpu": True}) def test_gpu_disabled_by_job(self, _cfg): assert resolve_device(use_gpu=False) == "cpu" @patch("abogen.utils.load_config", return_value={"use_gpu": False}) @patch("abogen.domain.pipeline_factory.select_device", return_value="cuda:0") def test_gpu_disabled_by_config(self, _sel, _cfg): assert resolve_device(use_gpu=True) == "cpu" class TestCreatePipelineForJob: @patch("abogen.domain.pipeline_factory.create_pipeline") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True) def test_supertonic_provider(self, _reg, mock_create): mock_create.return_value = MagicMock() result = create_pipeline_for_job("supertonic", "en", use_gpu=True) mock_create.assert_called_once_with("supertonic") assert result is mock_create.return_value @patch("abogen.domain.pipeline_factory.create_pipeline") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True) @patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu") def test_kokoro_provider(self, _dev, _reg, mock_create): mock_create.return_value = MagicMock() result = create_pipeline_for_job("kokoro", "en", use_gpu=False) mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu") assert result is mock_create.return_value @patch("abogen.domain.pipeline_factory.create_pipeline") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=False) @patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu") def test_unknown_provider_falls_back_to_kokoro(self, _dev, _reg, mock_create): mock_create.return_value = MagicMock() result = create_pipeline_for_job("unknown_provider", "en", use_gpu=False) mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu") @patch("abogen.domain.pipeline_factory.create_pipeline") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True) @patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu") def test_empty_provider_defaults_to_kokoro(self, _dev, _reg, mock_create): mock_create.return_value = MagicMock() result = create_pipeline_for_job("", "en", use_gpu=False) mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu") @patch("abogen.domain.pipeline_factory.create_pipeline") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=True) @patch("abogen.domain.pipeline_factory.resolve_device", return_value="cpu") def test_none_provider_defaults_to_kokoro(self, _dev, _reg, mock_create): mock_create.return_value = MagicMock() result = create_pipeline_for_job(None, "en", use_gpu=False) mock_create.assert_called_once_with("kokoro", lang_code="en", device="cpu") class TestDisposePipelines: def test_disposes_all_and_clears(self): p1 = MagicMock() p2 = MagicMock() pipelines = {"kokoro": p1, "supertonic": p2} dispose_pipelines(pipelines) p1.dispose.assert_called_once() p2.dispose.assert_called_once() assert pipelines == {} def test_handles_dispose_error(self): p1 = MagicMock() p1.dispose.side_effect = RuntimeError("boom") pipelines = {"kokoro": p1} dispose_pipelines(pipelines) assert pipelines == {} def test_empty_dict(self): pipelines = {} dispose_pipelines(pipelines) assert pipelines == {} class TestPipelinePool: @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") @patch("abogen.domain.pipeline_factory.initialize_voice_cache") def test_get_creates_and_caches(self, _cache, mock_create): mock_pipeline = MagicMock() mock_create.return_value = mock_pipeline pool = PipelinePool() result = pool.get("kokoro", "en", use_gpu=True) assert result is mock_pipeline mock_create.assert_called_once() result2 = pool.get("kokoro", "en", use_gpu=True) assert result2 is mock_pipeline 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_get_initializes_voice_cache_once(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.get("kokoro", "en", use_gpu=True, job=job) assert mock_cache.call_count == 1 @patch("abogen.domain.pipeline_factory.initialize_voice_cache") @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") def test_get_no_job_skips_voice_cache(self, mock_create, mock_cache): mock_create.return_value = MagicMock() pool = PipelinePool() pool.get("kokoro", "en", use_gpu=True) mock_cache.assert_not_called() @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") def test_get_separately_per_provider(self, 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 assert mock_create.call_count == 2 @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") @patch("abogen.domain.pipeline_factory.initialize_voice_cache") def test_dispose_all(self, mock_cache, mock_create): p1 = MagicMock(name="kokoro") p2 = MagicMock(name="supertonic") 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 == {} assert pool._voice_cache_initialized is False @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") def test_dispose_empty_pool(self, mock_create): pool = PipelinePool() pool.dispose_all() mock_create.assert_not_called() @patch("abogen.domain.pipeline_factory.create_pipeline_for_job") @patch("abogen.domain.pipeline_factory.initialize_voice_cache") @patch("abogen.domain.pipeline_factory.is_plugin_registered", return_value=False) def test_unknown_provider_falls_back(self, _reg, _cache, mock_create): mock_create.return_value = MagicMock() pool = PipelinePool() pool.get("bogus_provider", "en", use_gpu=True) mock_create.assert_called_once_with("kokoro", "en", True)