Files
voculinux/tests/test_rename_sweep.py
T
soubarnak bd7b624f50 feat(void): add package smoke gate across all templates
Ticket xavierk/voculinux#28, needs #27. One documented command
(packaging/void/smoke.sh, just void-smoke) lints, builds, installs
and smoke-tests every template under packaging/void/srcpkgs in
dependency order in the pinned Void container via podman, failing
fast naming the package with the harness echoing each stage.

The gate caught three latent breaks in the per-package harness and
the app template, fixed here:
- container-main.sh scoped the libggml scan to python3-pywhispercpp
  and gave the leaf packages their own import smokes (pynput by
  presence: no display in the container), failing loudly for
  packages without a smoke recipe;
- voculinux template drops the deprecated wrksrc override (xbps-src
  renames the single-top-dir archive itself; xlint rejects wrksrc)
  and builds the PEP 517 wheel (the sdist ships no setup.py);
- post_install promotes the pre-rename sdist names (desktop entry
  content, icons, /usr/bin launchers) to the renamed identity, and
  the payload smoke rejects pre-rename leftovers in OS-visible paths.

Offline policy tests (tests/test_void_smoke_gate.py) pin the gate to
the template set, the app-last order, podman-only pinned inputs and
per-package failure identity; the #27 policy tests and the rename
sweep cover the harness and template contracts.
2026-11-06 05:57:37 +05:30

197 lines
7.3 KiB
Python

"""Ticket #18 + #24: offline naming-sweep — no old-name strings on surfaces.
The fork takes its own name everywhere a user can see it. Anything still
spelling the old name must be on the allowlist below:
- upstream provenance (``VocaHQ/vocalinux`` URLs),
- the retained internal Python module name (``src/vocalinux`` paths,
``vocalinux.*`` imports/attributes, ``-m vocalinux.main``),
- retained internal class names (``VocalinuxEngine*``, ``VocalinuxDBusService``),
- one-time-legacy import sources (old config/dictionary/bus literals),
- generated or historical files (``uv.lock``, ``requirements/*.txt``,
``CHANGELOG.md``),
- web upstream surfaces (ticket #24): upstream GitHub/raw/nix flake URLs,
nix flake API identifiers, and the nixos-page fork-status note that names
the upstream flake as installing Vocalinux rather than Voculinux.
"""
import os
import re
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OLD_NAME = re.compile(r"vocalinux", re.IGNORECASE)
SKIP_DIRS = {
".git",
"__pycache__",
".venv",
"venv",
"node_modules",
".next",
"build",
"dist",
"out",
}
SKIP_SUFFIXES = (".egg-info",)
SKIP_FILES = {"uv.lock", "CHANGELOG.md"}
SKIP_PREFIXES = ("requirements/",)
BINARY_EXTS = (
".wav",
".mp3",
".ogg",
".png",
".svg",
".ico",
".icns",
".bin",
".onnx",
".pt",
".zip",
".tar",
".gz",
".whl",
".AppImage",
".zsync",
".pyc",
)
#: web/ was fully rebranded in ticket #24; only upstream-provenance lines
#: (matched in _allowed) may still spell the old name there.
WEB_OWNED = {"web/src/app/page.tsx", "web/package.json"}
def _allowed(path: str, line: str) -> bool:
"""Return True when an old-name match is an allowlisted surface."""
low = line.lower()
if "vocahq/" in low or "jatinkrmalik/vocalinux" in low:
return True
if "vocalinux" in low and "voculinux" in low:
# Migration-compat shims that name both installs at once.
return True
if "upstream" in low:
# Upstream-provenance mentions that explicitly label the old name
# as upstream (fork-status notes, provenance cards).
return True
if "/{name}/vocalinux" in line:
# Upstream repo-name template (retained provenance).
return True
if path == "snap/local/check_runtime.py" and '"vocalinux"' in line:
# Smoke imports resolve the retained import package.
return True
if re.search(r'vocalinux\.["a-z_]', line):
return True
if line.strip().rstrip(",").lstrip("/ ") == '"vocalinux"':
# Retained module-dir path segment element in tests and tooling
# (single-line and os.path.join element forms).
return True
if 'site / "vocalinux"' in line or '"platform/vocalinux"' in line:
# Fixture module trees named after the retained import package.
return True
if '"vocalinux/' in line:
# Fixture module file paths under the retained package name.
return True
if "/vocalinux/" in line or "src/vocalinux" in line:
return True
if "-m vocalinux" in line or "import vocalinux" in line:
return True
if "VocalinuxEngine" in line or "VocalinuxDBusService" in line:
return True
if "cmdline" in line:
return True
if " - vocalinux" in line:
# Log logger names keep the retained module path.
return True
if path in (
"src/vocalinux/legacy_import.py",
"tests/test_legacy_import.py",
):
return True
if "LEGACY" in line or "legacy" in line:
return True
if "~/.config/vocalinux" in line or '".config" / "vocalinux"' in line:
# Documented one-time-import source (ticket #18).
return True
if "from vocalinux" in line:
return True
if '"src"' in line and '"vocalinux"' in line:
# Retained module-dir path segments in tests and tooling.
return True
if path == "web/src/app/install/nixos/page.tsx":
# The Nix flake is upstream's: flake URL, input/output names, module
# paths, and programs.* options spell the retained upstream API.
# The page carries a fork-status note stating those snippets install
# Vocalinux (upstream), not Voculinux.
return True
if path == "tests/test_rename_sweep.py":
return True
if path == "pyproject.toml" and re.search(r'"vocalinux[."\'\]]', line):
return True
if path == "pyproject.toml" and "vocalinux.version" in line:
return True
if 'PKG_NAME="vocalinux"' in line:
# Installer probes the retained import package, not the distribution.
return True
if path == "packaging/void/srcpkgs/voculinux/template" and "vocalinux-${version}" in line:
# Pre-rename release asset root (ticket #27); dropped at the first
# post-rename release bump.
return True
if path == "tests/test_void_app_template.py" and '"vocalinux"' in line:
# The app-template policy test asserts the old name is gone.
return True
if path == "tests/test_void_app_template.py" and "usr/bin/vocalinux" in line:
# Pre-rename sdist launchers the template promotes at install time
# (ticket #28); dropped at the first post-rename release bump.
return True
if path == "packaging/void/container-main.sh" and (
"usr/share/applications/vocalinux" in line or 'version("vocalinux")' in line
):
# Pre-rename sdist compat (ticket #28): the payload guard names the
# old file names it rejects, and the import smoke queries the old
# distribution name. Dropped at the first post-rename release bump.
return True
if '== "vocalinux"' in line or '.startswith("vocalinux' in line:
# Retained module identity comparisons in test isolation helpers.
return True
return False
def _iter_files():
for root, dirs, files in os.walk(REPO_ROOT):
dirs[:] = [d for d in sorted(dirs) if d not in SKIP_DIRS]
for name in sorted(files):
if name in SKIP_FILES or name.endswith(BINARY_EXTS):
continue
full = os.path.join(root, name)
rel = os.path.relpath(full, REPO_ROOT)
if rel.startswith(SKIP_PREFIXES):
continue
yield rel, full
def test_naming_sweep_no_old_name_outside_allowlist() -> None:
"""Every old-name hit must be an allowlisted retained surface."""
offenders: list[str] = []
for rel, full in _iter_files():
if any(part.endswith(SKIP_SUFFIXES) for part in rel.split("/")):
continue
try:
with open(full, encoding="utf-8") as handle:
text = handle.read()
except (UnicodeDecodeError, ValueError):
continue
for number, line in enumerate(text.splitlines(), 1):
if OLD_NAME.search(line) and not _allowed(rel, line):
offenders.append(f"{rel}:{number}:{line.strip()[:160]}")
assert (
offenders == []
), f"{len(offenders)} old-name string(s) outside the allowlist:\n" + "\n".join(offenders[:60])
def test_owned_web_files_carry_new_name() -> None:
"""The two web rename surfaces name the fork (URLs aside)."""
for rel in sorted(WEB_OWNED):
with open(os.path.join(REPO_ROOT, rel), encoding="utf-8") as handle:
text = handle.read()
assert "Voculinux" in text or "voculinux" in text, rel