Import the vocalinux source tree (tray app, Next.js site in web/, packaging, installers, docs and tests) on top of the Gitea template commit. The project README replaces the template README, and the template CI files (.gitea/workflows/ci.yml, .jscpd.json, CI.md) are kept.
302 lines
10 KiB
Python
302 lines
10 KiB
Python
"""Behavioral tests for installer CUDA diagnostic functions."""
|
|
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
INSTALL_SH = Path(__file__).resolve().parents[1] / "install.sh"
|
|
|
|
|
|
def source_install_sh_functions(tmpdir: Path) -> str:
|
|
"""Extract and source only the function definitions from install.sh."""
|
|
content = INSTALL_SH.read_text()
|
|
|
|
setup_script = f"""
|
|
set -e
|
|
cd "{tmpdir}"
|
|
|
|
# Mock output functions
|
|
print_warning() {{ echo "WARNING: $*" >&2; }}
|
|
print_info() {{ echo "INFO: $*" >&2; }}
|
|
print_success() {{ echo "SUCCESS: $*" >&2; }}
|
|
print_error() {{ echo "ERROR: $*" >&2; }}
|
|
|
|
# Mock get_pywhispercpp_library_path (will be overridden per test)
|
|
get_pywhispercpp_library_path() {{ echo ""; }}
|
|
|
|
# Source the actual functions from install.sh
|
|
# Extract function definitions using sed
|
|
"""
|
|
return setup_script
|
|
|
|
|
|
def run_bash_test(script: str) -> subprocess.CompletedProcess:
|
|
"""Run a bash script and return the result."""
|
|
result = subprocess.run(["bash", "-c", script], capture_output=True, text=True, timeout=10)
|
|
return result
|
|
|
|
|
|
class TestCudaToolkitRootHasRuntimeLibrary:
|
|
"""Tests for cuda_toolkit_root_has_runtime_library function."""
|
|
|
|
def test_accepts_lib64_layout(self, tmp_path):
|
|
"""Standard x86_64 layout with lib64/libcudart.so."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "lib64").mkdir(parents=True)
|
|
(cuda_root / "lib64" / "libcudart.so.12.2").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
cuda_toolkit_root_has_runtime_library "{cuda_root}" && echo "PASS" || echo "FAIL"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_accepts_lib_layout(self, tmp_path):
|
|
"""Layout with lib/libcudart.so."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "lib").mkdir(parents=True)
|
|
(cuda_root / "lib" / "libcudart.so").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
cuda_toolkit_root_has_runtime_library "{cuda_root}" && echo "PASS" || echo "FAIL"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_accepts_debian_layout(self, tmp_path):
|
|
"""Debian multiarch layout with lib/x86_64-linux-gnu/libcudart.so."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "lib" / "x86_64-linux-gnu").mkdir(parents=True)
|
|
(cuda_root / "lib" / "x86_64-linux-gnu" / "libcudart.so.11.8").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
cuda_toolkit_root_has_runtime_library "{cuda_root}" && echo "PASS" || echo "FAIL"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_accepts_aarch64_layout(self, tmp_path):
|
|
"""ARM64 layout with targets/aarch64-linux/lib/libcudart.so."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "targets" / "aarch64-linux" / "lib").mkdir(parents=True)
|
|
(cuda_root / "targets" / "aarch64-linux" / "lib" / "libcudart.so.12.2").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
cuda_toolkit_root_has_runtime_library "{cuda_root}" && echo "PASS" || echo "FAIL"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_rejects_missing_runtime(self, tmp_path):
|
|
"""Reject when libcudart.so* is missing."""
|
|
cuda_root = tmp_path / "cuda"
|
|
cuda_root.mkdir(parents=True)
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
cuda_toolkit_root_has_runtime_library "{cuda_root}" && echo "FAIL" || echo "PASS"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
|
|
class TestValidateCudaToolkitRoot:
|
|
"""Tests for validate_cuda_toolkit_root function."""
|
|
|
|
def test_accepts_complete_root(self, tmp_path):
|
|
"""Accept when nvcc, cuda_runtime.h, and libcudart.so all present."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "bin").mkdir(parents=True)
|
|
(cuda_root / "include").mkdir(parents=True)
|
|
(cuda_root / "lib64").mkdir(parents=True)
|
|
|
|
nvcc = cuda_root / "bin" / "nvcc"
|
|
nvcc.touch()
|
|
nvcc.chmod(0o755)
|
|
|
|
(cuda_root / "include" / "cuda_runtime.h").touch()
|
|
(cuda_root / "lib64" / "libcudart.so.12.2").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
source <(sed -n '/^validate_cuda_toolkit_root/,/^}}/p' "{INSTALL_SH}")
|
|
validate_cuda_toolkit_root "{cuda_root}" && echo "PASS" || echo "FAIL"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_rejects_stale_root(self, tmp_path):
|
|
"""Reject when only bin/nvcc exists (stale /usr/local/cuda)."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "bin").mkdir(parents=True)
|
|
|
|
nvcc = cuda_root / "bin" / "nvcc"
|
|
nvcc.touch()
|
|
nvcc.chmod(0o755)
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
source <(sed -n '/^validate_cuda_toolkit_root/,/^}}/p' "{INSTALL_SH}")
|
|
validate_cuda_toolkit_root "{cuda_root}" && echo "FAIL" || echo "PASS"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
def test_rejects_nvcc_not_executable(self, tmp_path):
|
|
"""Reject when nvcc exists but is not executable."""
|
|
cuda_root = tmp_path / "cuda"
|
|
(cuda_root / "bin").mkdir(parents=True)
|
|
(cuda_root / "include").mkdir(parents=True)
|
|
(cuda_root / "lib64").mkdir(parents=True)
|
|
|
|
(cuda_root / "bin" / "nvcc").touch()
|
|
(cuda_root / "include" / "cuda_runtime.h").touch()
|
|
(cuda_root / "lib64" / "libcudart.so.12.2").touch()
|
|
|
|
script = f"""
|
|
{source_install_sh_functions(tmp_path)}
|
|
source <(sed -n '/^cuda_toolkit_root_has_runtime_library/,/^}}/p' "{INSTALL_SH}")
|
|
source <(sed -n '/^validate_cuda_toolkit_root/,/^}}/p' "{INSTALL_SH}")
|
|
validate_cuda_toolkit_root "{cuda_root}" && echo "FAIL" || echo "PASS"
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert "PASS" in result.stdout
|
|
|
|
|
|
class TestDetectNvidiaComputeArchitecturesAwkParser:
|
|
"""Tests for the awk parser in detect_nvidia_compute_architectures."""
|
|
|
|
def test_parses_single_gpu(self):
|
|
"""Parse single GPU compute capability 8.9 (RTX 4090)."""
|
|
script = """
|
|
echo "8.9" | awk '
|
|
{
|
|
gsub(/[[:space:]]/, "", $1)
|
|
if ($1 ~ /^[0-9]+(\\.[0-9]+)?$/) {
|
|
split($1, parts, ".")
|
|
minor = parts[2]
|
|
if (minor == "") {
|
|
minor = "0"
|
|
}
|
|
print parts[1] minor "-real"
|
|
}
|
|
}
|
|
' | sort -u | paste -sd ';' -
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "89-real"
|
|
|
|
def test_parses_multi_gpu(self):
|
|
"""Parse multi-GPU with different architectures."""
|
|
script = """
|
|
echo -e "8.9\\n8.6" | awk '
|
|
{
|
|
gsub(/[[:space:]]/, "", $1)
|
|
if ($1 ~ /^[0-9]+(\\.[0-9]+)?$/) {
|
|
split($1, parts, ".")
|
|
minor = parts[2]
|
|
if (minor == "") {
|
|
minor = "0"
|
|
}
|
|
print parts[1] minor "-real"
|
|
}
|
|
}
|
|
' | sort -u | paste -sd ';' -
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "86-real;89-real"
|
|
|
|
def test_filters_garbage(self):
|
|
"""Filter out non-numeric values like [Not Supported]."""
|
|
script = """
|
|
echo -e "[Not Supported]\\n8.9" | awk '
|
|
{
|
|
gsub(/[[:space:]]/, "", $1)
|
|
if ($1 ~ /^[0-9]+(\\.[0-9]+)?$/) {
|
|
split($1, parts, ".")
|
|
minor = parts[2]
|
|
if (minor == "") {
|
|
minor = "0"
|
|
}
|
|
print parts[1] minor "-real"
|
|
}
|
|
}
|
|
' | sort -u | paste -sd ';' -
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "89-real"
|
|
|
|
def test_deduplicates(self):
|
|
"""Deduplicate identical compute capabilities."""
|
|
script = """
|
|
echo -e "8.9\\n8.9\\n8.9" | awk '
|
|
{
|
|
gsub(/[[:space:]]/, "", $1)
|
|
if ($1 ~ /^[0-9]+(\\.[0-9]+)?$/) {
|
|
split($1, parts, ".")
|
|
minor = parts[2]
|
|
if (minor == "") {
|
|
minor = "0"
|
|
}
|
|
print parts[1] minor "-real"
|
|
}
|
|
}
|
|
' | sort -u | paste -sd ';' -
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "89-real"
|
|
|
|
|
|
class TestReadelfBundledLibcudaRegex:
|
|
"""Tests for the readelf regex that detects bundled libcuda."""
|
|
|
|
def test_detects_bundled_libcuda(self):
|
|
"""Detect auditwheel-bundled libcuda with hashed name."""
|
|
script = """
|
|
echo " 0x0000000000000001 (NEEDED) Shared library: [libcuda-535.129.03.so]" | \
|
|
sed -En 's/.*Shared library: \\[(libcuda-[^]]+\\.so).*/\\1/p'
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "libcuda-535.129.03.so"
|
|
|
|
def test_ignores_system_libcuda(self):
|
|
"""Do not match standard libcuda.so.1."""
|
|
script = """
|
|
echo " 0x0000000000000001 (NEEDED) Shared library: [libcuda.so.1]" | \
|
|
sed -En 's/.*Shared library: \\[(libcuda-[^]]+\\.so).*/\\1/p'
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == ""
|
|
|
|
def test_ignores_bundled_cudart(self):
|
|
"""Do not match bundled libcudart (different library)."""
|
|
script = """
|
|
echo " 0x0000000000000001 (NEEDED) Shared library: [libcudart-12.2.140.so]" | \
|
|
sed -En 's/.*Shared library: \\[(libcuda-[^]]+\\.so).*/\\1/p'
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == ""
|
|
|
|
def test_detects_hashed_libcuda(self):
|
|
"""Detect auditwheel hash-style bundled libcuda."""
|
|
script = """
|
|
echo " 0x0000000000000001 (NEEDED) Shared library: [libcuda-abc12345.so]" | \
|
|
sed -En 's/.*Shared library: \\[(libcuda-[^]]+\\.so).*/\\1/p'
|
|
"""
|
|
result = run_bash_test(script)
|
|
assert result.stdout.strip() == "libcuda-abc12345.so"
|