From 775b716d86f5e681807698522e56c3d0f861c5bf Mon Sep 17 00:00:00 2001 From: historia Date: Tue, 25 Aug 2026 16:49:36 -0400 Subject: feat: combine --speaker and --voice for clarity --- app/tests/test_tts.py | 63 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 25 deletions(-) (limited to 'app/tests/test_tts.py') diff --git a/app/tests/test_tts.py b/app/tests/test_tts.py index 0e35f78..c17609b 100644 --- a/app/tests/test_tts.py +++ b/app/tests/test_tts.py @@ -512,12 +512,11 @@ class AudioCppTTSClientHealthTests(unittest.TestCase): raise AssertionError(f"unexpected URL: {url}") return _dispatch - def _client(self, voice=None, language=None, model_id=None, speaker=None, - **kwargs): + def _client(self, voice=None, language=None, model_id=None, **kwargs): with patch("converter.tts.urllib.request.urlopen", side_effect=self._get_responses(**kwargs)): return AudioCppTTSClient(voice=voice, language=language, - model_id=model_id, speaker=speaker) + model_id=model_id) def test_unreachable_server_raises_with_readme_pointer(self): import urllib.error @@ -549,34 +548,51 @@ class AudioCppTTSClientHealthTests(unittest.TestCase): self.assertEqual(client.language, config.LANGUAGE) self.assertEqual(client.voice, "Vivian") self.assertFalse(client.preset_mode) + self.assertTrue(client.speaker_mode) def test_speaker_mode_uses_configured_speaker(self): with patch.object(config, "SPEAKER", "uncle_fu"): client = self._client() self.assertEqual(client.voice, "Uncle Fu") + self.assertTrue(client.speaker_mode) - def test_explicit_speaker_selects_speaker_mode(self): - # --speaker picks a CustomVoice speaker; the name is normalized to - # its wire (display) form and no preset validation runs. - client = self._client(speaker="Uncle_Fu") + def test_voice_speaker_name_selects_speaker_mode(self): + # --voice naming a built-in CustomVoice speaker selects speaker + # mode; the name is normalized to its wire (display) form and no + # preset validation runs. + client = self._client(voice="Uncle_Fu") self.assertEqual(client.voice, "Uncle Fu") self.assertFalse(client.preset_mode) + self.assertTrue(client.speaker_mode) - def test_explicit_speaker_on_clone_entry_raises(self): - # --speaker is meaningless on a clone-only (Base) entry. + def test_voice_speaker_name_on_clone_entry_is_a_preset(self): + # --voice on a clone-only (Base) entry is a server-side preset, + # not a built-in speaker, so the name is validated against the + # server's voice library. with self.assertRaises(RuntimeError) as ctx: - self._client(speaker="Ryan", model_id="Qwen3-TTS-12Hz-1.7B-Base-GGUF", + self._client(voice="Ryan", model_id="Qwen3-TTS-12Hz-1.7B-Base-GGUF", models={"data": [ {"id": "Qwen3-TTS-12Hz-1.7B-Base-GGUF", "family": "qwen3_tts"}]}) message = str(ctx.exception) - self.assertIn("no built-in speakers", message) + self.assertIn("'Ryan'", message) self.assertIn("--voice", message) - def test_voice_and_speaker_are_mutually_exclusive(self): - with self.assertRaises(ValueError) as ctx: - AudioCppTTSClient(voice="narrator", speaker="Ryan") - self.assertIn("mutually exclusive", str(ctx.exception)) + def test_voice_speaker_name_does_not_reroute_to_clone_model(self): + # A built-in speaker name on a CustomVoice primary selects speaker + # mode without the AUDIOCPP_CLONE_MODEL_ID reroute. + with patch.object(config, "AUDIOCPP_MODEL_ID", + "Qwen3-TTS-CustomVoice"), \ + patch.object(config, "AUDIOCPP_CLONE_MODEL_ID", "qwen3-tts-clone"): + client = self._client( + voice="Ryan", + models={"data": [{"id": "Qwen3-TTS-CustomVoice", + "family": "qwen3_tts"}, + {"id": "qwen3-tts-clone", + "family": "qwen3_tts"}]}) + self.assertEqual(client.model_id, "Qwen3-TTS-CustomVoice") + self.assertTrue(client.speaker_mode) + self.assertFalse(client.preset_mode) def test_no_voice_on_base_entry_raises_instead_of_silent_speaker(self): # The Base model has no built-in speakers: without --voice the run @@ -1063,7 +1079,6 @@ class AudioCppTTSClientRequestTests(unittest.TestCase): client.model_id = config.AUDIOCPP_MODEL_ID client.preset_mode = preset_mode client.voice = voice - client.speaker = None client.language = language client._seed = seed client.family = family @@ -1518,8 +1533,8 @@ class AudioCppUnloadModelsTests(unittest.TestCase): client.profile = tts.AUDIOCPP_FAMILY_PROFILES["qwen3_tts"] client.design_mode = False client.instruction_voice = False + client.speaker_mode = False client.instructions = "" - client.speaker = None with patch.object(client, "_check_health"), \ patch.object(client, "_list_models", return_value=[{"id": client.model_id, @@ -1548,8 +1563,8 @@ class AudioCppUnloadModelsTests(unittest.TestCase): client.profile = tts.AUDIOCPP_FAMILY_PROFILES["qwen3_tts"] client.design_mode = False client.instruction_voice = False + client.speaker_mode = False client.instructions = "" - client.speaker = None with patch.object(client, "_check_health"), \ patch.object(client, "_list_models", return_value=[{"id": client.model_id, @@ -1591,8 +1606,7 @@ class BackendWiringTests(unittest.TestCase): model_id=None, instructions=None, request_options={}, - api_url=None, - speaker=None) + api_url=None) mock_faster.assert_not_called() mock_qwen.assert_not_called() @@ -1604,8 +1618,7 @@ class BackendWiringTests(unittest.TestCase): model_id=None, instructions=None, request_options={}, - api_url=None, - speaker=None) + api_url=None) def test_audiocpp_backend_model_id_is_wired_through(self): with patch("converter.converter.AudioCppTTSClient") as mock_audiocpp: @@ -1615,7 +1628,7 @@ class BackendWiringTests(unittest.TestCase): mock_audiocpp.assert_called_once_with( voice="narrator", language=config.LANGUAGE, model_id="higgs", instructions=None, - request_options={}, api_url=None, speaker=None) + request_options={}, api_url=None) def test_audiocpp_backend_instructions_and_options_are_wired_through(self): with patch("converter.converter.AudioCppTTSClient") as mock_audiocpp: @@ -1629,7 +1642,7 @@ class BackendWiringTests(unittest.TestCase): model_id=None, instructions="A warm adult narrator", request_options={"emotion": "neutral", "speed": "1.1"}, - api_url=None, speaker=None) + api_url=None) def test_qwen_backend_uses_qwen_client(self): with patch("converter.converter.FasterTTSClient") as mock_faster, \ @@ -1656,7 +1669,7 @@ class BackendWiringTests(unittest.TestCase): mock_audiocpp.assert_called_once_with( voice="narrator", language=config.LANGUAGE, model_id=None, instructions=None, request_options={}, - api_url="http://10.0.0.5:8080", speaker=None) + api_url="http://10.0.0.5:8080") with patch("converter.converter.FasterTTSClient") as mock_faster: AudiobookConverter(voice_mode=tts.VOICE_MODE_CLONE, backend=tts.BACKEND_FASTER, voice="narrator", -- cgit v1.2.3