aboutsummaryrefslogtreecommitdiff
path: root/tests/test_backends_faster.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_backends_faster.py')
-rw-r--r--tests/test_backends_faster.py172
1 files changed, 172 insertions, 0 deletions
diff --git a/tests/test_backends_faster.py b/tests/test_backends_faster.py
new file mode 100644
index 0000000..641f6ee
--- /dev/null
+++ b/tests/test_backends_faster.py
@@ -0,0 +1,172 @@
+"""Tests for the faster-qwen3-tts backend setup module (backends/faster.py)."""
+
+import json
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+from unittest.mock import patch
+
+from backends import faster as make_voices
+
+
+class FindWavFilesTests(unittest.TestCase):
+ def setUp(self):
+ self._tmp = tempfile.TemporaryDirectory()
+ self.folder = Path(self._tmp.name)
+
+ def tearDown(self):
+ self._tmp.cleanup()
+
+ def _touch(self, name):
+ path = self.folder / name
+ path.write_bytes(b"x")
+ return path
+
+ def test_finds_only_wavs_case_insensitive(self):
+ self._touch("b.wav")
+ self._touch("a.WAV")
+ self._touch("notes.txt")
+ (self.folder / "sub").mkdir()
+ (self.folder / "sub" / "c.wav").write_bytes(b"x")
+ names = [path.name for path in make_voices.find_wav_files(self.folder)]
+ self.assertEqual(names, ["a.WAV", "b.wav"])
+
+ def test_sorted_alphabetically_case_insensitive(self):
+ for name in ("Zed.wav", "alpha.wav", "Beta.wav"):
+ self._touch(name)
+ names = [path.name for path in make_voices.find_wav_files(self.folder)]
+ self.assertEqual(names, ["alpha.wav", "Beta.wav", "Zed.wav"])
+
+ def test_empty_directory_returns_empty_list(self):
+ self.assertEqual(make_voices.find_wav_files(self.folder), [])
+
+
+class BuildVoicesTests(unittest.TestCase):
+ def setUp(self):
+ self._tmp = tempfile.TemporaryDirectory()
+ self.folder = Path(self._tmp.name)
+ self.narrator = self.folder / "narrator.wav"
+ self.narrator.write_bytes(b"x")
+ self.other = self.folder / "other.wav"
+ self.other.write_bytes(b"x")
+
+ def tearDown(self):
+ self._tmp.cleanup()
+
+ def test_voices_named_after_basenames_with_absolute_paths(self):
+ transcripts = {str(self.narrator): "First transcript.",
+ str(self.other): "Second transcript."}
+ with patch.object(make_voices, "transcribe_reference_audio",
+ side_effect=lambda path, model_name="base": transcripts[path]):
+ voices = make_voices.build_voices([self.narrator, self.other],
+ "English", "base")
+ self.assertEqual(list(voices), ["narrator", "other"])
+ self.assertEqual(voices["narrator"]["ref_text"], "First transcript.")
+ self.assertEqual(voices["narrator"]["language"], "English")
+ self.assertTrue(Path(voices["narrator"]["ref_audio"]).is_absolute())
+ self.assertEqual(Path(voices["narrator"]["ref_audio"]), self.narrator.resolve())
+
+ def test_failed_transcription_keeps_entry_with_empty_text(self):
+ with patch.object(make_voices, "transcribe_reference_audio",
+ return_value=None):
+ voices = make_voices.build_voices([self.narrator], "English", "base")
+ self.assertEqual(voices["narrator"]["ref_text"], "")
+
+ def test_whisper_model_name_is_passed_through(self):
+ with patch.object(make_voices, "transcribe_reference_audio",
+ return_value="text") as mock_transcribe:
+ make_voices.build_voices([self.narrator], "English", "large-v3")
+ self.assertEqual(mock_transcribe.call_args.kwargs["model_name"], "large-v3")
+
+
+class MainTests(unittest.TestCase):
+ """The flag-only (non-TUI) path through main(), end to end."""
+
+ def setUp(self):
+ self._tmp = tempfile.TemporaryDirectory()
+ self.folder = Path(self._tmp.name)
+ (self.folder / "narrator.wav").write_bytes(b"x")
+ (self.folder / "alpha.wav").write_bytes(b"x")
+ self.output = self.folder / "voices.json"
+ # Avoid touching the real converter/config.py and pip/git.
+ patcher = patch.object(make_voices.common, "update_config_value",
+ return_value=False)
+ patcher.start()
+ self.addCleanup(patcher.stop)
+ patcher = patch.object(make_voices, "_interactive", return_value=False)
+ patcher.start()
+ self.addCleanup(patcher.stop)
+
+ def tearDown(self):
+ self._tmp.cleanup()
+
+ def _run(self, argv):
+ with patch.object(sys, "argv", ["backends/faster.py"] + argv), \
+ patch.object(make_voices, "transcribe_reference_audio",
+ return_value="hello"):
+ return make_voices.main()
+
+ def test_writes_json_with_alphabetical_voice_order(self):
+ exit_code = self._run([str(self.folder), "--output", str(self.output),
+ "--skip-install", "--skip-clone"])
+ self.assertEqual(exit_code, 0)
+ data = json.loads(self.output.read_text(encoding="utf-8"))
+ self.assertEqual(list(data), ["alpha", "narrator"])
+ self.assertEqual(data["alpha"]["ref_text"], "hello")
+ self.assertEqual(data["alpha"]["language"], "English")
+
+ def test_custom_output_path(self):
+ custom = Path(self._tmp.name) / "custom.json"
+ exit_code = self._run([str(self.folder), "--output", str(custom),
+ "--skip-install", "--skip-clone"])
+ self.assertEqual(exit_code, 0)
+ self.assertTrue(custom.exists())
+ self.assertFalse(self.output.exists())
+
+ def test_invalid_language_errors_before_work(self):
+ with patch.object(make_voices, "transcribe_reference_audio") as mock_transcribe:
+ with self.assertRaises(SystemExit) as ctx:
+ self._run([str(self.folder), "--output", str(self.output),
+ "--language", "klingon", "--skip-install",
+ "--skip-clone"])
+ self.assertEqual(ctx.exception.code, 2)
+ mock_transcribe.assert_not_called()
+
+ def test_missing_input_dir_errors(self):
+ with self.assertRaises(SystemExit) as ctx:
+ self._run([str(self.folder / "nope"), "--output", str(self.output),
+ "--skip-install", "--skip-clone"])
+ self.assertEqual(ctx.exception.code, 2)
+
+ def test_no_wav_files_returns_error(self):
+ empty = Path(tempfile.mkdtemp())
+ try:
+ exit_code = self._run([str(empty), "--output",
+ str(empty / "voices.json"),
+ "--skip-install", "--skip-clone"])
+ self.assertEqual(exit_code, 1)
+ finally:
+ empty.rmdir()
+
+ def test_existing_output_declined_keeps_file(self):
+ self.output.write_text('{"old": true}', encoding="utf-8")
+ with patch.object(make_voices, "transcribe_reference_audio") as mock_transcribe:
+ exit_code = self._run([str(self.folder), "--output", str(self.output),
+ "--skip-install", "--skip-clone"])
+ self.assertEqual(exit_code, 1)
+ mock_transcribe.assert_not_called()
+ self.assertEqual(json.loads(self.output.read_text(encoding="utf-8")),
+ {"old": True})
+
+ def test_force_overwrites_without_prompt(self):
+ self.output.write_text('{"old": true}', encoding="utf-8")
+ exit_code = self._run([str(self.folder), "--output", str(self.output),
+ "--force", "--skip-install", "--skip-clone"])
+ self.assertEqual(exit_code, 0)
+ data = json.loads(self.output.read_text(encoding="utf-8"))
+ self.assertEqual(list(data), ["alpha", "narrator"])
+
+
+if __name__ == "__main__":
+ unittest.main()