diff options
| author | historia <historiavg@proton.me> | 2026-08-25 16:49:36 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-25 16:49:36 -0400 |
| commit | 775b716d86f5e681807698522e56c3d0f861c5bf (patch) | |
| tree | 420b31c7b2f743ee83ad933bbc00073d5f6b9e2a /app/tests | |
| parent | 867866f131b0b6c76c54272791e7f7dea01db990 (diff) | |
| download | tts-audiobook-generator-775b716d86f5e681807698522e56c3d0f861c5bf.tar.gz | |
feat: combine --speaker and --voice for clarity
Diffstat (limited to 'app/tests')
| -rw-r--r-- | app/tests/test_converter.py | 9 | ||||
| -rw-r--r-- | app/tests/test_hub.py | 16 | ||||
| -rw-r--r-- | app/tests/test_tts.py | 63 |
3 files changed, 49 insertions, 39 deletions
diff --git a/app/tests/test_converter.py b/app/tests/test_converter.py index 317e772..7aa9c69 100644 --- a/app/tests/test_converter.py +++ b/app/tests/test_converter.py @@ -131,7 +131,6 @@ class NarratorTagTests(unittest.TestCase): converter.voice_clone_ref_audio = ref_audio converter.backend = tts.BACKEND_QWEN converter.voice = None - converter.speaker = None converter.instructions = instructions return converter @@ -160,12 +159,11 @@ class NarratorTagTests(unittest.TestCase): self.assertEqual(self._converter(tts.VOICE_MODE_CLONE, "/x/???.wav")._narrator_tag(), "narrator") - def _audiocpp_converter(self, voice=None, instructions=None, speaker=None): + def _audiocpp_converter(self, voice=None, instructions=None): converter = self._converter(tts.VOICE_MODE_CUSTOM, instructions=instructions) converter.backend = tts.BACKEND_AUDIOCPP converter.voice = voice - converter.speaker = speaker return converter def test_audiocpp_design_run_uses_designed_tag(self): @@ -185,11 +183,11 @@ class NarratorTagTests(unittest.TestCase): def test_audiocpp_explicit_speaker_uses_speaker_tag(self): # A chosen CustomVoice speaker names the output, not config.SPEAKER. - converter = self._audiocpp_converter(speaker="Ryan") + converter = self._audiocpp_converter(voice="Ryan") self.assertEqual(converter._narrator_tag(), "Ryan") def test_audiocpp_explicit_speaker_normalizes_display_name(self): - converter = self._audiocpp_converter(speaker="Uncle_Fu") + converter = self._audiocpp_converter(voice="Uncle_Fu") self.assertEqual(converter._narrator_tag(), "Uncle_Fu") def test_preflight_design_run_uses_designed_tag(self): @@ -558,7 +556,6 @@ class RunOverwritePromptTests(unittest.TestCase): self.converter.voice_clone_ref_audio = None self.converter.backend = tts.BACKEND_QWEN self.converter.voice = None - self.converter.speaker = None self.converter.instructions = None self.converter.speed = 1.0 self.converter.single_file = False diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index c5ca346..965cbaa 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -754,7 +754,8 @@ class ConvertFlowTests(unittest.TestCase): def test_audiocpp_customvoice_entry_lists_builtin_speakers(self): # A CustomVoice entry populates the Voice menu with the Qwen3-TTS - # built-in speakers and maps the pick to --speaker. + # built-in speakers and maps the pick to --voice (which the client + # resolves as speaker mode). self._patch_remote( [{"id": "Qwen3-TTS-12Hz-1.7B-CustomVoice-GGUF", "family": "qwen3_tts", "task": "tts"}]) @@ -765,9 +766,9 @@ class ConvertFlowTests(unittest.TestCase): audiocpp_voice="Ryan", instructions="") cmd = self._convert( None, [self._remote("audiocpp", "audio.cpp")]) - # The speaker is passed as --speaker, not --voice. - self.assertIsNone(cmd[2]["voice"]) - self.assertEqual(cmd[2]["speaker"], "Ryan") + # The picked speaker is passed as --voice; no separate speaker kwarg. + self.assertEqual(cmd[2]["voice"], "Ryan") + self.assertNotIn("speaker", cmd[2]) fields = self.tui.forms_seen[0][1] voice_field = self._field("audiocpp_voice") self.assertEqual(voice_field["choices"](fields), @@ -792,7 +793,7 @@ class ConvertFlowTests(unittest.TestCase): cmd = self._convert( None, [self._remote("audiocpp", "audio.cpp")]) self.assertEqual(cmd[2]["voice"], "narrator") - self.assertIsNone(cmd[2]["speaker"]) + self.assertNotIn("speaker", cmd[2]) self.assertIsNone(cmd[2]["instructions"]) fields = self.tui.forms_seen[0][1] voice_field = self._field("audiocpp_voice") @@ -815,7 +816,7 @@ class ConvertFlowTests(unittest.TestCase): None, [self._remote("audiocpp", "audio.cpp")]) self.assertIsNotNone(cmd) self.assertIsNone(cmd[2]["voice"]) - self.assertIsNone(cmd[2]["speaker"]) + self.assertNotIn("speaker", cmd[2]) voice_field = self._field("audiocpp_voice") # Clone-only: an empty voice is refused (no built-in speaker option). self.assertIsNotNone(voice_field["validate"]("")) @@ -948,8 +949,7 @@ class ConvertFlowTests(unittest.TestCase): return_value=root), \ patch.object(hub.config, "AUDIOCPP_INSTRUCTIONS", ""): self._answer_form(backend="audiocpp", model_id="qwen", - audiocpp_voice="(built-in speaker)", - instructions="") + audiocpp_voice="", instructions="") cmd = self._convert(None, [ self._ready("audiocpp", "audio.cpp"), self._remote("audiocpp", "audio.cpp")]) 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", |
