"""Tests for scripts/merge_prefs.py""" import shutil from pathlib import Path from xml.etree import ElementTree as ET import pytest import merge_prefs FIXTURES = Path(__file__).parent / "fixtures" def _parse(path: Path) -> ET.Element: return ET.parse(path).getroot() def _attr(root: ET.Element, *id_path: str) -> dict: """Walk id_path through nested elements, return attribs.""" node = root for id_val in id_path: node = next( (c for c in node if c.get("id") == id_val), None ) assert node is not None, f"Node with id={id_val!r} not found" return dict(node.attrib) # ── merge into existing prefs ───────────────────────────────────────────────── def test_merge_changes_patched_attributes(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "options", "scroll")["mousewheel-zoom"] == "false" assert _attr(root, "options", "zoom")["mousewheel-zoom-ctrl"] == "true" assert _attr(root, "tools", "nodes")["square-handles"] == "true" def test_merge_creates_missing_nodes(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "options", "new-group")["new-attr"] == "new-value" def test_merge_preserves_user_nodes(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-user-custom.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "options", "user-custom-setting")["my-value"] == "keep-this" assert _attr(root, "tools", "user-tool-pref")["color"] == "#ff0000" def test_merge_preserves_existing_unknown_attributes(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-user-custom.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "tools", "nodes").get("user-pref") == "preserved" def test_merge_is_idempotent(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") content_after_first = prefs.read_text() merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") content_after_second = prefs.read_text() assert content_after_first == content_after_second # ── lenient: no prefs file ──────────────────────────────────────────────────── def test_merge_creates_prefs_when_missing(tmp_path): prefs = tmp_path / "inkscape" / "preferences.xml" merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") assert prefs.exists() root = _parse(prefs) assert _attr(root, "options", "scroll")["mousewheel-zoom"] == "false" def test_merge_creates_parent_dirs_when_missing(tmp_path): prefs = tmp_path / "deep" / "nested" / "dir" / "preferences.xml" merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") assert prefs.exists() # ── preset overrides ────────────────────────────────────────────────────────── def test_preset_cc_sets_keys_file(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "options", "keyboard")["file"] == "illustrator-cc" def test_preset_cs6_sets_keys_file(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cs6") root = _parse(prefs) assert _attr(root, "options", "keyboard")["file"] == "illustrator-cs6" def test_preset_cc_sets_px_units(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") root = _parse(prefs) assert _attr(root, "options", "units")["doc"] == "px" def test_preset_cs6_sets_pt_units(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cs6") root = _parse(prefs) assert _attr(root, "options", "units")["doc"] == "pt" # ── error handling ──────────────────────────────────────────────────────────── def test_merge_raises_on_malformed_prefs(tmp_path): prefs = tmp_path / "preferences.xml" prefs.write_text("this is not xml") with pytest.raises(ET.ParseError): merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "cc") def test_merge_raises_on_missing_patch(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) with pytest.raises(FileNotFoundError): merge_prefs.merge(prefs, tmp_path / "nonexistent-patch.xml", "cc") def test_merge_raises_on_malformed_patch(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) bad_patch = tmp_path / "bad-patch.xml" bad_patch.write_text("not xml at all") with pytest.raises(ET.ParseError): merge_prefs.merge(prefs, bad_patch, "cc") def test_merge_raises_on_invalid_preset(tmp_path): prefs = tmp_path / "preferences.xml" shutil.copy(FIXTURES / "prefs-fresh.xml", prefs) with pytest.raises(ValueError, match="Unknown preset"): merge_prefs.merge(prefs, FIXTURES / "patch-minimal.xml", "bad-preset")