- Use CalVer dot notation (2026.2.13) for PEP 440 compliance - Filter pinlabels for top-level components when ground/power hidden - Fix unused variables, long lines, import ordering (ruff clean) - Use StrEnum for PinDirection (Python 3.11+) - Add .gitignore and README.md - All 105 tests pass including WireViz roundtrip validation
174 lines
6.0 KiB
Python
174 lines
6.0 KiB
Python
"""Tests for the filter engine."""
|
|
|
|
from spice2wireviz.filter import (
|
|
FilterConfig,
|
|
apply_filters,
|
|
filter_component,
|
|
filter_instance,
|
|
filter_net,
|
|
)
|
|
from spice2wireviz.parser.models import (
|
|
ParsedNetlist,
|
|
SpiceComponent,
|
|
SpicePin,
|
|
SubcircuitInstance,
|
|
)
|
|
|
|
|
|
def _make_netlist(**kwargs) -> ParsedNetlist:
|
|
defaults = {
|
|
"subcircuit_defs": {},
|
|
"instances": [],
|
|
"top_level_components": [],
|
|
"all_nets": set(),
|
|
"global_nets": set(),
|
|
}
|
|
defaults.update(kwargs)
|
|
return ParsedNetlist(**defaults)
|
|
|
|
|
|
def _make_component(ref: str, prefix: str, nodes: list[str] | None = None) -> SpiceComponent:
|
|
nodes = nodes or []
|
|
return SpiceComponent(
|
|
reference=ref,
|
|
prefix=prefix,
|
|
nodes=nodes,
|
|
pins=[SpicePin(name=n, index=i + 1, net_name=n) for i, n in enumerate(nodes)],
|
|
)
|
|
|
|
|
|
def _make_instance(ref: str, subckt: str, port_to_net: dict[str, str]) -> SubcircuitInstance:
|
|
return SubcircuitInstance(reference=ref, subcircuit_name=subckt, port_to_net=port_to_net)
|
|
|
|
|
|
class TestComponentFilter:
|
|
def test_default_includes_boundary(self):
|
|
config = FilterConfig()
|
|
netlist = _make_netlist()
|
|
assert filter_component(_make_component("J1", "J"), config, netlist)
|
|
assert filter_component(_make_component("TP1", "TP"), config, netlist)
|
|
assert filter_component(_make_component("P1", "P"), config, netlist)
|
|
|
|
def test_exclude_prefix(self):
|
|
config = FilterConfig(exclude_prefixes=["TP"])
|
|
netlist = _make_netlist()
|
|
assert filter_component(_make_component("J1", "J"), config, netlist)
|
|
assert not filter_component(_make_component("TP1", "TP"), config, netlist)
|
|
|
|
def test_include_prefix_restricts(self):
|
|
config = FilterConfig(include_prefixes=["J"])
|
|
netlist = _make_netlist()
|
|
assert filter_component(_make_component("J1", "J"), config, netlist)
|
|
assert not filter_component(_make_component("TP1", "TP"), config, netlist)
|
|
|
|
def test_exclude_ref(self):
|
|
config = FilterConfig(exclude_refs=["J2"])
|
|
netlist = _make_netlist()
|
|
assert filter_component(_make_component("J1", "J"), config, netlist)
|
|
assert not filter_component(_make_component("J2", "J"), config, netlist)
|
|
|
|
def test_include_ref(self):
|
|
config = FilterConfig(include_refs=["J1"])
|
|
netlist = _make_netlist()
|
|
assert filter_component(_make_component("J1", "J"), config, netlist)
|
|
assert not filter_component(_make_component("J2", "J"), config, netlist)
|
|
|
|
|
|
class TestInstanceFilter:
|
|
def test_default_includes_x(self):
|
|
config = FilterConfig()
|
|
netlist = _make_netlist()
|
|
inst = _make_instance("X1", "amp", {"VIN": "NET1"})
|
|
assert filter_instance(inst, config, netlist)
|
|
|
|
def test_exclude_subcircuit(self):
|
|
config = FilterConfig(exclude_subcircuits=["power_supply"])
|
|
netlist = _make_netlist()
|
|
assert not filter_instance(
|
|
_make_instance("X1", "power_supply", {}), config, netlist
|
|
)
|
|
assert filter_instance(
|
|
_make_instance("X2", "amplifier", {}), config, netlist
|
|
)
|
|
|
|
def test_include_subcircuit(self):
|
|
config = FilterConfig(include_subcircuits=["amplifier"])
|
|
netlist = _make_netlist()
|
|
assert filter_instance(
|
|
_make_instance("X2", "amplifier", {}), config, netlist
|
|
)
|
|
assert not filter_instance(
|
|
_make_instance("X1", "power_supply", {}), config, netlist
|
|
)
|
|
|
|
def test_exclude_ref(self):
|
|
config = FilterConfig(exclude_refs=["X3"])
|
|
netlist = _make_netlist()
|
|
assert not filter_instance(
|
|
_make_instance("X3", "io_board", {}), config, netlist
|
|
)
|
|
|
|
|
|
class TestNetFilter:
|
|
def test_default_shows_all(self):
|
|
config = FilterConfig()
|
|
netlist = _make_netlist()
|
|
assert filter_net("VCC", config, netlist)
|
|
assert filter_net("GND", config, netlist)
|
|
assert filter_net("SIGNAL", config, netlist)
|
|
|
|
def test_hide_ground(self):
|
|
config = FilterConfig(show_ground=False)
|
|
netlist = _make_netlist()
|
|
assert not filter_net("GND", config, netlist)
|
|
assert not filter_net("AGND", config, netlist)
|
|
assert filter_net("VCC", config, netlist)
|
|
|
|
def test_hide_power(self):
|
|
config = FilterConfig(show_power=False)
|
|
netlist = _make_netlist()
|
|
assert not filter_net("VCC", config, netlist)
|
|
assert not filter_net("VDD", config, netlist)
|
|
assert filter_net("GND", config, netlist)
|
|
|
|
def test_include_nets_glob(self):
|
|
config = FilterConfig(include_nets=["SIG_*"])
|
|
netlist = _make_netlist()
|
|
assert filter_net("SIG_IN", config, netlist)
|
|
assert filter_net("SIG_OUT", config, netlist)
|
|
assert not filter_net("VCC", config, netlist)
|
|
|
|
def test_exclude_nets_glob(self):
|
|
config = FilterConfig(exclude_nets=["N0*"])
|
|
netlist = _make_netlist()
|
|
assert not filter_net("N001", config, netlist)
|
|
assert filter_net("SIGNAL", config, netlist)
|
|
|
|
|
|
class TestApplyFilters:
|
|
def test_filters_components(self):
|
|
comps = [
|
|
_make_component("J1", "J", ["NET1"]),
|
|
_make_component("TP1", "TP", ["NET2"]),
|
|
_make_component("R1", "R", ["NET3"]),
|
|
]
|
|
netlist = _make_netlist(top_level_components=comps)
|
|
config = FilterConfig() # default: J, TP, P, X
|
|
filtered = apply_filters(netlist, config)
|
|
refs = [c.reference for c in filtered.top_level_components]
|
|
assert "J1" in refs
|
|
assert "TP1" in refs
|
|
assert "R1" not in refs # R not in default include
|
|
|
|
def test_filters_instances(self):
|
|
insts = [
|
|
_make_instance("X1", "power", {"VCC": "NET1"}),
|
|
_make_instance("X2", "amp", {"VIN": "NET2"}),
|
|
]
|
|
netlist = _make_netlist(instances=insts)
|
|
config = FilterConfig(exclude_refs=["X1"])
|
|
filtered = apply_filters(netlist, config)
|
|
refs = [i.reference for i in filtered.instances]
|
|
assert "X1" not in refs
|
|
assert "X2" in refs
|