testsAndMisc/python_pkg/vscode_optimizer/tests/test_io.py
Krzysztof kuhy Rudnicki 9d12c03ccc vscode_optimizer: move under python_pkg and cover 0% -> 100%
meta/scripts/optimize_vscode.py was 498 lines against a 250-line cap and
completely untested, and at that path the repo-wide gate (--cov=python_pkg,
fail_under = 100) could not see it. Moved verbatim to
python_pkg/vscode_optimizer/_optimize.py - git records a pure rename, and
the content is byte-identical to HEAD.

The move and the tests are one commit by necessity: any intermediate state
with the module under python_pkg/ but uncovered turns every pytest run in
the repo red, including ci-mirror on pre-push for unrelated work.

Running it by path no longer works after the move (sys.path[0] would be the
module's own directory), so __main__.py replaces it, following the existing
focus_policy convention rather than a sys.path insert. Nothing invoked the
old path: grep over .sh/.desktop/.service/.yml/.py found zero callers.

98 tests reach 100% line and branch coverage. Patching is confined to real
boundaries - subprocess.run, the module-level Path, builtins.input, sys.argv
- so the forthcoming split has few monkeypatch targets to re-point.

Writing the tests surfaced a pre-existing bug, left unfixed here and pinned
by a strict xfail: _VENDOR_KW's "ati" key is a substring of "VGA compatible
controller", which appears in every lspci display line, so it matches before
"intel" is ever tried and Intel iGPUs are misreported as AMD.

Verified: python3 -m python_pkg.vscode_optimizer --dry-run reproduces the
pre-move 18-change plan and writes nothing; the real settings.json is
untouched; repo-wide pytest is 1846 passed at 100%; over-cap files 37 -> 36.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 13:10:10 +02:00

231 lines
7.9 KiB
Python

"""Tests for the filesystem layer: variant discovery, the JSONC parser,
backups, and the settings/flags readers and writers.
These use real files under ``tmp_path``. Nothing in this module may resolve a
path inside the running user's real ``~/.config``.
"""
from __future__ import annotations
import json
import shutil
from typing import TYPE_CHECKING
import pytest
from python_pkg.vscode_optimizer import _optimize as opt
if TYPE_CHECKING:
from pathlib import Path
# --------------------------------------------------------------------------- #
# _parse_jsonc
# --------------------------------------------------------------------------- #
def test_parse_jsonc_reads_plain_json() -> None:
"""The parser is a superset of JSON."""
assert opt._parse_jsonc('{"a": 1}') == {"a": 1}
def test_parse_jsonc_strips_line_and_block_comments() -> None:
"""Both VS Code comment styles are removed before parsing."""
text = """
{
// a line comment
"a": 1, /* an inline block */
/* a
multi-line block */
"b": 2
}
"""
assert opt._parse_jsonc(text) == {"a": 1, "b": 2}
def test_parse_jsonc_keeps_comment_markers_inside_strings() -> None:
"""A // or /* inside a string literal is data, not a comment."""
text = '{"url": "https://example.com/*x*/", "path": "// not a comment"}'
assert opt._parse_jsonc(text) == {
"url": "https://example.com/*x*/",
"path": "// not a comment",
}
def test_parse_jsonc_handles_escaped_quotes_in_strings() -> None:
"""A backslash-escaped quote does not end the string."""
assert opt._parse_jsonc(r'{"a": "he said \"hi\""}') == {"a": 'he said "hi"'}
def test_parse_jsonc_drops_trailing_commas() -> None:
"""Trailing commas are legal in JSONC, before both } and ]."""
assert opt._parse_jsonc('{"a": [1, 2,], "b": 3,}') == {"a": [1, 2], "b": 3}
def test_parse_jsonc_treats_an_empty_document_as_no_settings() -> None:
"""A blank or comment-only file parses to an empty mapping, not an error."""
assert opt._parse_jsonc("") == {}
assert opt._parse_jsonc(" \n\t ") == {}
assert opt._parse_jsonc("// nothing but a comment") == {}
def test_parse_jsonc_tolerates_a_comment_without_a_trailing_newline() -> None:
"""A line comment running to end-of-file terminates cleanly."""
assert opt._parse_jsonc('{"a": 1}\n// trailing') == {"a": 1}
def test_parse_jsonc_tolerates_an_unterminated_block_comment() -> None:
"""An unclosed /* swallows the rest of the file rather than looping."""
assert opt._parse_jsonc('{"a": 1}\n/* never closed') == {"a": 1}
def test_parse_jsonc_tolerates_an_unterminated_string() -> None:
"""An unclosed quote consumes to end-of-input and then fails to parse."""
with pytest.raises(json.JSONDecodeError):
opt._parse_jsonc('{"a": "unterminated')
# --------------------------------------------------------------------------- #
# _discover_variants
# --------------------------------------------------------------------------- #
def test_discover_variants_finds_an_installation_by_its_settings_file(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""A settings.json on disk is enough, even with no binary on PATH."""
settings = tmp_path / ".config" / "VSCodium" / "User" / "settings.json"
settings.parent.mkdir(parents=True)
settings.write_text("{}")
monkeypatch.setattr(opt.Path, "home", classmethod(lambda _cls: tmp_path))
monkeypatch.setattr(shutil, "which", lambda _b: None)
found = opt._discover_variants()
assert [v.name for v in found] == ["VSCodium"]
assert found[0].settings == settings
def test_discover_variants_finds_an_installation_by_its_binary(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""A binary on PATH counts even before the settings file exists."""
monkeypatch.setattr(opt.Path, "home", classmethod(lambda _cls: tmp_path))
monkeypatch.setattr(
shutil, "which", lambda b: "/usr/bin/code" if b == "code" else None
)
found = opt._discover_variants()
assert [v.name for v in found] == ["VS Code (stable)"]
assert found[0].flags == tmp_path / ".config" / "code-flags.conf"
def test_discover_variants_returns_nothing_when_no_editor_is_installed(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""A machine with no VS Code variant yields an empty list."""
monkeypatch.setattr(opt.Path, "home", classmethod(lambda _cls: tmp_path))
monkeypatch.setattr(shutil, "which", lambda _b: None)
assert opt._discover_variants() == []
def test_discover_variants_finds_every_installed_variant(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""All three known variants can coexist."""
monkeypatch.setattr(opt.Path, "home", classmethod(lambda _cls: tmp_path))
monkeypatch.setattr(shutil, "which", lambda _b: "/usr/bin/x")
assert len(opt._discover_variants()) == 3
# --------------------------------------------------------------------------- #
# _backup
# --------------------------------------------------------------------------- #
def test_backup_copies_the_file_and_returns_the_destination(tmp_path: Path) -> None:
"""The backup keeps the original content and leaves the source in place."""
src = tmp_path / "settings.json"
src.write_text('{"a": 1}')
dst = opt._backup(src)
assert dst is not None
assert dst.read_text() == '{"a": 1}'
assert src.exists()
assert dst.name.endswith(".bak")
def test_backup_of_a_missing_file_is_a_no_op(tmp_path: Path) -> None:
"""Nothing to back up is not an error."""
assert opt._backup(tmp_path / "absent.json") is None
# --------------------------------------------------------------------------- #
# settings and flags round-trips
# --------------------------------------------------------------------------- #
def test_read_settings_parses_jsonc_from_disk(tmp_path: Path) -> None:
"""Comments in a real settings.json are tolerated."""
path = tmp_path / "settings.json"
path.write_text('{\n // comment\n "editor.fontSize": 14\n}')
assert opt._read_settings(path) == {"editor.fontSize": 14}
def test_read_settings_of_a_missing_file_is_empty(tmp_path: Path) -> None:
"""A variant with no settings yet reads as no settings."""
assert opt._read_settings(tmp_path / "absent.json") == {}
def test_write_settings_merges_the_plan_over_the_current_values(
tmp_path: Path,
) -> None:
"""Proposed values win; untouched user settings survive."""
path = tmp_path / "nested" / "settings.json"
current: dict[str, object] = {"editor.fontSize": 14, "a": 1}
opts = [
opt._Opt("a", 2, "reason"),
opt._Opt(key="b", value=True, reason="reason"),
]
opt._write_settings(path, current, opts)
assert json.loads(path.read_text()) == {"editor.fontSize": 14, "a": 2, "b": True}
def test_write_settings_creates_the_parent_directory(tmp_path: Path) -> None:
"""A brand-new variant directory is created rather than failing."""
path = tmp_path / "deep" / "nested" / "settings.json"
opt._write_settings(path, {}, [])
assert path.exists()
def test_read_flags_ignores_comments_and_blank_lines(tmp_path: Path) -> None:
"""The flags file is a plain list, with # comments."""
path = tmp_path / "code-flags.conf"
path.write_text(
"# a comment\n\n --enable-gpu-rasterization \n--enable-zero-copy\n"
)
assert opt._read_flags(path) == [
"--enable-gpu-rasterization",
"--enable-zero-copy",
]
def test_read_flags_of_a_missing_file_is_empty(tmp_path: Path) -> None:
"""No flags file means no existing flags."""
assert opt._read_flags(tmp_path / "absent.conf") == []
def test_write_flags_round_trips_through_read_flags(tmp_path: Path) -> None:
"""What is written is what is read back."""
path = tmp_path / "code-flags.conf"
flags = ["--enable-gpu-rasterization", "--ignore-gpu-blocklist"]
opt._write_flags(path, flags)
assert opt._read_flags(path) == flags
assert path.read_text().endswith("\n")