- install.py / uninstall.py: full Python installer for Linux, macOS, Windows
- detect_platform.py: shared Inkscape config path detection
- Windows config path: %APPDATA%\inkscape (native/winget/scoop) or
%LOCALAPPDATA%\Packages\org.inkscape.Inkscape* (MS Store)
- Windows desktop: Start Menu .lnk via PowerShell WScript.Shell COM object
- macOS: XDG / ~/Library/Application Support detection, no desktop step
- install.sh / uninstall.sh: reduced to one-line exec python3 wrappers
- eliminates SC1091 (source not followed) and SC2155 (declare+assign)
- scripts/detect_platform.sh: export INKSCAPE_CONFIG / INKSCAPE_INSTALL_METHOD
- fixes SC2034 (variables appear unused to shellcheck)
- assets/illuscape.ico: pre-rendered 16/32/48/64/128/256px ICO (ImageMagick)
- tests/test_install.py: pytest integration tests replacing test_install.bats
- cross-platform fake inkscape injected via PATH
- 12 tests covering backup, palettes, templates, prefs, uninstall restore
- ci.yml: add windows-latest matrix leg; shellcheck/inkscape Linux-only;
bats removed in favour of pytest integration tests; 27 tests total
222 lines
7 KiB
Python
222 lines
7 KiB
Python
"""
|
|
Integration tests for install.py and uninstall.py — cross-platform.
|
|
Replaces tests/test_install.bats.
|
|
|
|
Each test runs inside a fully isolated tmp_path with a fake inkscape binary
|
|
injected via PATH and an overridden config home pointing to tmp_path.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import platform
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO = Path(__file__).parent.parent
|
|
INSTALL_PY = REPO / "install.py"
|
|
UNINSTALL_PY = REPO / "uninstall.py"
|
|
|
|
_FAKE_INKSCAPE_SH = """\
|
|
#!/usr/bin/env sh
|
|
case "$1" in
|
|
--version) echo 'Inkscape 1.3.2 (1:1.3.2+202311252150+091e20ef0f)' ;;
|
|
esac
|
|
"""
|
|
|
|
_FAKE_INKSCAPE_BAT = """\
|
|
@echo off
|
|
if "%1"=="--version" echo Inkscape 1.3.2 (1:1.3.2+202311252150+091e20ef0f)
|
|
"""
|
|
|
|
|
|
def _make_fake_inkscape(bin_dir: Path) -> None:
|
|
"""Create a platform-appropriate fake inkscape binary in *bin_dir*."""
|
|
if platform.system() == "Windows":
|
|
bat = bin_dir / "inkscape.bat"
|
|
bat.write_text(_FAKE_INKSCAPE_BAT)
|
|
else:
|
|
script = bin_dir / "inkscape"
|
|
script.write_text(_FAKE_INKSCAPE_SH)
|
|
script.chmod(0o755)
|
|
|
|
|
|
def _config_dir(tmp_path: Path) -> Path:
|
|
"""
|
|
Return the expected config path matching detect_config() for this platform
|
|
when the env fixture overrides the config home to *tmp_path*.
|
|
"""
|
|
if platform.system() == "Windows":
|
|
# install.py on Windows: APPDATA/inkscape
|
|
return tmp_path / "config" / "inkscape"
|
|
else:
|
|
# install.py on Linux/macOS: XDG_CONFIG_HOME/inkscape
|
|
return tmp_path / "config" / "inkscape"
|
|
|
|
|
|
@pytest.fixture()
|
|
def env(tmp_path: Path):
|
|
"""
|
|
Isolated subprocess environment with a fake inkscape and a temp config dir.
|
|
On Linux/macOS: overrides HOME and XDG_CONFIG_HOME.
|
|
On Windows: overrides APPDATA and LOCALAPPDATA.
|
|
"""
|
|
bin_dir = tmp_path / "bin"
|
|
bin_dir.mkdir()
|
|
_make_fake_inkscape(bin_dir)
|
|
|
|
cfg_dir = _config_dir(tmp_path)
|
|
cfg_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
base_env = os.environ.copy()
|
|
base_env["PATH"] = str(bin_dir) + os.pathsep + base_env.get("PATH", "")
|
|
# Block flatpak / snap detection inside subprocesses
|
|
base_env.pop("FLATPAK_ID", None)
|
|
|
|
if platform.system() == "Windows":
|
|
base_env["APPDATA"] = str(tmp_path / "config")
|
|
base_env["LOCALAPPDATA"] = str(tmp_path / "local")
|
|
else:
|
|
base_env["XDG_CONFIG_HOME"] = str(tmp_path / "config")
|
|
base_env["HOME"] = str(tmp_path)
|
|
|
|
return {
|
|
"tmp_path": tmp_path,
|
|
"config_dir": cfg_dir,
|
|
"env": base_env,
|
|
}
|
|
|
|
|
|
def _run_install(env_fixture: dict, preset: str = "cc") -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
[sys.executable, str(INSTALL_PY), "--yes", f"--preset={preset}"],
|
|
env=env_fixture["env"],
|
|
cwd=str(REPO),
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
|
|
def _run_uninstall(env_fixture: dict) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
[sys.executable, str(UNINSTALL_PY)],
|
|
env=env_fixture["env"],
|
|
cwd=str(REPO),
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
|
|
# ── install tests ─────────────────────────────────────────────────────────────
|
|
|
|
def test_install_creates_backup(env):
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, result.stderr
|
|
backups = list(env["tmp_path"].glob("config/inkscape.bak-illuscape-*"))
|
|
assert backups, "No backup directory created"
|
|
|
|
|
|
def test_install_backup_is_idempotent(env):
|
|
_run_install(env)
|
|
first_backups = sorted(env["tmp_path"].glob("config/inkscape.bak-illuscape-*"))
|
|
_run_install(env)
|
|
second_backups = sorted(env["tmp_path"].glob("config/inkscape.bak-illuscape-*"))
|
|
assert first_backups == second_backups, "Second install created an extra backup"
|
|
|
|
|
|
def test_install_cc_key_file(env):
|
|
result = _run_install(env, preset="cc")
|
|
assert result.returncode == 0, result.stderr
|
|
assert (env["config_dir"] / "keys/illustrator-cc.xml").exists()
|
|
|
|
|
|
def test_install_cs6_key_file(env):
|
|
result = _run_install(env, preset="cs6")
|
|
assert result.returncode == 0, result.stderr
|
|
assert (env["config_dir"] / "keys/illustrator-cs6.xml").exists()
|
|
|
|
|
|
def test_install_palettes(env):
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, result.stderr
|
|
for name in (
|
|
"Illustrator-Defaults.gpl",
|
|
"Illustrator-Grays.gpl",
|
|
"Illustrator-Earth.gpl",
|
|
):
|
|
assert (env["config_dir"] / "palettes" / name).exists(), f"Missing palette: {name}"
|
|
|
|
|
|
def test_install_templates(env):
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, result.stderr
|
|
for name in (
|
|
"Letter.svg",
|
|
"A4.svg",
|
|
"Web-1920x1080.svg",
|
|
"Web-1280x720.svg",
|
|
"Print-CMYK-Letter.svg",
|
|
"Print-CMYK-A4.svg",
|
|
):
|
|
assert (env["config_dir"] / "templates" / name).exists(), f"Missing template: {name}"
|
|
|
|
|
|
def test_install_creates_prefs(env):
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, result.stderr
|
|
assert (env["config_dir"] / "preferences.xml").exists()
|
|
|
|
|
|
def test_install_prefs_contains_units(env):
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, result.stderr
|
|
content = (env["config_dir"] / "preferences.xml").read_text()
|
|
assert "px" in content or "pt" in content, "No unit setting found in preferences.xml"
|
|
|
|
|
|
def test_install_noninteractive(env):
|
|
"""--yes + --preset should exit 0 without any stdin."""
|
|
result = _run_install(env)
|
|
assert result.returncode == 0, f"Non-interactive install failed:\n{result.stderr}"
|
|
|
|
|
|
# ── uninstall tests ───────────────────────────────────────────────────────────
|
|
|
|
def test_uninstall_restores_prefs(env):
|
|
"""Original prefs.xml is restored after install + uninstall."""
|
|
original_xml = '<inkscape version="1.0" />'
|
|
(env["config_dir"] / "preferences.xml").write_text(original_xml)
|
|
|
|
_run_install(env)
|
|
# prefs should now contain the patched version
|
|
result = _run_uninstall(env)
|
|
assert result.returncode == 0, result.stderr
|
|
|
|
restored = (env["config_dir"] / "preferences.xml").read_text()
|
|
assert restored == original_xml, "prefs.xml was not restored to the original content"
|
|
|
|
|
|
def test_uninstall_removes_palettes(env):
|
|
_run_install(env)
|
|
result = _run_uninstall(env)
|
|
assert result.returncode == 0, result.stderr
|
|
for name in (
|
|
"Illustrator-Defaults.gpl",
|
|
"Illustrator-Grays.gpl",
|
|
"Illustrator-Earth.gpl",
|
|
):
|
|
assert not (
|
|
env["config_dir"] / "palettes" / name
|
|
).exists(), f"Palette was not removed: {name}"
|
|
|
|
|
|
def test_uninstall_removes_templates(env):
|
|
_run_install(env)
|
|
result = _run_uninstall(env)
|
|
assert result.returncode == 0, result.stderr
|
|
for name in ("Letter.svg", "A4.svg"):
|
|
assert not (
|
|
env["config_dir"] / "templates" / name
|
|
).exists(), f"Template was not removed: {name}"
|