New modules: - patterns/ library: decoupling bank, pull resistor, crystal oscillator placement with power symbol attachment and grid math helpers - tools/batch.py: atomic file-based batch operations with dry_run - tools/power_symbols.py: add_power_symbol with auto #PWR refs - tools/schematic_patterns.py: MCP wrappers for pattern library - tools/schematic_edit.py: modify/remove components, title blocks, annotations - resources/schematic.py: schematic data resources 43 new tests (99 total), lint clean.
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
"""Tests for schematic editing tools."""
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from tests.conftest import requires_sch_api
|
|
|
|
|
|
@requires_sch_api
|
|
@pytest.mark.unit
|
|
class TestModifyComponent:
|
|
"""Tests for the modify_component tool."""
|
|
|
|
def test_modify_value(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import modify_component
|
|
|
|
result = modify_component(
|
|
schematic_path=populated_schematic,
|
|
reference="R1",
|
|
value="22k",
|
|
)
|
|
assert result["success"] is True
|
|
assert any("value" in m for m in result["modified"])
|
|
|
|
def test_modify_nonexistent_component(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import modify_component
|
|
|
|
result = modify_component(
|
|
schematic_path=populated_schematic,
|
|
reference="Z99",
|
|
value="1k",
|
|
)
|
|
assert result["success"] is False
|
|
assert "error" in result
|
|
|
|
def test_modify_no_changes(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import modify_component
|
|
|
|
result = modify_component(
|
|
schematic_path=populated_schematic,
|
|
reference="R1",
|
|
)
|
|
assert result["success"] is True
|
|
assert result.get("modified") == []
|
|
|
|
|
|
@requires_sch_api
|
|
@pytest.mark.unit
|
|
class TestRemoveComponent:
|
|
"""Tests for the remove_component tool."""
|
|
|
|
def test_remove_existing(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import remove_component
|
|
|
|
result = remove_component(
|
|
schematic_path=populated_schematic,
|
|
reference="R2",
|
|
)
|
|
assert result["success"] is True
|
|
assert result["reference"] == "R2"
|
|
|
|
def test_remove_nonexistent(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import remove_component
|
|
|
|
result = remove_component(
|
|
schematic_path=populated_schematic,
|
|
reference="Z99",
|
|
)
|
|
assert result["success"] is False
|
|
|
|
|
|
@requires_sch_api
|
|
@pytest.mark.unit
|
|
class TestBackupSchematic:
|
|
"""Tests for the backup_schematic tool."""
|
|
|
|
def test_backup_creates_file(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import backup_schematic
|
|
|
|
result = backup_schematic(schematic_path=populated_schematic)
|
|
assert result["success"] is True
|
|
assert "backup_path" in result
|
|
assert os.path.isfile(result["backup_path"])
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestValidation:
|
|
"""Tests for input validation in edit tools."""
|
|
|
|
def test_invalid_path_extension(self):
|
|
from mckicad.tools.schematic_edit import modify_component
|
|
|
|
result = modify_component(
|
|
schematic_path="/tmp/test.txt",
|
|
reference="R1",
|
|
value="1k",
|
|
)
|
|
assert result["success"] is False
|
|
assert ".kicad_sch" in result["error"]
|
|
|
|
def test_empty_path(self):
|
|
from mckicad.tools.schematic_edit import remove_component
|
|
|
|
result = remove_component(schematic_path="", reference="R1")
|
|
assert result["success"] is False
|
|
|
|
def test_nonexistent_file(self):
|
|
from mckicad.tools.schematic_edit import set_title_block
|
|
|
|
result = set_title_block(
|
|
schematic_path="/tmp/nonexistent.kicad_sch",
|
|
title="Test",
|
|
)
|
|
assert result["success"] is False
|
|
|
|
|
|
@requires_sch_api
|
|
@pytest.mark.unit
|
|
class TestSetTitleBlock:
|
|
"""Tests for the set_title_block tool."""
|
|
|
|
def test_set_fields(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import set_title_block
|
|
|
|
result = set_title_block(
|
|
schematic_path=populated_schematic,
|
|
title="Test Circuit",
|
|
revision="1.0",
|
|
)
|
|
assert result["success"] is True
|
|
assert "title" in result.get("fields_set", [])
|
|
|
|
def test_set_author(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import set_title_block
|
|
|
|
result = set_title_block(
|
|
schematic_path=populated_schematic,
|
|
author="Test Author",
|
|
)
|
|
assert result["success"] is True
|
|
assert "author" in result.get("fields_set", [])
|
|
|
|
def test_no_fields_is_noop(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import set_title_block
|
|
|
|
result = set_title_block(schematic_path=populated_schematic)
|
|
assert result["success"] is True
|
|
assert result.get("fields_set") == []
|
|
|
|
|
|
@requires_sch_api
|
|
@pytest.mark.unit
|
|
class TestAnnotationTools:
|
|
"""Tests for add_text_annotation and add_no_connect."""
|
|
|
|
def test_add_text(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import add_text_annotation
|
|
|
|
result = add_text_annotation(
|
|
schematic_path=populated_schematic,
|
|
text="Test note",
|
|
x=50,
|
|
y=50,
|
|
)
|
|
assert result["success"] is True
|
|
|
|
def test_add_no_connect(self, populated_schematic):
|
|
from mckicad.tools.schematic_edit import add_no_connect
|
|
|
|
result = add_no_connect(
|
|
schematic_path=populated_schematic,
|
|
x=300,
|
|
y=300,
|
|
)
|
|
assert result["success"] is True
|