Files
voculinux/tests/test_installer_review_notes.py
T
Jatin K MalikandGitHub 26f90b6998 chore(release): prepare v0.17.0
Minor on the stable line. Faster Whisper and Parakeet extras, Speech Model simple setup, Snap uinput, Flatpak bundles on the tag, and QA fixes from the release branch.

Fixes tracked in the PR. Post-release follow-ups: #834.
2026-09-16 00:18:09 -07:00

208 lines
7.8 KiB
Python

"""Source checks for leftover installer review notes from #700 and #705."""
import os
import subprocess
import sys
import tomllib
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
INSTALLER = Path(__file__).resolve().parents[1] / "install.sh"
PYPROJECT = REPO_ROOT / "pyproject.toml"
SETTINGS = Path(__file__).resolve().parents[1] / "src" / "vocalinux" / "ui" / "settings_dialog.py"
AGENTS = Path(__file__).resolve().parents[1] / "AGENTS.md"
def _installer_source() -> str:
return INSTALLER.read_text(encoding="utf-8")
def test_help_places_transcript_under_installation() -> None:
result = subprocess.run(
["bash", str(INSTALLER), "--help"],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0
assert "During installation a full transcript is saved to" in result.stdout
assert "every run" not in result.stdout
def test_help_does_not_create_install_log_or_scratch_dir(tmp_path, monkeypatch) -> None:
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
monkeypatch.setenv("TMPDIR", str(tmp_path / "tmp"))
(tmp_path / "tmp").mkdir()
result = subprocess.run(
["bash", str(INSTALLER), "--help"],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0
assert not list((tmp_path / "state").rglob("install-*.log"))
assert not list((tmp_path / "tmp").glob("vocalinux-install.*"))
def test_scratch_dir_owns_pip_logs_and_model_temps() -> None:
source = _installer_source()
assert 'PIP_LOG_DIR="$VOCALINUX_TMP_DIR/pip"' in source
assert 'export TMPDIR="$VOCALINUX_TMP_DIR"' in source
assert 'TEMP_FILE="$VOCALINUX_TMP_DIR/tiny.pt"' in source
assert 'TEMP_FILE="$VOCALINUX_TMP_DIR/ggml-tiny.bin"' in source
assert 'TEMP_ZIP="$VOCALINUX_TMP_DIR/$(basename $SMALL_MODEL_URL)"' in source
def test_system_dep_and_venv_failures_use_structured_exit_codes() -> None:
source = _installer_source()
assert 'print_error "Failed to install dependencies"; exit "$EXIT_MISSING_DEPS"' in source
assert 'print_error "Failed to update package lists"; exit "$EXIT_NETWORK"' in source
assert (
'print_error "Failed to create virtual environment. Please check your Python installation."'
in source
)
assert 'exit "$EXIT_MISSING_DEPS"' in source
assert (
'print_error "Failed to update pip, setuptools, and wheel"; exit "$EXIT_NETWORK"' in source
)
assert (
'print_error "Failed to install Vocalinux package. Installation cannot continue."' in source
)
assert 'exit "$EXIT_NETWORK"' in source
assert "Could not terminate all Vocalinux processes" in source
assert 'exit "$EXIT_USER_ABORT"' in source
def test_release_tag_hint_is_not_hardcoded() -> None:
source = _installer_source()
assert "--tag=<release>" in source
assert "--tag=v0.15.0" not in source
def test_whispercpp_fallback_installs_vosk_extra() -> None:
source = _installer_source()
assert 'pip_install_extras_skip_pygobject "$PIP_LOG_FILE" vosk' in source
assert "pip install vosk --log" not in source
assert 'pip install ".[vosk]"' not in source
def test_project_pip_install_skips_pygobject() -> None:
source = _installer_source()
assert "write_pip_reqs_skip_pygobject()" in source
assert "pip_install_project_skip_pygobject()" in source
assert "pip install --no-deps" in source
assert "--no-emit-package pygobject" in source
assert 'pip install . --log "$PIP_LOG_FILE"' not in source
assert 'pip install -e . --log "$PIP_LOG_FILE"' not in source
assert 'pip install ".[vad]"' not in source
assert 'pip install -e ".[vad]"' not in source
assert 'pip install -e ".[whisper,dev]"' not in source
def _run_reqs_writer(dest: Path, *extras: str) -> None:
source = _installer_source()
start = source.index("from pathlib import Path\nimport re\nimport sys\n")
end = source.index("\nPY\n}", start)
result = subprocess.run(
[sys.executable, "-", str(dest), *extras],
check=False,
cwd=INSTALLER.parent,
input=source[start:end],
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stderr
def test_write_pip_reqs_skip_pygobject_runtime(tmp_path) -> None:
dest = tmp_path / "runtime.txt"
_run_reqs_writer(dest)
reqs = dest.read_text().splitlines()
assert reqs
assert all("pygobject" not in req.lower() for req in reqs)
assert any(req.startswith("pyaudio") for req in reqs)
assert any(req.startswith("pywhispercpp") for req in reqs)
def test_write_pip_reqs_skip_pygobject_vosk_extra(tmp_path) -> None:
"""The writer copies an extra's specifiers through, bounds and all.
Read out of pyproject.toml rather than written here as a literal. This
assertion was `["vosk>=0.3.45"]` and failed the day the extra gained an
upper bound, reporting an intentional pyproject edit as an installer defect.
"""
with PYPROJECT.open("rb") as handle:
expected = tomllib.load(handle)["project"]["optional-dependencies"]["vosk"]
assert expected, "the vosk extra declares nothing; this would compare [] to []"
dest = tmp_path / "vosk.txt"
_run_reqs_writer(dest, "vosk")
assert dest.read_text().splitlines() == expected
def test_settings_uses_engine_flag_not_removed_with_whisper() -> None:
text = SETTINGS.read_text(encoding="utf-8")
assert "./install.sh --engine=whisper" in text
assert "--with-whisper" not in text
def test_session_type_env_reads_use_default_expansion() -> None:
source = _installer_source()
start = source.index("install_text_input_tools()")
end = source.index('print_info "Detected session type:', start)
block = source[start:end]
assert '[ -n "${XDG_SESSION_TYPE:-}" ]' in block
assert '[ -n "${WAYLAND_DISPLAY:-}" ]' in block
assert '[ -n "${DISPLAY:-}" ]' in block
assert '"$XDG_SESSION_TYPE"' not in block
assert '"$WAYLAND_DISPLAY"' not in block
assert '"$DISPLAY"' not in block
env = os.environ.copy()
env.pop("XDG_SESSION_TYPE", None)
env.pop("WAYLAND_DISPLAY", None)
env["DISPLAY"] = ":1"
result = subprocess.run(
[
"bash",
"-c",
'set -u; [ -n "${XDG_SESSION_TYPE:-}" ] && echo xdg; '
'[ -n "${DISPLAY:-}" ] && echo x11',
],
check=False,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "x11"
def test_agents_does_not_claim_requirements_are_consumed() -> None:
text = AGENTS.read_text(encoding="utf-8")
assert "consumed by `install.sh`" not in text
assert "`uv.lock` is authoritative" in text
assert "Do not edit `requirements/*.txt` by hand" in text
assert "https://just.systems" in text
def test_interactive_engine_boxes_share_one_width() -> None:
"""Boxes 4/5 used a wider frame and unpadded bullets, so the right edge
wandered. Keep every engine-menu echo at the same width as whisper.cpp."""
source = _installer_source()
start = source.index('echo " │ 1. WHISPER.CPP')
end = source.index("Choose engine [1-5]", start)
widths = []
for line in source[start:end].splitlines():
if 'echo " │' not in line and 'echo " ┌' not in line and 'echo " └' not in line:
continue
drawn = line.split("echo ", 1)[1].strip()
assert drawn[0] == drawn[-1] == '"'
widths.append(len(drawn[1:-1]))
assert widths
assert len(set(widths)) == 1, widths
assert "Best performance on NVIDIA GPUs (CUDA)" not in source[start:end]
assert "Fast on CPU with INT8 quantization" in source[start:end]
assert "Checksum-verified Hugging Face models" in source[start:end]