* feat(settings): delete unused downloaded speech models Add a Remove Model dropdown on the Speech Model page so whisper.cpp, Whisper, and VOSK downloads can be deleted from Settings. The currently selected model is marked in use and cannot be removed until you switch away from it. Fixes #650 * fix(settings): move unused model deletion below the picker Unused downloads is its own group under the model info card. Only leftover files appear, each with a Delete button. The group hides when empty. * fix(settings): collapse unused downloads into an expander Keep leftover models behind one row so a long list does not stretch the Speech Model page. Expand to delete files one at a time; the list scrolls once it grows past a few entries. * fix(settings): keep leftover model list from clipping in the expander Wrap the unused-downloads list so the expander can show a few rows and scroll the rest, instead of painting only the first leftover.
52 lines
2.5 KiB
Python
52 lines
2.5 KiB
Python
"""Tests for the XDG base-directory helpers in vocalinux.utils.paths."""
|
|
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
from vocalinux.utils import paths
|
|
|
|
|
|
class TestXdgHelpers:
|
|
def test_defaults_when_env_unset(self):
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
assert paths.xdg_config_home() == os.path.expanduser("~/.config")
|
|
assert paths.xdg_data_home() == os.path.expanduser("~/.local/share")
|
|
assert paths.config_dir() == os.path.expanduser("~/.config/vocalinux")
|
|
assert paths.data_dir() == os.path.expanduser("~/.local/share/vocalinux")
|
|
assert paths.models_dir() == os.path.expanduser("~/.local/share/vocalinux/models")
|
|
|
|
def test_honours_xdg_environment(self):
|
|
env = {
|
|
"XDG_CONFIG_HOME": "/run/user/1000/cfg",
|
|
"XDG_DATA_HOME": "/run/user/1000/data",
|
|
}
|
|
with patch.dict(os.environ, env, clear=True):
|
|
assert paths.config_dir() == "/run/user/1000/cfg/vocalinux"
|
|
assert paths.data_dir() == "/run/user/1000/data/vocalinux"
|
|
assert paths.models_dir() == "/run/user/1000/data/vocalinux/models"
|
|
|
|
def test_empty_env_falls_back_to_default(self):
|
|
# The XDG spec says empty values must be treated as unset.
|
|
with patch.dict(os.environ, {"XDG_CONFIG_HOME": "", "XDG_DATA_HOME": ""}, clear=True):
|
|
assert paths.xdg_config_home() == os.path.expanduser("~/.config")
|
|
assert paths.xdg_data_home() == os.path.expanduser("~/.local/share")
|
|
|
|
def test_flatpak_style_data_home(self):
|
|
# Flatpak points XDG_DATA_HOME at ~/.var/app/<app-id>/data.
|
|
flatpak_data = "/home/user/.var/app/com.vocalinux.Vocalinux/data"
|
|
with patch.dict(os.environ, {"XDG_DATA_HOME": flatpak_data}, clear=True):
|
|
assert paths.models_dir() == os.path.join(flatpak_data, "vocalinux", "models")
|
|
|
|
def test_is_within_directory(self, tmp_path):
|
|
nested = tmp_path / "models" / "ggml-tiny.bin"
|
|
nested.parent.mkdir()
|
|
nested.write_bytes(b"x")
|
|
|
|
assert paths.is_within_directory(str(nested), str(tmp_path / "models"))
|
|
assert not paths.is_within_directory(str(tmp_path / "models"), str(tmp_path / "models"))
|
|
assert paths.is_within_directory(
|
|
str(tmp_path / "models"), str(tmp_path / "models"), allow_root=True
|
|
)
|
|
assert not paths.is_within_directory(str(tmp_path / "other"), str(tmp_path / "models"))
|
|
assert not paths.is_within_directory("/etc/passwd", str(tmp_path / "models"))
|