aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_backends_audiocpp.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-28 18:18:47 -0400
committerhistoria <historiavg@proton.me>2026-08-28 18:18:47 -0400
commit1487796d74a7b171f2c53c4212e446759d3a4bec (patch)
treebe0546fe36404a7bc6b98c1a472af574f4b28868 /app/tests/test_backends_audiocpp.py
parente66eb0e7d4342ae1c58e9bbd341843753be548f0 (diff)
downloadtts-audiobook-generator-1487796d74a7b171f2c53c4212e446759d3a4bec.tar.gz
feat: offer to download prebuilt mac/win binaries, new macos build fallback if xcode is not installed
Diffstat (limited to 'app/tests/test_backends_audiocpp.py')
-rw-r--r--app/tests/test_backends_audiocpp.py521
1 files changed, 520 insertions, 1 deletions
diff --git a/app/tests/test_backends_audiocpp.py b/app/tests/test_backends_audiocpp.py
index 7f14b68..4c00001 100644
--- a/app/tests/test_backends_audiocpp.py
+++ b/app/tests/test_backends_audiocpp.py
@@ -1,14 +1,19 @@
"""Tests for the audio.cpp backend setup module (backends/audiocpp.py)."""
import argparse
+import hashlib
import io
import json
+import shutil
import sys
+import tarfile
import tempfile
import threading
import unittest
+import zipfile
from contextlib import redirect_stdout
from pathlib import Path
+from typing import Optional
from unittest.mock import MagicMock, patch
from converter import config
@@ -574,7 +579,7 @@ class BackendOptionsTests(unittest.TestCase):
with patch("sys.platform", "darwin"):
options, default_index = make_server.catalog._backend_options()
self.assertEqual(options,
- [("cpu - Apple Metal (recorded as cpu)", "cpu")])
+ [("cpu - Apple Metal", "cpu")])
self.assertEqual(default_index, 0)
def test_darwin_marks_detected_cpu(self):
@@ -1299,6 +1304,8 @@ class BuildAudiocppTests(unittest.TestCase):
def test_darwin_uses_metal_script_without_backend(self):
with patch.object(common, "run_console_subprocess",
return_value=0) as run, \
+ patch.object(make_server.build, "_metal_compiler_available",
+ return_value=True), \
patch("sys.platform", "darwin"):
rc = make_server.build.build_audiocpp(self.checkout, "cpu")
self.assertEqual(rc, 0)
@@ -1311,9 +1318,46 @@ class BuildAudiocppTests(unittest.TestCase):
self.assertIn("--deployment-build", argv)
self.assertEqual(run.call_args[1]["cwd"], self.checkout)
+ def test_darwin_without_metal_compiler_runs_cmake_directly(self):
+ # No Xcode (no offline Metal compiler): build_metal.sh would abort
+ # at its probe, so the build must fall back to a direct cmake
+ # invocation with the Metal shaders embedded as source — which
+ # needs only the Command Line Tools.
+ with patch.object(common, "run_console_subprocess",
+ return_value=0) as run, \
+ patch.object(make_server.build, "_metal_compiler_available",
+ return_value=False), \
+ patch("sys.platform", "darwin"):
+ rc = make_server.build.build_audiocpp(self.checkout, "cpu")
+ self.assertEqual(rc, 0)
+ argv = run.call_args[0][0]
+ self.assertEqual(argv[:2], ["bash", "-c"])
+ script = argv[2]
+ self.assertIn("cmake -S . -B build/macos-metal-release", script)
+ self.assertIn("-DENGINE_ENABLE_METAL=ON", script)
+ self.assertIn("-DGGML_METAL_EMBED_LIBRARY=ON", script)
+ self.assertIn("-DAUDIOCPP_DEPLOYMENT_BUILD=ON", script)
+ self.assertIn("cmake --build build/macos-metal-release", script)
+ self.assertIn("--target audiocpp_server", script)
+ self.assertNotIn("build_metal.sh", script)
+ self.assertEqual(run.call_args[1]["cwd"], self.checkout)
+
+ def test_metal_compiler_probe(self):
+ # The probe mirrors build_metal.sh's own check: a failing (or
+ # missing) xcrun metal means the offline compiler is absent.
+ with patch.object(common, "run_console_subprocess_quiet",
+ return_value=None):
+ self.assertFalse(
+ make_server.build._metal_compiler_available())
+ with patch.object(common, "run_console_subprocess_quiet",
+ return_value=MagicMock(returncode=0)):
+ self.assertTrue(make_server.build._metal_compiler_available())
+
def test_darwin_cuda_backend_gets_no_arch_flags(self):
with patch.object(common, "run_console_subprocess",
return_value=0) as run, \
+ patch.object(make_server.build, "_metal_compiler_available",
+ return_value=True), \
patch("sys.platform", "darwin"):
rc = make_server.build.build_audiocpp(self.checkout, "cuda")
self.assertEqual(rc, 0)
@@ -3087,3 +3131,478 @@ class LaunchHintTests(unittest.TestCase):
"-Target audiocpp_server -DeploymentBuild", out)
self.assertIn("windows-cpu-release", out)
self.assertIn("build_windows_hip.ps1", out)
+
+
+def _asset(name, size=1234, digest=None):
+ """A GitHub release asset entry shaped like the API returns."""
+ return {"name": name, "size": size, "digest": digest,
+ "browser_download_url": f"https://example.test/{name}"}
+
+
+def _release(assets, tag="v9.9.9"):
+ return {"tag_name": tag, "assets": assets}
+
+
+def _sha256_file(path: Path) -> str:
+ return hashlib.sha256(path.read_bytes()).hexdigest()
+
+
+def _make_tar_gz(tmp: Path, name: str, entries: dict,
+ mode: int = 0o755) -> Path:
+ archive = tmp / name
+ with tarfile.open(archive, "w:gz") as tf:
+ for member, content in entries.items():
+ data = content.encode("utf-8")
+ info = tarfile.TarInfo(member)
+ info.size = len(data)
+ info.mode = mode
+ tf.addfile(info, io.BytesIO(data))
+ return archive
+
+
+def _make_zip(tmp: Path, name: str, entries: dict) -> Path:
+ archive = tmp / name
+ with zipfile.ZipFile(archive, "w") as zf:
+ for member, content in entries.items():
+ zf.writestr(member, content)
+ return archive
+
+
+class PrebuiltSelectTests(unittest.TestCase):
+ """Asset selection for the prebuilt release downloads."""
+
+ ASSETS = [
+ _asset("audio-v9.9.9-bin-macos-arm64-metal.tar.gz"),
+ _asset("audio-v9.9.9-bin-macos-x64-metal.tar.gz"),
+ _asset("audio-v9.9.9-bin-windows-x64-cpu.zip"),
+ _asset("audio-v9.9.9-bin-windows-x64-vulkan.zip"),
+ _asset("audio-v9.9.9-bin-windows-x64-cuda12.4.zip"),
+ _asset("audio-v9.9.9-bin-windows-x64-cuda13.3.zip"),
+ _asset("audio-v9.9.9-cudart-windows-x64-cuda12.4.zip"),
+ _asset("audio-v9.9.9-cudart-windows-x64-cuda13.3.zip"),
+ _asset("audio-v9.9.9-bin-ubuntu-x64-cpu.tar.gz"),
+ ]
+
+ def test_darwin_arm64_selects_the_metal_tarball(self):
+ pair = make_server.prebuilt.select_assets(
+ self.ASSETS, "cpu", platform="darwin", machine="arm64")
+ self.assertEqual(
+ pair[0]["name"], "audio-v9.9.9-bin-macos-arm64-metal.tar.gz")
+ self.assertIsNone(pair[1])
+
+ def test_darwin_x64_selects_its_own_tarball(self):
+ pair = make_server.prebuilt.select_assets(
+ self.ASSETS, "cpu", platform="darwin", machine="x86_64")
+ self.assertEqual(
+ pair[0]["name"], "audio-v9.9.9-bin-macos-x64-metal.tar.gz")
+
+ def test_darwin_has_no_cuda_asset(self):
+ self.assertIsNone(make_server.prebuilt.select_assets(
+ self.ASSETS, "cuda", platform="darwin", machine="arm64"))
+
+ def test_windows_selects_cpu_and_vulkan_zips(self):
+ for backend, name in (("cpu", "audio-v9.9.9-bin-windows-x64-cpu.zip"),
+ ("vulkan",
+ "audio-v9.9.9-bin-windows-x64-vulkan.zip")):
+ pair = make_server.prebuilt.select_assets(
+ self.ASSETS, backend, platform="win32", machine="AMD64")
+ self.assertEqual(pair[0]["name"], name)
+ self.assertIsNone(pair[1])
+
+ def test_windows_cuda_pairs_the_binary_with_its_cudart(self):
+ pair = make_server.prebuilt.select_assets(
+ self.ASSETS, "cuda", platform="win32", machine="AMD64",
+ cuda_variant="12.4")
+ self.assertEqual(pair[0]["name"],
+ "audio-v9.9.9-bin-windows-x64-cuda12.4.zip")
+ self.assertEqual(pair[1]["name"],
+ "audio-v9.9.9-cudart-windows-x64-cuda12.4.zip")
+
+ def test_windows_cuda_without_cudart_asset_is_rejected(self):
+ assets = [_asset("audio-v9.9.9-bin-windows-x64-cuda12.4.zip")]
+ self.assertIsNone(make_server.prebuilt.select_assets(
+ assets, "cuda", platform="win32", machine="AMD64",
+ cuda_variant="12.4"))
+
+ def test_windows_hip_has_no_asset(self):
+ self.assertIsNone(make_server.prebuilt.select_assets(
+ self.ASSETS, "hip", platform="win32", machine="AMD64"))
+
+ def test_windows_arm64_has_no_asset(self):
+ self.assertIsNone(make_server.prebuilt.select_assets(
+ self.ASSETS, "cpu", platform="win32", machine="arm64"))
+
+ def test_linux_never_selects(self):
+ self.assertIsNone(make_server.prebuilt.select_assets(
+ self.ASSETS, "cuda", platform="linux", machine="x86_64"))
+
+ def test_prebuilt_supported_matrix(self):
+ supported = make_server.prebuilt.prebuilt_supported
+ self.assertTrue(supported("cpu", platform="darwin"))
+ self.assertFalse(supported("cuda", platform="darwin"))
+ self.assertTrue(supported("cpu", platform="win32"))
+ self.assertTrue(supported("vulkan", platform="win32"))
+ self.assertTrue(supported("cuda", platform="win32"))
+ self.assertFalse(supported("hip", platform="win32"))
+ self.assertFalse(supported("cuda", platform="linux"))
+
+ def test_default_cuda_variant_follows_the_driver(self):
+ def driver(version):
+ proc = MagicMock()
+ proc.returncode = 0
+ proc.stdout = f"{version}\n".encode("utf-8")
+ return proc
+
+ with patch.object(make_server.prebuilt.common,
+ "run_console_subprocess_quiet",
+ return_value=driver("580.82.07")):
+ self.assertEqual(
+ make_server.prebuilt._default_cuda_variant(), "13.3")
+ with patch.object(make_server.prebuilt.common,
+ "run_console_subprocess_quiet",
+ return_value=driver("579.10")):
+ self.assertEqual(
+ make_server.prebuilt._default_cuda_variant(), "12.4")
+ with patch.object(make_server.prebuilt.common,
+ "run_console_subprocess_quiet",
+ return_value=None):
+ self.assertEqual(
+ make_server.prebuilt._default_cuda_variant(), "12.4")
+
+
+class PrebuiltInstallTests(unittest.TestCase):
+ """install_prebuilt: download, verify, extract, mark."""
+
+ def _checkout(self) -> Path:
+ tmp = tempfile.TemporaryDirectory()
+ self.addCleanup(tmp.cleanup)
+ return _make_checkout(Path(tmp.name))
+
+ def _marker(self, checkout: Path, backend: str) -> Path:
+ return make_server.prebuilt.marker_path(checkout, backend,
+ platform="darwin")
+
+ def _install_darwin(self, checkout: Path, *, digest: str,
+ asset_name="audio-v9.9.9-bin-macos-x64-metal.tar.gz",
+ archive=None):
+ if archive is None:
+ archive = _make_tar_gz(
+ Path(self.tmp.name), asset_name,
+ {"./audiocpp_server": "#!/bin/sh\n",
+ "./tools/model_manager_v2.py": "# tool\n"})
+ asset = _asset(asset_name, size=archive.stat().st_size,
+ digest=f"sha256:{digest}")
+ release = _release([asset])
+
+ def fake_download(url, dest, *, emit=None, cancel=None):
+ self.assertEqual(url, asset["browser_download_url"])
+ shutil.copyfile(archive, dest)
+ return 0
+
+ with patch("sys.platform", "darwin"), \
+ patch.object(make_server.prebuilt, "fetch_latest_release",
+ return_value=release), \
+ patch.object(make_server.prebuilt, "_download",
+ side_effect=fake_download) as mk_dl, \
+ patch.object(make_server.prebuilt.common,
+ "run_console_subprocess", return_value=0):
+ rc = make_server.prebuilt.install_prebuilt(checkout, "cpu")
+ return rc, mk_dl
+
+ def setUp(self):
+ tmp = tempfile.TemporaryDirectory()
+ self.addCleanup(tmp.cleanup)
+ self.tmp = tmp
+
+ def test_darwin_install_extracts_and_marks(self):
+ checkout = self._checkout()
+ archive = _make_tar_gz(
+ Path(self.tmp.name), "audio-v9.9.9-bin-macos-x64-metal.tar.gz",
+ {"./audiocpp_server": "#!/bin/sh\n",
+ "./tools/model_manager_v2.py": "# tool\n"}, mode=0o644)
+ rc, _ = self._install_darwin(
+ checkout, digest=_sha256_file(archive), archive=archive)
+ self.assertEqual(rc, 0)
+ bin_dir = checkout / "build" / "macos-metal-release" / "bin"
+ server = bin_dir / "audiocpp_server"
+ self.assertTrue(server.exists())
+ # GitHub's artifact round-trip loses the tar permission bits; the
+ # install must restore the exec bit the hub relies on to launch.
+ self.assertTrue(server.stat().st_mode & 0o111)
+ self.assertTrue((bin_dir / "tools" / "model_manager_v2.py").exists())
+ marker = json.loads(
+ (checkout / "build" / "macos-metal-release" / "prebuilt.json")
+ .read_text(encoding="utf-8"))
+ self.assertEqual(marker["tag"], "v9.9.9")
+ self.assertEqual(marker["asset"],
+ "audio-v9.9.9-bin-macos-x64-metal.tar.gz")
+
+ def test_install_replaces_a_previous_build_dir(self):
+ checkout = self._checkout()
+ stale = checkout / "build" / "macos-metal-release" / "bin"
+ stale.mkdir(parents=True)
+ (stale / "junk.txt").write_text("stale", encoding="utf-8")
+ archive = _make_tar_gz(
+ Path(self.tmp.name), "audio-v9.9.9-bin-macos-x64-metal.tar.gz",
+ {"./audiocpp_server": "#!/bin/sh\n"})
+ rc, _ = self._install_darwin(
+ checkout, digest=_sha256_file(archive), archive=archive)
+ self.assertEqual(rc, 0)
+ self.assertFalse((stale / "junk.txt").exists())
+ self.assertTrue((stale / "audiocpp_server").exists())
+
+ def test_reinstall_of_the_same_release_is_a_noop(self):
+ checkout = self._checkout()
+ archive = _make_tar_gz(
+ Path(self.tmp.name), "audio-v9.9.9-bin-macos-x64-metal.tar.gz",
+ {"./audiocpp_server": "#!/bin/sh\n"})
+ rc, _ = self._install_darwin(
+ checkout, digest=_sha256_file(archive), archive=archive)
+ self.assertEqual(rc, 0)
+ rc, mk_dl = self._install_darwin(
+ checkout, digest=_sha256_file(archive), archive=archive)
+ self.assertEqual(rc, 0)
+ mk_dl.assert_not_called()
+
+ def test_checksum_mismatch_aborts_without_installing(self):
+ checkout = self._checkout()
+ rc, _ = self._install_darwin(checkout,
+ digest="sha256:" + "0" * 64)
+ self.assertEqual(rc, 1)
+ self.assertFalse(
+ (checkout / "build" / "macos-metal-release" / "bin"
+ / "audiocpp_server").exists())
+ self.assertIsNone(
+ make_server.prebuilt.installed_release(checkout, "cpu"))
+
+ def test_windows_cuda_installs_both_zips_into_the_preset_dir(self):
+ checkout = self._checkout()
+ main = _make_zip(
+ Path(self.tmp.name), "audio-v9.9.9-bin-windows-x64-cuda13.3.zip",
+ {"audiocpp_server.exe": "MZ",
+ "ggml-cuda.dll": "MZ"})
+ cudart = _make_zip(
+ Path(self.tmp.name), "audio-v9.9.9-cudart-windows-x64-cuda13.3.zip",
+ {"cudart64_13.dll": "MZ"})
+ release = _release([
+ _asset(main.name, size=main.stat().st_size,
+ digest=f"sha256:{_sha256_file(main)}"),
+ _asset(cudart.name, size=cudart.stat().st_size,
+ digest=f"sha256:{_sha256_file(cudart)}"),
+ ])
+ archives = {f"https://example.test/{p.name}": p
+ for p in (main, cudart)}
+
+ def fake_download(url, dest, *, emit=None, cancel=None):
+ shutil.copyfile(archives[url], dest)
+ return 0
+
+ with patch("sys.platform", "win32"), \
+ patch.object(make_server.prebuilt, "fetch_latest_release",
+ return_value=release), \
+ patch.object(make_server.prebuilt, "_download",
+ side_effect=fake_download), \
+ patch.object(make_server.prebuilt.common,
+ "run_console_subprocess", return_value=0):
+ rc = make_server.prebuilt.install_prebuilt(
+ checkout, "cuda", cuda_variant="13.3")
+ self.assertEqual(rc, 0)
+ bin_dir = checkout / "build" / "windows-cuda-release" / "bin"
+ self.assertTrue((bin_dir / "audiocpp_server.exe").exists())
+ self.assertTrue((bin_dir / "cudart64_13.dll").exists())
+ self.assertEqual(make_server.prebuilt.installed_release(
+ checkout, "cuda", platform="win32")["tag"], "v9.9.9")
+
+ def test_unsupported_backend_fails_loudly(self):
+ checkout = self._checkout()
+ with patch("sys.platform", "linux"):
+ rc = make_server.prebuilt.install_prebuilt(checkout, "cuda")
+ self.assertEqual(rc, 1)
+
+ def test_marker_roundtrip_and_garbage(self):
+ checkout = self._checkout()
+ self.assertIsNone(make_server.prebuilt.installed_release(
+ checkout, "cpu", platform="darwin"))
+ marker = self._marker(checkout, "cpu")
+ marker.parent.mkdir(parents=True)
+ marker.write_text(json.dumps({"tag": "v1.2.3"}),
+ encoding="utf-8")
+ self.assertEqual(make_server.prebuilt.installed_release(
+ checkout, "cpu", platform="darwin")["tag"], "v1.2.3")
+ marker.write_text("not json", encoding="utf-8")
+ self.assertIsNone(make_server.prebuilt.installed_release(
+ checkout, "cpu", platform="darwin"))
+
+ def test_install_dir_mirrors_the_source_build_layout(self):
+ checkout = Path("/tmp/acpp")
+ self.assertEqual(
+ make_server.prebuilt.install_dir(checkout, "cpu",
+ platform="darwin"),
+ checkout / "build" / "macos-metal-release" / "bin")
+ self.assertEqual(
+ make_server.prebuilt.install_dir(checkout, "cuda",
+ platform="win32"),
+ checkout / "build" / "windows-cuda-release" / "bin")
+ self.assertIsNone(
+ make_server.prebuilt.install_dir(checkout, "hip",
+ platform="win32"))
+ self.assertIsNone(
+ make_server.prebuilt.install_dir(checkout, "cpu",
+ platform="linux"))
+
+
+class PrebuiltUpdateRoutingTests(unittest.TestCase):
+ """update() routes prebuilt installs to a release re-download."""
+
+ def _checkout(self) -> Path:
+ tmp = tempfile.TemporaryDirectory()
+ self.addCleanup(tmp.cleanup)
+ return _make_checkout(Path(tmp.name))
+
+ def _mark_prebuilt(self, checkout: Path, tag: str) -> None:
+ marker = make_server.prebuilt.marker_path(checkout, "cpu",
+ platform="darwin")
+ marker.parent.mkdir(parents=True)
+ (marker.parent / "bin").mkdir(exist_ok=True)
+ marker.write_text(json.dumps({"tag": tag, "asset": "x"}),
+ encoding="utf-8")
+
+ def _run(self, checkout: Path, *, marker: Optional[dict],
+ release: Optional[dict]):
+ with patch("sys.platform", "darwin"), \
+ patch.object(make_server.build, "find_local_checkout",
+ return_value=checkout), \
+ patch.object(make_server.build.servers, "pid_for",
+ return_value=None), \
+ patch.object(make_server.build, "load_server_config",
+ return_value={"backend": "cpu"}), \
+ patch.object(make_server.prebuilt, "installed_release",
+ return_value=marker), \
+ patch.object(make_server.prebuilt, "fetch_latest_release",
+ return_value=release), \
+ patch.object(make_server.prebuilt, "install_prebuilt",
+ return_value=0) as mk_install, \
+ patch.object(common, "git_update",
+ return_value=0) as mk_git:
+ rc = make_server.build.update()
+ return rc, mk_install, mk_git
+
+ def test_newer_release_triggers_a_redownload(self):
+ checkout = self._checkout()
+ self._mark_prebuilt(checkout, "v0.6.0")
+ rc, mk_install, mk_git = self._run(
+ checkout, marker={"tag": "v0.6.0", "asset": "x"},
+ release=_release([], tag="v0.7.0"))
+ self.assertEqual(rc, 0)
+ mk_git.assert_not_called()
+ mk_install.assert_called_once_with(checkout, "cpu", emit=None,
+ cancel=None)
+
+ def test_current_release_is_a_noop(self):
+ checkout = self._checkout()
+ rc, mk_install, mk_git = self._run(
+ checkout, marker={"tag": "v0.7.0", "asset": "x"},
+ release=_release([], tag="v0.7.0"))
+ self.assertEqual(rc, 0)
+ mk_install.assert_not_called()
+ mk_git.assert_not_called()
+
+ def test_unreachable_github_keeps_the_install(self):
+ checkout = self._checkout()
+ rc, mk_install, mk_git = self._run(
+ checkout, marker={"tag": "v0.7.0", "asset": "x"},
+ release=None)
+ self.assertEqual(rc, 0)
+ mk_install.assert_not_called()
+ mk_git.assert_not_called()
+
+ def test_source_builds_still_route_through_git(self):
+ checkout = self._checkout()
+ (checkout / "server.json").write_text(
+ json.dumps({"models": [], "backend": "cpu"}),
+ encoding="utf-8")
+ with patch("sys.platform", "darwin"), \
+ patch.object(make_server.build, "find_local_checkout",
+ return_value=checkout), \
+ patch.object(make_server.build.servers, "pid_for",
+ return_value=None), \
+ patch.object(make_server.prebuilt, "installed_release",
+ return_value=None), \
+ patch.object(common, "git_update",
+ return_value=0) as mk_git, \
+ patch.object(make_server.build, "build_audiocpp",
+ return_value=0) as mk_build:
+ rc = make_server.build.update()
+ self.assertEqual(rc, 0)
+ mk_git.assert_called_once()
+ mk_build.assert_called_once_with(checkout, "cpu", emit=None,
+ cancel=None)
+
+
+class PrebuiltFlagTests(unittest.TestCase):
+ """--prebuilt resolves into a concrete install mode."""
+
+ def _args(self, value):
+ return make_server.wizard.build_parser().parse_args(
+ ["--prebuilt", value] if value else [])
+
+ def test_auto_downloads_on_macos_and_windows(self):
+ with patch("sys.platform", "darwin"):
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("auto"),
+ "cpu"), "prebuilt")
+ with patch("sys.platform", "win32"):
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("auto"),
+ "cuda"), "prebuilt")
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("auto"),
+ "hip"), "source")
+
+ def test_auto_builds_from_source_on_linux(self):
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("auto"),
+ "cuda"), "source")
+
+ def test_yes_and_no_force_their_modes(self):
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("yes"), "hip"),
+ "prebuilt")
+ self.assertEqual(
+ make_server.wizard._flag_build_mode(self._args("no"), "cpu"),
+ "source")
+
+ def test_darwin_wizard_honors_the_form_mode(self):
+ # On macOS the combined form asks "Get audiocpp_server" as a
+ # three-way choice; the answer must drive the install mode.
+ tmp = tempfile.TemporaryDirectory()
+ self.addCleanup(tmp.cleanup)
+ checkout = _make_checkout(Path(tmp.name))
+ catalog = make_server.catalog.load_model_catalog(checkout)
+ qwen_index = next(i for i, entry in enumerate(catalog)
+ if entry["family"] == "qwen3_tts")
+
+ def fake_tree(*args, **kwargs):
+ return [(qwen_index, "Qwen3-TTS-12Hz-1.7B-Base-GGUF")]
+
+ def fake_form(stdscr, title, fields, **kwargs):
+ by_key = {f["key"]: f for f in fields}
+ return {f["key"]: f["value"] for f in fields} | {
+ "backend": by_key["backend"]["value"],
+ "build_mode": mode,
+ }
+
+ for mode, expected_build in (("skip", False), ("source", True),
+ ("prebuilt", True)):
+ with patch("sys.platform", "darwin"), \
+ patch.object(make_server.build, "find_local_checkout",
+ return_value=checkout), \
+ patch.object(tui, "checkbox_tree",
+ side_effect=fake_tree), \
+ patch.object(tui, "form", side_effect=fake_form):
+ settings = make_server.wizard._wizard(
+ None, make_server.wizard.build_parser().parse_args([]),
+ make_server.wizard.build_parser())
+ self.assertIsNotNone(settings)
+ self.assertEqual(settings["build_mode"], mode)
+ self.assertEqual(settings["build"], expected_build)