131 lines
4.4 KiB
Python
131 lines
4.4 KiB
Python
"""Tests for drc module: design rule checks on schematic objects."""
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from mcp_ltspice.drc import (
|
|
DRCResult,
|
|
DRCViolation,
|
|
Severity,
|
|
_check_duplicate_names,
|
|
_check_ground,
|
|
_check_simulation_directive,
|
|
)
|
|
from mcp_ltspice.schematic import Component, Flag, Schematic, Text, Wire, 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 mcp_ltspice.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 mcp_ltspice.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)
|