Files
voculinux/tests/test_snap_packaging.py
T
Jatin K MalikandGitHub 4b6e81d441 fix(snap): type into native Wayland apps with ydotool (#823)
* fix(snap): inject into native Wayland apps via ydotool

Edge rev 7 only ships wtype and xdotool. GNOME rejects the virtual
keyboard protocol, so the snap falls back to xdotool and types only
into XWayland windows. Native GTK/Qt apps get nothing.

Stage ydotool, request the uinput plug, and prefer ydotool when wtype
is rejected. Distro ydotool 0.1.x still needs a writable /dev/uinput;
log `snap connect vocalinux:uinput` when the Snap cannot open it.

* docs(snap): v0.16.2 edge has no uinput plug

The store snap (0.16.2, latest/edge rev 7) does not declare uinput.
`sudo snap connect vocalinux:uinput` fails with "has no plug named
uinput" and dictation only reaches XWayland apps. Spell that out in
README, INSTALL, UPDATE, the website changelog, and the store copy so
people do not run the 0.17 connect command against rev 7.

* docs(snap): drop the unconfirmed 0.17 version from uinput notes

Greptile P2: the tree is still 0.16.2 and the release process does not
name the next tag. Tell people to refresh until `snap info` lists the
uinput plug, instead of waiting on a version we have not cut.
2026-09-12 12:58:35 -07:00

97 lines
4.2 KiB
Python

"""Structural tests for Snap packaging (issue #48)."""
from pathlib import Path
import yaml
REPO_ROOT = Path(__file__).resolve().parents[1]
SNAPCRAFT_YAML = REPO_ROOT / "snap" / "snapcraft.yaml"
DESKTOP_FILE = REPO_ROOT / "snap" / "gui" / "vocalinux.desktop"
SNAP_PNG = REPO_ROOT / "snap" / "gui" / "vocalinux.png"
def test_snapcraft_recipe_and_gui_assets() -> None:
assert SNAPCRAFT_YAML.is_file()
doc = yaml.safe_load(SNAPCRAFT_YAML.read_text(encoding="utf-8"))
assert doc["name"] == "vocalinux"
assert doc["base"] == "core24"
assert doc["summary"] == "Free offline voice dictation for Linux"
assert doc["license"] == "AGPL-3.0-only"
assert doc["icon"] == "snap/gui/vocalinux.png"
assert doc["website"] == "https://vocalinux.com"
assert doc["confinement"] == "strict"
assert doc["grade"] == "stable"
plugs = set((doc.get("apps") or {}).get("vocalinux", {}).get("plugs") or [])
assert "raw-input" in plugs
assert "audio-record" in plugs
assert "uinput" in plugs
stage = doc["parts"]["vocalinux"].get("stage-packages") or []
assert "ydotool" in stage
assert DESKTOP_FILE.is_file()
assert SNAP_PNG.is_file()
assert SNAP_PNG.stat().st_size > 0
def test_snap_puts_gnome_platform_first_on_ld_library_path() -> None:
"""core24 gdk-pixbuf finds libpixbufloader_svg.so only if gnome-platform wins.
Stage-packages pull a second gdk-pixbuf (no SVG loader) into
$SNAP/usr/lib/<triplet>. desktop-launch's query-loaders walks the first
LD_LIBRARY_PATH entry that ends in that triplet, so gnome-platform must
come first or About-page SVGs fail with "Image type svg is not supported".
"""
doc = yaml.safe_load(SNAPCRAFT_YAML.read_text(encoding="utf-8"))
env = (doc.get("apps") or {}).get("vocalinux", {}).get("environment") or {}
ld_path = env.get("LD_LIBRARY_PATH")
assert isinstance(ld_path, str)
assert ld_path.startswith("$SNAP/gnome-platform/usr/lib/$CRAFT_ARCH_TRIPLET:")
assert ld_path.endswith(":$LD_LIBRARY_PATH")
def test_snap_docs_warn_that_0162_has_no_uinput_plug() -> None:
"""v0.16.2 edge has no uinput plug; the connect command must not stand alone."""
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
install = (REPO_ROOT / "docs" / "INSTALL.md").read_text(encoding="utf-8")
update = (REPO_ROOT / "docs" / "UPDATE.md").read_text(encoding="utf-8")
changelog = (REPO_ROOT / "web" / "src" / "app" / "changelog" / "page.tsx").read_text(
encoding="utf-8"
)
snapcraft = SNAPCRAFT_YAML.read_text(encoding="utf-8")
for text in (readme, install, update, changelog, snapcraft):
uinput_lines = "\n".join(line for line in text.splitlines() if "uinput" in line.lower())
lowered = uinput_lines.lower()
assert "uinput" in lowered
assert "plug" in lowered and "no" in lowered
assert "0.17" not in uinput_lines
def test_snap_strips_pygobject_and_uses_gnome_gi() -> None:
"""Pip must not build PyGObject; GI comes from the gnome extension."""
text = SNAPCRAFT_YAML.read_text(encoding="utf-8")
doc = yaml.safe_load(text)
override_pull = doc["parts"]["vocalinux"]["override-pull"]
assert "PyGObject" in override_pull
assert "pyproject.toml" in override_pull
assert "gnome extension" in override_pull
stage = doc["parts"]["vocalinux"].get("stage-packages") or []
assert "python3-gi" not in stage
assert "python3-gi-cairo" not in stage
def test_release_publishes_snap_to_edge_and_candidate() -> None:
"""v* tags must ship the snap to the store, not attach it to GitHub."""
text = (REPO_ROOT / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8")
assert " publish-snap:\n" in text
job = text.split(" publish-snap:\n", 1)[1].split("\n deploy-website:", 1)[0]
assert "needs: build-and-release" in job
assert "snapcore/action-build@" in job
assert "snapcore/action-publish@" in job
assert "release: edge,candidate" in job
assert "release: stable" not in job
assert "SNAPCRAFT_STORE_CREDENTIALS is unset; cannot publish the snap" in job
assert "gh release upload" not in job
assert "action-gh-release" not in job
assert "upload-artifact" not in job