chore: format test files
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 19s
Python / lint (pull_request) Successful in 32s
Python / fmt (pull_request) Failing after 37s
Python / test (pull_request) Successful in 54s
C++ / build (pull_request) Successful in 1m57s
Rust / fmt (pull_request) Successful in 1m16s
Rust / build (pull_request) Successful in 1m56s
Python / buildcheck (pull_request) Successful in 2m44s
Rust / clippy (pull_request) Successful in 1m56s
C++ / clang-tidy (pull_request) Successful in 3m46s

This commit is contained in:
2026-07-08 17:01:25 +02:00
parent 9300da258e
commit 0ae6d6fc55
2 changed files with 283 additions and 214 deletions
+149 -121
View File
@@ -1,167 +1,195 @@
from __future__ import annotations
import pytest
from pathlib import Path
import pytest
from zshell.utils import schemepalettes as sp
@pytest.fixture
def tmp_schemes(tmp_path: Path) -> Path:
schemes = tmp_path / "schemes"
schemes.mkdir()
schemes = tmp_path / "schemes"
schemes.mkdir()
gmedium = schemes / "gruvbox" / "medium"
gmedium.mkdir(parents=True)
(gmedium / "dark.txt").write_text("background 101415\nonBackground e0e3e4\nprimary 81d3e0\nsurface 1c2021\n")
(gmedium / "light.txt").write_text("background fbf1c7\nonBackground 3c3836\nprimary 6b5f10\nsurface fbf1c7\n")
gmedium = schemes / "gruvbox" / "medium"
gmedium.mkdir(parents=True)
(gmedium / "dark.txt").write_text(
"background 101415\nonBackground e0e3e4\nprimary 81d3e0\nsurface 1c2021\n"
)
(gmedium / "light.txt").write_text(
"background fbf1c7\nonBackground 3c3836\nprimary 6b5f10\nsurface fbf1c7\n"
)
ghard = schemes / "gruvbox" / "hard"
ghard.mkdir(parents=True)
(ghard / "dark.txt").write_text("background 0b0d0e\nprimary 81d3e0\n")
ghard = schemes / "gruvbox" / "hard"
ghard.mkdir(parents=True)
(ghard / "dark.txt").write_text("background 0b0d0e\nprimary 81d3e0\n")
cmocha = schemes / "catppuccin" / "mocha"
cmocha.mkdir(parents=True)
(cmocha / "dark.txt").write_text("background 1e1e2e\nprimary cba6f7\nsecondary 756294\nsurface 313244\n")
(cmocha / "mauve-dark.txt").write_text("background 1e1e2e\nprimary cba6f7\nsecondary 756294\nsurface 313244\n")
(cmocha / "green-dark.txt").write_text("background 1e1e2e\nprimary a6e3a1\nsecondary 5b8964\nsurface 313244\n")
cmocha = schemes / "catppuccin" / "mocha"
cmocha.mkdir(parents=True)
(cmocha / "dark.txt").write_text(
"background 1e1e2e\nprimary cba6f7\nsecondary 756294\nsurface 313244\n"
)
(cmocha / "mauve-dark.txt").write_text(
"background 1e1e2e\nprimary cba6f7\nsecondary 756294\nsurface 313244\n"
)
(cmocha / "green-dark.txt").write_text(
"background 1e1e2e\nprimary a6e3a1\nsecondary 5b8964\nsurface 313244\n"
)
clatte = schemes / "catppuccin" / "latte"
clatte.mkdir(parents=True)
(clatte / "light.txt").write_text("background eff1f5\nprimary 8839ef\nsecondary c2b8d0\nsurface ccd0da\n")
(clatte / "mauve-light.txt").write_text("background eff1f5\nprimary 8839ef\nsecondary c2b8d0\nsurface ccd0da\n")
clatte = schemes / "catppuccin" / "latte"
clatte.mkdir(parents=True)
(clatte / "light.txt").write_text(
"background eff1f5\nprimary 8839ef\nsecondary c2b8d0\nsurface ccd0da\n"
)
(clatte / "mauve-light.txt").write_text(
"background eff1f5\nprimary 8839ef\nsecondary c2b8d0\nsurface ccd0da\n"
)
cextra = schemes / "extra" / "default"
cextra.mkdir(parents=True)
(cextra / "dark.txt").write_text(
"# this is a comment\n\nbackground 000000\nprimary ffffff\n\n # indented comment \n secondary cccccc\n"
)
cextra = schemes / "extra" / "default"
cextra.mkdir(parents=True)
(cextra / "dark.txt").write_text(
"# this is a comment\n\nbackground 000000\nprimary ffffff\n\n # indented comment \n secondary cccccc\n"
)
return schemes
return schemes
class TestParseTxt:
def test_basic(self, tmp_schemes):
path = tmp_schemes / "gruvbox" / "medium" / "dark.txt"
colors = sp._parse_txt(path)
assert colors["background"] == "#101415"
assert colors["primary"] == "#81d3e0"
assert colors["surface"] == "#1c2021"
def test_basic(self, tmp_schemes):
path = tmp_schemes / "gruvbox" / "medium" / "dark.txt"
colors = sp._parse_txt(path)
assert colors["background"] == "#101415"
assert colors["primary"] == "#81d3e0"
assert colors["surface"] == "#1c2021"
def test_adds_hash_prefix(self, tmp_schemes):
path = tmp_schemes / "gruvbox" / "medium" / "dark.txt"
colors = sp._parse_txt(path)
for v in colors.values():
assert v.startswith("#"), f"value {v!r} missing # prefix"
def test_adds_hash_prefix(self, tmp_schemes):
path = tmp_schemes / "gruvbox" / "medium" / "dark.txt"
colors = sp._parse_txt(path)
for v in colors.values():
assert v.startswith("#"), f"value {v!r} missing # prefix"
def test_skips_comments_and_empty_lines(self, tmp_schemes):
path = tmp_schemes / "extra" / "default" / "dark.txt"
colors = sp._parse_txt(path)
assert colors["background"] == "#000000"
assert colors["primary"] == "#ffffff"
assert colors["secondary"] == "#cccccc"
assert len(colors) == 3
def test_skips_comments_and_empty_lines(self, tmp_schemes):
path = tmp_schemes / "extra" / "default" / "dark.txt"
colors = sp._parse_txt(path)
assert colors["background"] == "#000000"
assert colors["primary"] == "#ffffff"
assert colors["secondary"] == "#cccccc"
assert len(colors) == 3
class TestDiscoverSchemes:
def test_discovers_all_schemes(self):
schemes = sp._discover_schemes()
assert "gruvbox" in schemes
assert "catppuccin" in schemes
assert "everforest" in schemes
assert "nord" in schemes
assert len(schemes) >= 10
def test_discovers_all_schemes(self):
schemes = sp._discover_schemes()
assert "gruvbox" in schemes
assert "catppuccin" in schemes
assert "everforest" in schemes
assert "nord" in schemes
assert len(schemes) >= 10
def test_scheme_has_variants(self):
schemes = sp._discover_schemes()
gruvbox = schemes["gruvbox"]
var_ids = {v.id for v in gruvbox.variants}
assert "medium" in var_ids
assert "hard" in var_ids
assert "soft" in var_ids
def test_scheme_has_variants(self):
schemes = sp._discover_schemes()
gruvbox = schemes["gruvbox"]
var_ids = {v.id for v in gruvbox.variants}
assert "medium" in var_ids
assert "hard" in var_ids
assert "soft" in var_ids
def test_variant_has_modes(self):
schemes = sp._discover_schemes()
gmedium = next(v for v in schemes["gruvbox"].variants if v.id == "medium")
assert "dark" in gmedium.modes
assert "light" in gmedium.modes
def test_variant_has_modes(self):
schemes = sp._discover_schemes()
gmedium = next(
v for v in schemes["gruvbox"].variants if v.id == "medium"
)
assert "dark" in gmedium.modes
assert "light" in gmedium.modes
def test_catppuccin_has_accents(self):
schemes = sp._discover_schemes()
mocha = next(v for v in schemes["catppuccin"].variants if v.id == "mocha")
assert "mauve" in mocha.accents
assert "green" in mocha.accents
assert "rosewater" in mocha.accents
assert len(mocha.accents) >= 14
def test_catppuccin_has_accents(self):
schemes = sp._discover_schemes()
mocha = next(
v for v in schemes["catppuccin"].variants if v.id == "mocha"
)
assert "mauve" in mocha.accents
assert "green" in mocha.accents
assert "rosewater" in mocha.accents
assert len(mocha.accents) >= 14
def test_non_accent_scheme_has_no_accents(self):
schemes = sp._discover_schemes()
gmedium = next(v for v in schemes["gruvbox"].variants if v.id == "medium")
assert gmedium.accents == ()
def test_non_accent_scheme_has_no_accents(self):
schemes = sp._discover_schemes()
gmedium = next(
v for v in schemes["gruvbox"].variants if v.id == "medium"
)
assert gmedium.accents == ()
class TestGetPalette:
def test_loads_basic_palette(self):
pal = sp.get_palette("gruvbox", "medium", "dark")
assert pal.scheme == "gruvbox"
assert pal.variant == "medium"
assert pal.mode == "dark"
assert pal.colors["background"].startswith("#")
assert pal.colors["primary"].startswith("#")
def test_loads_basic_palette(self):
pal = sp.get_palette("gruvbox", "medium", "dark")
assert pal.scheme == "gruvbox"
assert pal.variant == "medium"
assert pal.mode == "dark"
assert pal.colors["background"].startswith("#")
assert pal.colors["primary"].startswith("#")
def test_loads_accent_palette(self):
pal = sp.get_palette("catppuccin", "mocha", "dark", accent="mauve")
assert pal.accent == "mauve"
assert pal.colors["primary"] == "#cba6f7"
def test_loads_accent_palette(self):
pal = sp.get_palette("catppuccin", "mocha", "dark", accent="mauve")
assert pal.accent == "mauve"
assert pal.colors["primary"] == "#cba6f7"
def test_different_accent_changes_colors(self):
mauve = sp.get_palette("catppuccin", "mocha", "dark", accent="mauve")
green = sp.get_palette("catppuccin", "mocha", "dark", accent="green")
assert mauve.colors["primary"] != green.colors["primary"]
assert mauve.colors["secondary"] != green.colors["secondary"]
def test_different_accent_changes_colors(self):
mauve = sp.get_palette("catppuccin", "mocha", "dark", accent="mauve")
green = sp.get_palette("catppuccin", "mocha", "dark", accent="green")
assert mauve.colors["primary"] != green.colors["primary"]
assert mauve.colors["secondary"] != green.colors["secondary"]
def test_unknown_scheme_raises(self):
with pytest.raises(KeyError, match="Unknown scheme 'nope'"):
sp.get_palette("nope", "medium", "dark")
def test_unknown_scheme_raises(self):
with pytest.raises(KeyError, match="Unknown scheme 'nope'"):
sp.get_palette("nope", "medium", "dark")
def test_unknown_variant_raises(self):
with pytest.raises(KeyError, match="Unknown variant 'bogus' for scheme 'gruvbox'"):
sp.get_palette("gruvbox", "bogus", "dark")
def test_unknown_variant_raises(self):
with pytest.raises(
KeyError, match="Unknown variant 'bogus' for scheme 'gruvbox'"
):
sp.get_palette("gruvbox", "bogus", "dark")
def test_unknown_accent_falls_back(self):
pal = sp.get_palette("catppuccin", "mocha", "dark", accent="nonexistent")
assert pal.accent == "nonexistent"
assert pal.colors["primary"] is not None
def test_unknown_accent_falls_back(self):
pal = sp.get_palette(
"catppuccin", "mocha", "dark", accent="nonexistent"
)
assert pal.accent == "nonexistent"
assert pal.colors["primary"] is not None
def test_accent_on_non_accent_scheme(self):
pal = sp.get_palette("gruvbox", "medium", "dark", accent="mauve")
assert pal.colors is not None
def test_accent_on_non_accent_scheme(self):
pal = sp.get_palette("gruvbox", "medium", "dark", accent="mauve")
assert pal.colors is not None
def test_non_existent_mode_raises(self):
with pytest.raises(FileNotFoundError):
sp.get_palette("catppuccin", "mocha", "light")
def test_non_existent_mode_raises(self):
with pytest.raises(FileNotFoundError):
sp.get_palette("catppuccin", "mocha", "light")
class TestListSchemes:
def test_returns_dict(self):
schemes = sp.list_schemes()
assert isinstance(schemes, dict)
def test_returns_dict(self):
schemes = sp.list_schemes()
assert isinstance(schemes, dict)
def test_includes_known_schemes(self):
schemes = sp.list_schemes()
assert "catppuccin" in schemes
assert "gruvbox" in schemes
def test_includes_known_schemes(self):
schemes = sp.list_schemes()
assert "catppuccin" in schemes
assert "gruvbox" in schemes
class TestResolvePreset:
def test_two_parts(self):
assert sp.resolve_preset("gruvbox:medium") == ("gruvbox", "medium")
def test_two_parts(self):
assert sp.resolve_preset("gruvbox:medium") == ("gruvbox", "medium")
def test_three_parts(self):
with pytest.raises(ValueError, match="Invalid preset spec"):
sp.resolve_preset("catppuccin:mocha:mauve")
def test_three_parts(self):
with pytest.raises(ValueError, match="Invalid preset spec"):
sp.resolve_preset("catppuccin:mocha:mauve")
def test_one_part(self):
assert sp.resolve_preset("default") == ("default", "default")
def test_one_part(self):
assert sp.resolve_preset("default") == ("default", "default")
def test_edge_spaces(self):
assert sp.resolve_preset(" catppuccin : mocha ") == (" catppuccin ", " mocha ")
def test_edge_spaces(self):
assert sp.resolve_preset(" catppuccin : mocha ") == (
" catppuccin ",
" mocha ",
)
+134 -93
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
from subprocess import CompletedProcess
from unittest.mock import patch, call
from unittest.mock import call, patch
from typer.testing import CliRunner
from zshell.subcommands.shell import app
@@ -10,122 +10,163 @@ runner = CliRunner()
def invoke(*args: str):
result = runner.invoke(app, args)
if result.exit_code != 0:
raise RuntimeError(result.output)
return result
result = runner.invoke(app, args)
if result.exit_code != 0:
raise RuntimeError(result.output)
return result
class TestKill:
@patch("zshell.subcommands.shell.subprocess.run")
def test_kill_runs_qs_kill_success(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"Killed abc\n")
invoke("kill")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "kill"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_kill_runs_qs_kill_success(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"Killed abc\n")
invoke("kill")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "kill"], capture_output=True
)
@patch("zshell.subcommands.shell.subprocess.run")
def test_kill_no_instance_errors(self, mock_run):
mock_run.return_value = CompletedProcess([], 255, b"", b"No running instances\n")
result = runner.invoke(app, ["kill"])
assert result.exit_code != 0
assert "No running instance to kill" in result.output
@patch("zshell.subcommands.shell.subprocess.run")
def test_kill_no_instance_errors(self, mock_run):
mock_run.return_value = CompletedProcess(
[], 255, b"", b"No running instances\n"
)
result = runner.invoke(app, ["kill"])
assert result.exit_code != 0
assert "No running instance to kill" in result.output
class TestStart:
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_default_daemon(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"Launching config\n")
invoke("start")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "-n", "-d"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_default_daemon(self, mock_run):
mock_run.return_value = CompletedProcess(
[], 0, b"", b"Launching config\n"
)
invoke("start")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "-n", "-d"], capture_output=True
)
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_no_daemon(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"Launching config\n")
invoke("start", "--no-daemon")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "-n"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_no_daemon(self, mock_run):
mock_run.return_value = CompletedProcess(
[], 0, b"", b"Launching config\n"
)
invoke("start", "--no-daemon")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "-n"], capture_output=True
)
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_already_running_errors(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"An instance of this configuration is already running.\n", b"")
result = runner.invoke(app, ["start"])
assert result.exit_code != 0
assert "already running" in result.output
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_already_running_errors(self, mock_run):
mock_run.return_value = CompletedProcess(
[],
0,
b"An instance of this configuration is already running.\n",
b"",
)
result = runner.invoke(app, ["start"])
assert result.exit_code != 0
assert "already running" in result.output
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_other_failure_errors(self, mock_run):
mock_run.return_value = CompletedProcess([], 1, b"", b"Config error\n")
result = runner.invoke(app, ["start"])
assert result.exit_code != 0
assert "Config error" in result.output
@patch("zshell.subcommands.shell.subprocess.run")
def test_start_other_failure_errors(self, mock_run):
mock_run.return_value = CompletedProcess([], 1, b"", b"Config error\n")
result = runner.invoke(app, ["start"])
assert result.exit_code != 0
assert "Config error" in result.output
class TestShow:
@patch("zshell.subcommands.shell.subprocess.run")
def test_show_runs_ipc_show(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"target visibilities\n", b"")
result = invoke("show")
assert "target visibilities" in result.output
mock_run.assert_called_once_with(["qs", "-c", "zshell", "ipc", "show"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_show_runs_ipc_show(self, mock_run):
mock_run.return_value = CompletedProcess(
[], 0, b"target visibilities\n", b""
)
result = invoke("show")
assert "target visibilities" in result.output
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "ipc", "show"], capture_output=True
)
class TestLog:
@patch("zshell.subcommands.shell.subprocess.run")
def test_log_runs_qs_log(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"log output\n", b"")
invoke("log")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "log"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_log_runs_qs_log(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"log output\n", b"")
invoke("log")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "log"], capture_output=True
)
class TestLock:
@patch("zshell.subcommands.shell.subprocess.run")
def test_lock_runs_ipc_call_lock(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("lock")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "ipc", "call", "lock", "lock"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_lock_runs_ipc_call_lock(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("lock")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "ipc", "call", "lock", "lock"],
capture_output=True,
)
class TestCall:
@patch("zshell.subcommands.shell.subprocess.run")
def test_call_no_args(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("call", "target", "method")
mock_run.assert_called_once_with(["qs", "-c", "zshell", "ipc", "call", "target", "method"], capture_output=True)
@patch("zshell.subcommands.shell.subprocess.run")
def test_call_no_args(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("call", "target", "method")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "ipc", "call", "target", "method"],
capture_output=True,
)
@patch("zshell.subcommands.shell.subprocess.run")
def test_call_with_args(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("call", "target", "method", "arg1", "arg2")
mock_run.assert_called_once_with(
["qs", "-c", "zshell", "ipc", "call", "target", "method", "arg1", "arg2"],
capture_output=True,
)
@patch("zshell.subcommands.shell.subprocess.run")
def test_call_with_args(self, mock_run):
mock_run.return_value = CompletedProcess([], 0, b"", b"")
invoke("call", "target", "method", "arg1", "arg2")
mock_run.assert_called_once_with(
[
"qs",
"-c",
"zshell",
"ipc",
"call",
"target",
"method",
"arg1",
"arg2",
],
capture_output=True,
)
class TestRestart:
@patch("zshell.subcommands.shell.start_instance")
@patch("zshell.subcommands.shell.subprocess.run")
def test_restart_kills_then_starts(self, mock_run, mock_start):
mock_run.side_effect = [
CompletedProcess([], 0, b"", b"Killed abc\n"), # first kill (captured)
CompletedProcess([], 255, b"", b""), # poll → no instance
]
invoke("restart")
assert mock_run.call_args_list == [
call(["qs", "-c", "zshell", "kill"], capture_output=True),
call(["qs", "-c", "zshell", "kill"], capture_output=True),
]
mock_start.assert_called_once_with(no_daemon=False)
@patch("zshell.subcommands.shell.start_instance")
@patch("zshell.subcommands.shell.subprocess.run")
def test_restart_kills_then_starts(self, mock_run, mock_start):
mock_run.side_effect = [
CompletedProcess(
[], 0, b"", b"Killed abc\n"
), # first kill (captured)
CompletedProcess([], 255, b"", b""), # poll → no instance
]
invoke("restart")
assert mock_run.call_args_list == [
call(["qs", "-c", "zshell", "kill"], capture_output=True),
call(["qs", "-c", "zshell", "kill"], capture_output=True),
]
mock_start.assert_called_once_with(no_daemon=False)
@patch("zshell.subcommands.shell.start_instance")
@patch("zshell.subcommands.shell.subprocess.run")
def test_restart_no_daemon(self, mock_run, mock_start):
mock_run.side_effect = [
CompletedProcess([], 0, b"", b"Killed abc\n"),
CompletedProcess([], 255, b"", b""),
]
invoke("restart", "--no-daemon")
assert mock_run.call_args_list == [
call(["qs", "-c", "zshell", "kill"], capture_output=True),
call(["qs", "-c", "zshell", "kill"], capture_output=True),
]
mock_start.assert_called_once_with(no_daemon=True)
@patch("zshell.subcommands.shell.start_instance")
@patch("zshell.subcommands.shell.subprocess.run")
def test_restart_no_daemon(self, mock_run, mock_start):
mock_run.side_effect = [
CompletedProcess([], 0, b"", b"Killed abc\n"),
CompletedProcess([], 255, b"", b""),
]
invoke("restart", "--no-daemon")
assert mock_run.call_args_list == [
call(["qs", "-c", "zshell", "kill"], capture_output=True),
call(["qs", "-c", "zshell", "kill"], capture_output=True),
]
mock_start.assert_called_once_with(no_daemon=True)