Files
voculinux/tests/test_cross_distro_compatibility.py
T
Jatin K MalikandGitHub af6b372177 fix(install): Fedora and Arch need glslc, not glslang (#763)
* fix(install): Fedora and Arch need glslc, not glslang

ggml's Vulkan CMake looks for glslc and will not take glslangValidator.
install.sh was still installing glslang on Fedora, Arch, Alpine, Void,
Solus, and Gentoo, so the pywhispercpp source build fell back to a CPU
wheel. Ubuntu and Debian already pull glslc plus patchelf.

See #604.

* fix(install): Gentoo patchelf is under dev-util

sys-devel/patchelf does not exist. emerge fails and install.sh exits,
so a fresh Gentoo install never reaches the whisper.cpp build.

See #604.
2026-08-31 10:43:06 -07:00

255 lines
11 KiB
Python

"""
Test cross-distro compatibility features.
This module tests the cross-distribution compatibility features including:
- Dynamic GI_TYPELIB_PATH detection
- Wrapper script generation with correct paths
- Desktop entry configuration
"""
import os
from pathlib import Path
import pytest
def get_repo_root() -> Path:
"""Get the repository root directory."""
# Start from current file and traverse up to find repo root
current = Path(__file__).resolve()
for parent in [current] + list(current.parents):
# Check for common repo indicators
if (parent / ".git").exists() or (parent / "install.sh").exists():
return parent
# Also check if we're in a 'vocalinux' subdirectory (CI structure)
if parent.name == "vocalinux" and (parent.parent / "vocalinux" / "install.sh").exists():
return parent.parent / "vocalinux"
# Fallback: use parent of tests directory
return current.parent.parent.parent
REPO_ROOT = get_repo_root()
class TestCrossDistroCompatibility:
"""Test cross-distro compatibility features."""
@pytest.fixture
def install_sh_content(self):
"""Fixture to load install.sh content."""
install_sh_path = REPO_ROOT / "install.sh"
if not install_sh_path.exists():
pytest.skip(f"install.sh not found at {install_sh_path}")
return install_sh_path.read_text()
@pytest.fixture
def workflow_content(self):
"""Fixture to load workflow content."""
workflow_path = REPO_ROOT / ".github" / "workflows" / "unified-pipeline.yml"
if not workflow_path.exists():
pytest.skip(f"Workflow file not found at {workflow_path}")
return workflow_path.read_text()
def test_detect_typelib_path_function_exists(self, install_sh_content):
"""Test that detect_typelib_path function exists in install.sh."""
assert (
"detect_typelib_path()" in install_sh_content
), "detect_typelib_path function should exist"
assert (
"pkg-config --variable=typelibdir" in install_sh_content
), "Should use pkg-config for detection"
def test_no_hardcoded_gi_typelib_path_in_verification(self, install_sh_content):
"""Test that package verification uses dynamic GI_TYPELIB_PATH."""
# The verification function should use the detected path, not hardcoded
assert (
"verify_package_installed()" in install_sh_content
), "verify_package_installed function should exist"
# Check that it uses GI_TYPELIB_DETECTED variable
verify_section = install_sh_content[
install_sh_content.find("verify_package_installed()") : install_sh_content.find(
"verify_package_installed()"
)
+ 500
]
assert (
"GI_TYPELIB_DETECTED" in verify_section or '"$GI_TYPELIB_DETECTED"' in verify_section
), "verify_package_installed should use GI_TYPELIB_DETECTED variable"
def test_wrapper_script_uses_detected_path(self, install_sh_content):
"""Test that wrapper scripts use the detected GI_TYPELIB_PATH."""
# Check that wrapper script creation uses GI_TYPELIB_DETECTED
assert (
"export GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED" in install_sh_content
), "Wrapper script should export GI_TYPELIB_PATH with detected value"
def test_desktop_entry_uses_detected_path(self, install_sh_content):
"""Test that desktop entry uses detected GI_TYPELIB_PATH."""
# The sed command for desktop entry should use the detected path
assert (
"GI_TYPELIB_PATH=$GI_TYPELIB_DETECTED" in install_sh_content
or r"GI_TYPELIB_PATH=${GI_TYPELIB_DETECTED}" in install_sh_content
), "Desktop entry sed command should use GI_TYPELIB_DETECTED"
# Should NOT have hardcoded /usr/lib/girepository-1.0 in desktop entry section
desktop_start = install_sh_content.find("install_desktop_entry()")
if desktop_start == -1:
pytest.skip("install_desktop_entry function not found")
desktop_section = install_sh_content[desktop_start : desktop_start + 1000]
if "sed -i" in desktop_section and "vocalinux.desktop" in desktop_section:
assert (
"/usr/lib/girepository-1.0"
not in desktop_section.split("sed -i")[1].split("vocalinux.desktop")[0]
), "Desktop entry should not hardcode /usr/lib/girepository-1.0 path"
def test_detect_typelib_path_with_pkgconfig(self, install_sh_content):
"""Test that detect_typelib_path uses pkg-config when available."""
# Extract the detect_typelib_path function
func_start = install_sh_content.find("detect_typelib_path() {")
if func_start == -1:
pytest.skip("detect_typelib_path function not found")
func_end = install_sh_content.find("}", func_start)
func_content = install_sh_content[func_start:func_end]
# Should check for pkg-config first
assert "pkg-config" in func_content, "Should check for pkg-config"
assert "gobject-introspection-1.0" in func_content, "Should query gobject-introspection-1.0"
def test_detect_typelib_path_fallback_paths(self, install_sh_content):
"""Test that detect_typelib_path has fallback paths for various distros."""
# Should have fallback paths for different distros
expected_paths = [
"/usr/lib/x86_64-linux-gnu/girepository-1.0", # Ubuntu/Debian x86_64
"/usr/lib/aarch64-linux-gnu/girepository-1.0", # ARM64
"/usr/lib64/girepository-1.0", # Fedora/RHEL
"/usr/lib/girepository-1.0", # Arch and fallback
]
for path in expected_paths:
assert path in install_sh_content, f"Should have fallback path: {path}"
def test_opensuse_dependency_mapping_uses_tumbleweed_package_names(self, install_sh_content):
"""Test that openSUSE uses Tumbleweed-compatible dependency resolution."""
assert "suse_python_package_prefix()" in install_sh_content
assert "suse_python_package_candidates()" in install_sh_content
assert "typelib-1_0-AyatanaAppIndicator3-0_1" in install_sh_content
assert "typelib-1_0-Notify-0_7" in install_sh_content
assert "libnotify4" in install_sh_content
assert "suse_install_appindicator_runtime" in install_sh_content
assert "shaderc glslang-devel glslang" in install_sh_content
zypper_line = next(
line for line in install_sh_content.splitlines() if "local ZYPPER_PACKAGES=" in line
)
assert "python3-devel" not in zypper_line
assert "python3-virtualenv" not in zypper_line
assert "libappindicator-gtk3" not in zypper_line
assert "glslang" not in zypper_line
def test_rpm_arch_install_glslc_not_glslang(self, install_sh_content):
"""ggml Vulkan needs glslc (shaderc). glslangValidator is not a substitute (#604)."""
dnf_line = next(
line for line in install_sh_content.splitlines() if "local DNF_PACKAGES=" in line
)
pacman_line = next(
line for line in install_sh_content.splitlines() if "local PACMAN_PACKAGES=" in line
)
assert "glslc" in dnf_line
assert "patchelf" in dnf_line
assert "glslang" not in dnf_line
assert "shaderc" in pacman_line
assert "patchelf" in pacman_line
assert "glslang" not in pacman_line
assert (
"Fedora: sudo dnf install vulkan-loader-devel vulkan-tools glslc patchelf"
in install_sh_content
)
assert (
"Arch: sudo pacman -S vulkan-headers vulkan-tools shaderc patchelf"
in install_sh_content
)
emerge_line = next(
line for line in install_sh_content.splitlines() if "local EMERGE_PACKAGES=" in line
)
assert "media-libs/shaderc" in emerge_line
assert "dev-util/patchelf" in emerge_line
assert "sys-devel/patchelf" not in emerge_line
def test_skip_system_deps_option_is_implemented(self, install_sh_content):
"""Test that the documented --skip-system-deps option is parsed and honored."""
assert "SKIP_SYSTEM_DEPS" in install_sh_content
assert "--skip-system-deps" in install_sh_content
assert "Skipping system dependency installation" in install_sh_content
def test_noninteractive_tty_fallback_does_not_abort(self, install_sh_content):
"""Test that missing controlling TTY falls back to non-interactive mode."""
assert "{ true < /dev/tty; } 2>/dev/null" in install_sh_content
assert "exec < /dev/tty" in install_sh_content
assert 'NON_INTERACTIVE="yes"' in install_sh_content
def test_ci_workflow_uses_dynamic_detection(self, workflow_content):
"""Test that CI workflow uses dynamic GI_TYPELIB_PATH detection."""
# Should use pkg-config or dynamic detection
assert (
"pkg-config --variable=typelibdir" in workflow_content
), "CI workflow should use pkg-config for GI_TYPELIB_PATH"
class TestResourceManagerCrossDistro:
"""Test ResourceManager cross-distro path handling."""
def test_resource_manager_has_xdg_support(self):
"""Test that ResourceManager supports XDG directories."""
from vocalinux.utils.resource_manager import ResourceManager
rm = ResourceManager()
resources_dir = rm.resources_dir
# Should be a valid path string
assert isinstance(resources_dir, str), "resources_dir should be a string"
assert len(resources_dir) > 0, "resources_dir should not be empty"
class TestRecognitionManagerCrossDistro:
"""Test RecognitionManager cross-distro model path handling."""
def test_system_model_paths_exists(self):
"""Test that system model paths configuration exists."""
from vocalinux.speech_recognition.recognition_manager import SYSTEM_MODELS_DIRS
# Should be a list of paths
assert isinstance(SYSTEM_MODELS_DIRS, list), "SYSTEM_MODELS_DIRS should be a list"
assert len(SYSTEM_MODELS_DIRS) > 0, "SYSTEM_MODELS_DIRS should not be empty"
# Should include XDG paths
has_xdg = any("/share/" in path for path in SYSTEM_MODELS_DIRS)
assert has_xdg, "SYSTEM_MODELS_DIRS should include XDG paths"
class TestDocumentation:
"""Test that documentation is accurate."""
def test_distro_compatibility_documentation_exists(self):
"""Test that DISTRO_COMPATIBILITY.md exists and is accurate."""
docs_path = REPO_ROOT / "docs" / "DISTRO_COMPATIBILITY.md"
if not docs_path.exists():
pytest.skip(f"DISTRO_COMPATIBILITY.md not found at {docs_path}")
content = docs_path.read_text()
# Should mention the dynamic path detection
assert (
"pkg-config" in content or "detect_typelib_path" in content
), "Should document dynamic path detection"
# Should mention supported distros
expected_distros = ["Ubuntu", "Debian", "Fedora", "Arch", "openSUSE"]
for distro in expected_distros:
assert distro in content, f"Should mention {distro} support"
if __name__ == "__main__":
pytest.main([__file__, "-v"])