diff options
Diffstat (limited to 'app/tests')
| -rw-r--r-- | app/tests/test_backends.py | 19 | ||||
| -rw-r--r-- | app/tests/test_backends_envs.py | 84 | ||||
| -rw-r--r-- | app/tests/test_backends_faster.py | 9 |
3 files changed, 101 insertions, 11 deletions
diff --git a/app/tests/test_backends.py b/app/tests/test_backends.py index 1e1a3f9..7fdcce6 100644 --- a/app/tests/test_backends.py +++ b/app/tests/test_backends.py @@ -420,7 +420,8 @@ class QwenUninstallWeightsTests(unittest.TestCase): return_value=0) as mk_pip: rc = qwen.uninstall(emit="EMIT") self.assertEqual(rc, 0) - mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit="EMIT") + mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit="EMIT", + env_dir=qwen.QWEN_ENV) self.assertEqual(list(Path(td).iterdir()), []) def test_weights_deleted_even_when_pip_failed(self): @@ -459,7 +460,8 @@ class QwenUninstallWeightsTests(unittest.TestCase): side_effect=pip_flips_cancel) as mk_pip: rc = qwen.uninstall(cancel=cancel) self.assertEqual(rc, 130) - mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit=None) + mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit=None, + env_dir=qwen.QWEN_ENV) # Cancelled between phases: the weights stay untouched... self.assertEqual(len(list(Path(td).iterdir())), 3) @@ -549,13 +551,14 @@ class QwenInstallModelTests(unittest.TestCase): def test_hf_cli_prefers_hf_then_falls_back(self): from backends import qwen with tempfile.TemporaryDirectory() as td: - with patch.object(qwen.envs, "ENV_DIR", Path(td)): + # The CLI is looked up in the qwen backend's own venv. + with patch.object(qwen, "QWEN_ENV", Path(td)): self.assertIsNone(qwen._hf_download_prefix()) - cli = qwen.envs.env_script("huggingface-cli") + cli = qwen.envs.env_script("huggingface-cli", qwen.QWEN_ENV) cli.parent.mkdir(parents=True) cli.write_bytes(b"x") self.assertEqual(qwen._hf_download_prefix(), [str(cli)]) - hf = qwen.envs.env_script("hf") + hf = qwen.envs.env_script("hf", qwen.QWEN_ENV) hf.write_bytes(b"x") self.assertEqual(qwen._hf_download_prefix(), [str(hf)]) @@ -827,8 +830,10 @@ class QwenUninstallTests(unittest.TestCase): self.assertEqual(rc, 0) self.assertEqual([c.args[0] for c in mk_stop.call_args_list], ["qwen"]) - # The task view's emit is forwarded so pip never touches the terminal. - mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit="EMIT") + # The task view's emit is forwarded so pip never touches the terminal, + # and the package comes out of the qwen backend's own venv. + mk_pip.assert_called_once_with([qwen.QWEN_PIP_PKG], emit="EMIT", + env_dir=qwen.QWEN_ENV) def test_skips_stop_when_no_server_was_started(self): # No pid files: stop() is not called (no "not started by this diff --git a/app/tests/test_backends_envs.py b/app/tests/test_backends_envs.py index 184a3b3..8b17458 100644 --- a/app/tests/test_backends_envs.py +++ b/app/tests/test_backends_envs.py @@ -16,25 +16,47 @@ class EnvPathTests(unittest.TestCase): self.assertEqual(envs.ENV_DIR.name, "tts") self.assertEqual(envs.ENV_DIR.parent.name, "envs") + def test_backend_env_dirs_are_separate_from_the_app_env(self): + # Each pip-installed backend gets its own venv next to the app's: + # qwen-tts and faster-qwen3-tts both ship a qwen_tts module with + # conflicting transformers pins, so they must never share one. + self.assertEqual(envs.QWEN_ENV_DIR.name, "qwen") + self.assertEqual(envs.FASTER_ENV_DIR.name, "faster") + self.assertEqual(envs.QWEN_ENV_DIR.parent, envs.ENV_DIR.parent) + distinct = {envs.ENV_DIR, envs.QWEN_ENV_DIR, envs.FASTER_ENV_DIR} + self.assertEqual(len(distinct), 3) + def test_env_python_posix(self): with patch.object(envs, "_is_windows", return_value=False): self.assertEqual(envs.env_python(), envs.ENV_DIR / "bin" / "python") + self.assertEqual( + envs.env_python(envs.QWEN_ENV_DIR), + envs.QWEN_ENV_DIR / "bin" / "python") def test_env_python_windows(self): with patch.object(envs, "_is_windows", return_value=True): self.assertEqual(envs.env_python(), envs.ENV_DIR / "Scripts" / "python.exe") + self.assertEqual( + envs.env_python(envs.FASTER_ENV_DIR), + envs.FASTER_ENV_DIR / "Scripts" / "python.exe") def test_env_script_posix(self): with patch.object(envs, "_is_windows", return_value=False): self.assertEqual(envs.env_script("qwen-tts-demo"), envs.ENV_DIR / "bin" / "qwen-tts-demo") + self.assertEqual( + envs.env_script("qwen-tts-demo", envs.QWEN_ENV_DIR), + envs.QWEN_ENV_DIR / "bin" / "qwen-tts-demo") def test_env_script_windows(self): with patch.object(envs, "_is_windows", return_value=True): self.assertEqual(envs.env_script("qwen-tts-demo"), envs.ENV_DIR / "Scripts" / "qwen-tts-demo.exe") + self.assertEqual( + envs.env_script("qwen-tts-demo", envs.QWEN_ENV_DIR), + envs.QWEN_ENV_DIR / "Scripts" / "qwen-tts-demo.exe") def test_env_exists_false_when_python_missing(self): with patch.object(envs, "env_python", @@ -63,6 +85,13 @@ class CreateEnvTests(unittest.TestCase): self.assertEqual(argv[2], "venv") self.assertEqual(argv[3], str(envs.ENV_DIR)) + def test_create_env_targets_the_requested_env_dir(self): + with patch.object(envs.common, "run_console_subprocess", + return_value=0) as run: + envs.create_env(envs.FASTER_ENV_DIR) + argv = run.call_args[0][0] + self.assertEqual(argv[3], str(envs.FASTER_ENV_DIR)) + def test_create_env_reports_remediation_on_failure(self): with patch.object(envs.common, "run_console_subprocess", return_value=1): @@ -84,12 +113,31 @@ class PipInstallTests(unittest.TestCase): side_effect=fake_run): rc = envs.pip_install(["qwen-tts"]) self.assertEqual(rc, 0) - mk.assert_called_once_with() + mk.assert_called_once_with(None) # The actual pip call targets the venv's python. self.assertEqual(calls[0][0], str(envs.env_python())) self.assertIn("pip", calls[0]) self.assertIn("qwen-tts", calls[0]) + def test_backend_env_targets_the_backend_python_and_env(self): + calls = [] + + def fake_run(argv, **kwargs): + calls.append(list(argv)) + return 0 + + with patch.object(envs, "env_exists", return_value=False), \ + patch.object(envs, "create_env", return_value=0) as mk, \ + patch.object(envs.common, "run_console_subprocess", + side_effect=fake_run): + rc = envs.pip_install(["qwen-tts"], env_dir=envs.QWEN_ENV_DIR) + self.assertEqual(rc, 0) + # Both create-if-missing and pip itself are scoped to the qwen env; + # the app env is never touched. + mk.assert_called_once_with(envs.QWEN_ENV_DIR) + self.assertEqual(calls[0][0], + str(envs.env_python(envs.QWEN_ENV_DIR))) + def test_skips_create_when_env_exists(self): with patch.object(envs, "env_exists", return_value=True), \ patch.object(envs, "create_env") as mk, \ @@ -115,6 +163,13 @@ class PipUninstallTests(unittest.TestCase): self.assertEqual(rc, 0) run.assert_not_called() + def test_missing_backend_env_is_also_a_noop(self): + with patch.object(envs, "env_exists", return_value=False), \ + patch.object(envs.common, "run_console_subprocess") as run: + rc = envs.pip_uninstall(["qwen-tts"], env_dir=envs.QWEN_ENV_DIR) + self.assertEqual(rc, 0) + run.assert_not_called() + def test_runs_pip_uninstall_against_the_venv_python(self): calls = [] @@ -133,6 +188,21 @@ class PipUninstallTests(unittest.TestCase): self.assertIn("-y", calls[0]) self.assertIn("qwen-tts", calls[0]) + def test_targets_the_requested_env_python(self): + calls = [] + + def fake_run(argv, **kwargs): + calls.append(list(argv)) + return 0 + + with patch.object(envs, "env_exists", return_value=True), \ + patch.object(envs.common, "run_console_subprocess", + side_effect=fake_run): + envs.pip_uninstall(["faster-qwen3-tts"], + env_dir=envs.FASTER_ENV_DIR) + self.assertEqual(calls[0][0], + str(envs.env_python(envs.FASTER_ENV_DIR))) + def test_streams_to_emit_when_given(self): with patch.object(envs, "env_exists", return_value=True), \ patch.object(envs.common, "run_console_subprocess", @@ -167,6 +237,18 @@ class ModuleAvailableTests(unittest.TestCase): self.assertEqual(argv[0], str(envs.env_python())) self.assertIn("import qwen_tts", argv[2]) + def test_probes_the_requested_env_interpreter(self): + import subprocess + fake = subprocess.CompletedProcess(args=["x"], returncode=0) + with patch.object(envs, "env_exists", return_value=True), \ + patch("subprocess.run", return_value=fake) as run: + self.assertTrue( + envs.module_available("qwen_tts", envs.QWEN_ENV_DIR)) + argv = run.call_args[0][0] + self.assertEqual(argv[0], + str(envs.env_python(envs.QWEN_ENV_DIR))) + self.assertIn("import qwen_tts", argv[2]) + def test_false_when_subprocess_exits_nonzero(self): import subprocess fake = subprocess.CompletedProcess(args=["x"], returncode=1) diff --git a/app/tests/test_backends_faster.py b/app/tests/test_backends_faster.py index a093fbe..8f024bf 100644 --- a/app/tests/test_backends_faster.py +++ b/app/tests/test_backends_faster.py @@ -419,8 +419,10 @@ class UninstallTests(unittest.TestCase): rc = make_voices.uninstall(emit="EMIT") self.assertEqual(rc, 0) mk_stop.assert_called_once_with("faster") - # The task view's emit is forwarded so pip never touches the terminal. - mk_pip.assert_called_once_with(["faster-qwen3-tts"], emit="EMIT") + # The task view's emit is forwarded so pip never touches the terminal, + # and the package comes out of the faster backend's own venv. + mk_pip.assert_called_once_with(["faster-qwen3-tts"], emit="EMIT", + env_dir=make_voices.FASTER_ENV) self.assertFalse(checkout.exists()) def test_no_checkout_still_uninstalls_the_package(self): @@ -435,7 +437,8 @@ class UninstallTests(unittest.TestCase): self.assertEqual(rc, 0) # No pid file: no stop attempt (and no noise about it). mk_stop.assert_not_called() - mk_pip.assert_called_once_with(["faster-qwen3-tts"], emit=None) + mk_pip.assert_called_once_with(["faster-qwen3-tts"], emit=None, + env_dir=make_voices.FASTER_ENV) def test_cancel_before_pip_skips_everything_after_stopping(self): cancel = threading.Event() |
