generated from Labyricorn/labyricorn-project-template
Initial commit (forked from jamiepine/voicebox)
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
Phase 1.2 Test: ROCm build script configuration.
|
||||
|
||||
Validates that build_binary.py --rocm generates the correct PyInstaller
|
||||
arguments and optionally performs a true E2E build.
|
||||
|
||||
Usage:
|
||||
python -m pytest backend/tests/test_rocm_build.py -v
|
||||
python -m pytest backend/tests/test_rocm_build.py -v -m "slow" # include E2E
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from build_binary import build_server
|
||||
|
||||
|
||||
class TestRocmBuildArgs:
|
||||
"""Validate PyInstaller arguments for ROCm builds."""
|
||||
|
||||
@pytest.fixture
|
||||
def captured_args(self):
|
||||
"""Run build_server(rocm=True) with mocked PyInstaller and return args."""
|
||||
with (
|
||||
patch("build_binary.PyInstaller.__main__.run") as mock_run,
|
||||
patch("build_binary.platform.system", return_value="Linux"),
|
||||
patch("build_binary.os.chdir"),
|
||||
):
|
||||
build_server(rocm=True)
|
||||
return mock_run.call_args[0][0]
|
||||
|
||||
def test_binary_name(self, captured_args):
|
||||
idx = captured_args.index("--name")
|
||||
assert captured_args[idx + 1] == "voicebox-server-rocm"
|
||||
|
||||
def test_pack_mode_is_onedir(self, captured_args):
|
||||
assert "--onedir" in captured_args
|
||||
assert "--onefile" not in captured_args
|
||||
|
||||
def test_hidden_imports_cuda(self, captured_args):
|
||||
"""ROCm builds must include torch.cuda hidden imports."""
|
||||
assert "torch.cuda" in captured_args
|
||||
|
||||
def test_no_cudnn_hidden_import_for_rocm(self, captured_args):
|
||||
"""ROCm builds must NOT include NVIDIA-specific cudnn hidden imports."""
|
||||
assert "torch.backends.cudnn" not in captured_args
|
||||
|
||||
def test_nvidia_excludes_present(self, captured_args):
|
||||
"""ROCm builds must exclude nvidia packages to avoid bundling ~3GB of bloat."""
|
||||
excludes = []
|
||||
for i, arg in enumerate(captured_args):
|
||||
if arg == "--exclude-module":
|
||||
excludes.append(captured_args[i + 1])
|
||||
assert "nvidia" in excludes
|
||||
assert "nvidia.cudnn" in excludes
|
||||
|
||||
|
||||
class TestRocmBuildCli:
|
||||
"""Validate CLI argument parsing for --rocm."""
|
||||
|
||||
def test_rocm_flag_parses(self):
|
||||
build_script = Path(__file__).parent.parent / "build_binary.py"
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(build_script), "--rocm", "--help"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode == 0
|
||||
assert "--rocm" in result.stdout
|
||||
|
||||
def test_cannot_combine_cuda_and_rocm(self):
|
||||
"""Building with both CUDA and ROCm should raise ValueError."""
|
||||
with pytest.raises(ValueError, match="Cannot build with both CUDA and ROCm"):
|
||||
build_server(cuda=True, rocm=True)
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
@pytest.mark.skipif(sys.platform != "win32", reason="ROCm build E2E only runs on Windows")
|
||||
class TestRocmBuildE2E:
|
||||
"""
|
||||
True end-to-end build test.
|
||||
Executes build_binary.py --rocm, verifies the binary exists, and runs it
|
||||
with --help to confirm it boots without import errors.
|
||||
"""
|
||||
|
||||
def test_rocm_binary_compiles_and_runs(self, tmp_path):
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
build_script = backend_dir / "build_binary.py"
|
||||
dist_dir = backend_dir / "dist"
|
||||
binary_dir = dist_dir / "voicebox-server-rocm"
|
||||
binary_exe = binary_dir / "voicebox-server-rocm.exe"
|
||||
|
||||
# Clean previous dist if it exists to ensure a fresh build
|
||||
if binary_dir.exists():
|
||||
import shutil
|
||||
shutil.rmtree(binary_dir)
|
||||
|
||||
# Run the full build (this can take several minutes)
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(build_script), "--rocm"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=str(backend_dir),
|
||||
timeout=900,
|
||||
)
|
||||
|
||||
assert result.returncode == 0, (
|
||||
f"Build failed with stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
|
||||
)
|
||||
assert binary_exe.exists(), (
|
||||
f"Expected binary not found at {binary_exe}"
|
||||
)
|
||||
|
||||
# Run the binary with --help to ensure it boots without import errors
|
||||
run_result = subprocess.run(
|
||||
[str(binary_exe), "--help"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
# A frozen binary may not have argparse help, but it should not crash
|
||||
# with a ModuleNotFoundError or similar import error.
|
||||
assert "ModuleNotFoundError" not in run_result.stderr
|
||||
assert "ImportError" not in run_result.stderr
|
||||
Reference in New Issue
Block a user