"""Offline policy tests for the Void template of the whisper.cpp binding (#26). Spec #16 packages three templates in void-packages layout under ``packaging/void``; this ticket owns the hard one, ``python3-pywhispercpp``, built from the pinned PyPI source distribution with the Vulkan backend, shared libraries, and portable CPU flags. Ticket #28 builds the container smoke gate on top; everything asserted here runs offline from sources already in the repo. Independent sources of truth: ``install.sh`` pins the binding version (``PYWHISPERCPP_VERSION``), ``uv.lock`` pins the sdist URL and sha256, and the sdist's own ``setup.py``/``PKG-INFO`` name the build and runtime requirements. The template must agree with all three. """ import re from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] VOID_DIR = REPO_ROOT / "packaging" / "void" TEMPLATE = VOID_DIR / "srcpkgs" / "python3-pywhispercpp" / "template" PINS = VOID_DIR / "tool_checksums.txt" BUILD_SH = VOID_DIR / "build-release.sh" INSTALL_SH = REPO_ROOT / "install.sh" UV_LOCK = REPO_ROOT / "uv.lock" #: ggml SIMD options pinned off for the plain x86-64 baseline (#957). #: GGML_NATIVE=OFF alone is not portable: while ggml's native default is on, #: every GGML_* SIMD option stays on and still compiles -mavx2 -mfma. #: Mirrors PORTABLE_GGML_OPTIONS in tests/test_flatpak_packaging.py and the #: CMAKE_ARGS line in packaging/flatpak/python3-dependencies.yaml. PORTABLE_GGML_OPTIONS = ( "GGML_NATIVE", "GGML_SSE42", "GGML_AVX", "GGML_AVX2", "GGML_BMI2", "GGML_FMA", "GGML_F16C", ) #: install_requires of pywhispercpp's setup.py, mapped to the Void binary #: package names. Each name was verified to exist as srcpkgs//template #: in current void-packages (2026-11-05); the container build re-verifies via #: xbps-query, so a removal upstream fails there rather than silently here. VOID_RUNTIME_DEPS = ( "python3-numpy", "python3-requests", "python3-tqdm", "python3-platformdirs", ) def _template() -> str: assert TEMPLATE.is_file(), f"missing Void template: {TEMPLATE}" return TEMPLATE.read_text(encoding="utf-8") def _pinned_version() -> str: text = INSTALL_SH.read_text(encoding="utf-8") match = re.search(r'^PYWHISPERCPP_VERSION="([^"]+)"', text, re.M) assert match, "install.sh no longer pins PYWHISPERCPP_VERSION" return match.group(1) def _sdist_url_and_hash() -> tuple[str, str]: text = UV_LOCK.read_text(encoding="utf-8") match = re.search( r'sdist = \{ url = "(https://files\.pythonhosted\.org/[^"]*?' r"/pywhispercpp-([0-9.]+)\.tar\.gz)\"," r' hash = "sha256:([0-9a-f]{64})"', text, ) assert match, "uv.lock no longer pins the pywhispercpp sdist URL and hash" return match.group(1), match.group(3) def test_template_names_the_package_after_its_directory() -> None: """pkgname must match srcpkgs/, per the Void manual.""" text = _template() match = re.search(r"^pkgname=(\S+)", text, re.M) assert match, "template sets no pkgname" assert match.group(1) == TEMPLATE.parent.name == "python3-pywhispercpp" def test_template_carries_the_mandatory_void_variables() -> None: """Mandatory vars: version, revision, short_desc, maintainer, license.""" text = _template() assert re.search(r"^version=[0-9][0-9.]*$", text, re.M), "unpinned or missing version" assert re.search(r"^revision=1$", text, re.M), "new templates start at revision 1" match = re.search(r'^short_desc="([^"]+)"', text, re.M) assert match, "missing short_desc" assert len(match.group(1)) <= 72, f"short_desc exceeds 72 chars: {match.group(1)!r}" assert re.search( r'^maintainer="?[^"]*<\S+@\S+>"?$', text, re.M ), "maintainer is not 'name '" assert re.search(r'^license="?MIT"?$', text, re.M), "pywhispercpp is MIT licensed" assert re.search(r'^homepage="?https://github\.com/absadiki/pywhispercpp"?$', text, re.M) def test_template_version_matches_the_installer_and_lockfile_pins() -> None: """The template builds the pinned binding, not newest upstream.""" text = _template() pinned = _pinned_version() match = re.search(r"^version=(\S+)", text, re.M) assert match and match.group(1) == pinned, ( f"template builds {match.group(1) if match else 'nothing'}, " f"install.sh pins {pinned}" ) def test_distfiles_and_checksum_match_the_locked_sdist() -> None: """One PyPI source distribution, checksum-verified against uv.lock.""" text = _template() url, sha = _sdist_url_and_hash() assert ( "${PYPI_SITE}/p/pywhispercpp/pywhispercpp-${version}.tar.gz" in text ), "distfiles must use the ${PYPI_SITE} mirror helper with ${version}" match = re.search(r"^checksum=([0-9a-f]{64})$", text, re.M) assert match, "checksum must be a bare sha256 hex digest" assert match.group(1) == sha, "template checksum is behind uv.lock's sdist hash" assert url.endswith(f"pywhispercpp-{_pinned_version()}.tar.gz") def test_build_style_is_python3_module() -> None: """setup.py exists, so the module style applies (per the Void manual).""" assert re.search(r"^build_style=python3-module$", _template(), re.M) def test_portable_cpu_flags_pin_every_ggml_simd_option_off() -> None: """No host-native tuning: the plain x86-64 baseline, like the Flatpak.""" text = _template() for flag in PORTABLE_GGML_OPTIONS: assert f"-D{flag}=OFF" in text, ( f"template does not pin {flag} off; the package inherits the " "builder's instruction set and SIGILLs on older CPUs (#938, #957)" ) def _code_lines() -> str: """Template with comment lines stripped; flags live in code, not prose.""" return "\n".join(line for line in _template().splitlines() if not line.lstrip().startswith("#")) def test_no_native_tuning_or_cuda_anywhere() -> None: """Void has no CUDA toolkit package; portable means portable.""" text = _code_lines() assert "-march=native" not in text and "-mcpu=native" not in text assert "GGML_NATIVE=ON" not in text assert "cuda" not in text.lower(), "no CUDA build: Void ships no CUDA toolkit" def test_vulkan_is_a_default_on_build_option() -> None: """Source builders rebuild CPU-only with -o ~vulkan.""" text = _template() match = re.search(r"^build_options=(.*)$", text, re.M) assert match and "vulkan" in match.group(1).replace('"', "").split() match = re.search(r"^build_options_default=(.*)$", text, re.M) assert match and "vulkan" in match.group(1).replace('"', "").split(), "Vulkan is on by default" # The common options description already defines desc_option_vulkan; # a local redefinition would drift from it. assert "desc_option_vulkan" not in text assert "GGML_VULKAN=1" in text, "the Vulkan backend is enabled via GGML_VULKAN=1" assert "build_option_vulkan" in text, "GGML_VULKAN must be gated on the option" def test_vulkan_deps_are_conditional_and_runtime_names_exist() -> None: """Vulkan-only deps vanish from CPU-only builds; runtime names are real.""" text = _template() assert "vopt_if vulkan" in text, "conditional Vulkan deps use vopt_if" assert "vulkan-loader" in text, "libvulkan comes from vulkan-loader" assert "Vulkan-Headers" in text or "vulkan-loader-devel" in text assert "shaderc" in text, "ggml-vulkan requires glslc at build time" depends = re.search(r"^depends=(.*)$", text, re.M) assert depends, "template declares no runtime depends" for dep in VOID_RUNTIME_DEPS: assert dep in depends.group(1), f"{dep} missing from depends" def test_build_time_deps_cover_setup_py() -> None: """setup.py imports bdist_wheel at top level and shells out to cmake.""" text = _template() host = re.search(r"^hostmakedepends=(.*)$", text, re.M) assert host, "template declares no hostmakedepends" for dep in ("cmake", "pkg-config", "python3-setuptools", "python3-wheel"): assert dep in host.group(1), f"{dep} missing from hostmakedepends" make = re.search(r"^makedepends=(.*)$", text, re.M) assert make and "python3-devel" in make.group(1), "compiled extension needs python3-devel" def test_version_is_injected_for_the_offline_build() -> None: """The sdist carries no version.txt; setup.py reads PYWHISPERCPP_VERSION.""" assert "PYWHISPERCPP_VERSION" in _template() def test_build_fetches_nothing_beyond_the_sdist() -> None: """Only declared distfiles may come from the network.""" text = _template() assert text.count("https://") == 1, "homepage is the only URL a template may name" assert "FetchContent" not in text and "ExternalProject" not in text assert "make_check=no" in text, "the sdist ships no tests to check" def test_template_style_is_lint_clean() -> None: """Trailing whitespace and missing final newline fail xlint.""" raw = TEMPLATE.read_bytes().decode("utf-8") for lineno, line in enumerate(raw.splitlines(), 1): assert line == line.rstrip(), f"trailing whitespace on line {lineno}" assert "\t" not in line or line.startswith("\t"), f"stray tab on line {lineno}" assert raw.endswith("\n"), "template must end with a newline" def test_tool_checksums_pin_the_void_glibc_image() -> None: """The container gate builds in one pinned image, like native/appimage.""" assert PINS.is_file(), f"missing pins file: {PINS}" pins = {} for line in PINS.read_text(encoding="utf-8").splitlines(): if not line.strip() or line.lstrip().startswith("#"): continue name, kind, value, _source = line.split(None, 3) pins[name] = (kind, value) assert "base-image" in pins, "no pinned base image" kind, value = pins["base-image"] assert kind == "docker", "base-image pin kind matches native/appimage convention" assert value.startswith( "ghcr.io/void-linux/void-glibc-full@sha256:" ), f"base image must be the pinned Void glibc image, got {value}" assert len(value.rsplit(":", 1)[1]) == 64, "image digest must be a full sha256" def test_build_script_uses_podman_and_the_pinned_image() -> None: """Container work uses podman, never Docker; the image comes from the pins.""" assert BUILD_SH.is_file(), f"missing build script: {BUILD_SH}" text = BUILD_SH.read_text(encoding="utf-8") assert "podman" in text, "the build harness must use podman" scrubbed = text.lower().replace("never docker", "") assert "docker" not in scrubbed, "never Docker" assert "tool_checksums.txt" in text, "the image digest comes from the pins file" # The container flow lives in a mounted file, not an inline sh -c string: # single quotes inside sh -c silently regroup the command. name = "container-release.sh" assert (VOID_DIR / name).is_file(), f"missing container script: {name}" assert name in text, f"{BUILD_SH.name} must mount {name}"