spice2wireviz/tests/test_filter.py
Ryan Malloy e20a956f51 Initial project structure for spice2wireviz
SPICE netlist to WireViz YAML converter with:
- Custom lightweight netlist parser (.net/.cir/.sp)
- Single-module mapper (subcircuit external interface)
- Inter-module mapper (multi-board wiring)
- Filter engine with glob patterns
- Click CLI with auto-detection, inspection commands
- Optional .asc parser via spicelib
- Comprehensive test suite with fixtures
2026-02-13 01:24:41 -07:00

168 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