Files
voculinux/tests/test_rename_sweep.py
T
soubarnak ea7be5f111 feat(identity): rename user-visible identity to voculinux with one-time legacy import
The fork takes its own name everywhere a user can see it: binaries
(voculinux, voculinux-gui), desktop entry, autostart, tray, D-Bus
namespace (com.voculinux.Voculinux, no alias for the old name),
config/data/cache dirs, single-instance lock, IBus engine identity,
tray icons, resource prefixes, VOCULINUX_* environment names, and the
update-checker user agent. The internal Python module stays vocalinux
and the distribution metadata keeps the upstream URLs as provenance.

On first run a previous Vocalinux install's config.json, custom
dictionary, and portal restore token are copied once into the new
locations (new config always wins, no merge, no write-back); models
re-resolve through the pinned-checksum download path. An offline
naming-sweep test proves no old-name strings remain on user-visible
surfaces outside the allowlist.

Refs #18
2026-11-06 01:29:39 +05:30

159 lines
5.7 KiB
Python

"""Ticket #18: offline naming-sweep — no old-name strings on user-visible 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``),
- the rest of ``web/`` (owned by ticket #24; only ``page.tsx`` and
``package.json`` belong to this ticket).
"""
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"}
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/ belongs to ticket #24 except the two rename-inventory files.
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 "/{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 == "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 '== "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
if rel.startswith("web/") and rel not in WEB_OWNED:
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