mcltspice/tests/test_drc.py
Ryan Malloy cf8394fa6f Rename mcp-ltspice -> mcltspice, remove stdout banner
Rename package from mcp-ltspice/mcp_ltspice to mcltspice throughout:
source directory, imports, pyproject.toml, tests, and README.

Remove startup banner prints from main() since FastMCP handles
its own banner and stdout is the MCP JSON-RPC transport.

Point repo URL at git.supported.systems/MCP/mcltspice.
2026-02-12 22:53:16 -07:00

128 lines
4.3 KiB
Python

"""Tests for drc module: design rule checks on schematic objects."""
from mcltspice.drc import (
DRCResult,
DRCViolation,
Severity,
_check_duplicate_names,
_check_ground,
_check_simulation_directive,
)
from mcltspice.schematic import Schematic, write_schematic
def _run_single_check(check_fn, schematic: Schematic) -> DRCResult:
"""Run a single DRC check function and return results."""
result = DRCResult()
check_fn(schematic, result)
return result
class TestGroundCheck:
def test_missing_ground_detected(self, schematic_no_ground):
result = _run_single_check(_check_ground, schematic_no_ground)
assert not result.passed
assert any(v.rule == "NO_GROUND" for v in result.violations)
def test_ground_present(self, valid_schematic):
result = _run_single_check(_check_ground, valid_schematic)
assert result.passed
assert len(result.violations) == 0
class TestSimDirectiveCheck:
def test_missing_sim_directive_detected(self, schematic_no_sim):
result = _run_single_check(_check_simulation_directive, schematic_no_sim)
assert not result.passed
assert any(v.rule == "NO_SIM_DIRECTIVE" for v in result.violations)
def test_sim_directive_present(self, valid_schematic):
result = _run_single_check(_check_simulation_directive, valid_schematic)
assert result.passed
class TestDuplicateNameCheck:
def test_duplicate_names_detected(self, schematic_duplicate_names):
result = _run_single_check(_check_duplicate_names, schematic_duplicate_names)
assert not result.passed
assert any(v.rule == "DUPLICATE_NAME" for v in result.violations)
def test_unique_names_pass(self, valid_schematic):
result = _run_single_check(_check_duplicate_names, valid_schematic)
assert result.passed
class TestDRCResult:
def test_passed_when_no_errors(self):
result = DRCResult()
result.violations.append(
DRCViolation(rule="TEST", severity=Severity.WARNING, message="warning only")
)
assert result.passed # Warnings don't cause failure
def test_failed_when_errors(self):
result = DRCResult()
result.violations.append(
DRCViolation(rule="TEST", severity=Severity.ERROR, message="error")
)
assert not result.passed
def test_summary_no_violations(self):
result = DRCResult(checks_run=5)
assert "passed" in result.summary().lower()
def test_summary_with_errors(self):
result = DRCResult(checks_run=5)
result.violations.append(
DRCViolation(rule="TEST", severity=Severity.ERROR, message="error")
)
assert "FAILED" in result.summary()
def test_to_dict(self):
result = DRCResult(checks_run=3)
result.violations.append(
DRCViolation(rule="NO_GROUND", severity=Severity.ERROR, message="No ground")
)
d = result.to_dict()
assert d["passed"] is False
assert d["error_count"] == 1
assert len(d["violations"]) == 1
def test_errors_and_warnings_properties(self):
result = DRCResult()
result.violations.append(
DRCViolation(rule="E1", severity=Severity.ERROR, message="err")
)
result.violations.append(
DRCViolation(rule="W1", severity=Severity.WARNING, message="warn")
)
result.violations.append(
DRCViolation(rule="I1", severity=Severity.INFO, message="info")
)
assert len(result.errors) == 1
assert len(result.warnings) == 1
class TestFullDRC:
"""Integration test: write a schematic to disk and run the full DRC pipeline."""
def test_valid_schematic_passes(self, valid_schematic, tmp_path):
"""A valid schematic should pass DRC with no errors."""
from mcltspice.drc import run_drc
path = tmp_path / "valid.asc"
write_schematic(valid_schematic, path)
result = run_drc(path)
# May have warnings (floating nodes etc) but no errors
assert len(result.errors) == 0
def test_no_ground_fails(self, schematic_no_ground, tmp_path):
from mcltspice.drc import run_drc
path = tmp_path / "no_ground.asc"
write_schematic(schematic_no_ground, path)
result = run_drc(path)
assert any(v.rule == "NO_GROUND" for v in result.errors)