aboutsummaryrefslogtreecommitdiff
path: root/app/tests
diff options
context:
space:
mode:
Diffstat (limited to 'app/tests')
-rw-r--r--app/tests/test_backends.py19
-rw-r--r--app/tests/test_backends_envs.py84
-rw-r--r--app/tests/test_backends_faster.py9
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()