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
146 lines
5.1 KiB
Python
146 lines
5.1 KiB
Python
"""Tests for the inter-module mapper."""
|
|
|
|
from pathlib import Path
|
|
|
|
from spice2wireviz.filter import FilterConfig
|
|
from spice2wireviz.mapper.inter_module import map_inter_module
|
|
from spice2wireviz.parser.netlist import parse_netlist
|
|
|
|
FIXTURES = Path(__file__).parent / "fixtures"
|
|
|
|
|
|
class TestInterModuleMapping:
|
|
def test_multi_board_connectors(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
connectors = result["connectors"]
|
|
# Should have connectors for each X instance and top-level components
|
|
assert "X1" in connectors
|
|
assert "X2" in connectors
|
|
assert "X3" in connectors
|
|
assert "J_CHASSIS" in connectors
|
|
assert "TP_VCC" in connectors
|
|
|
|
def test_instance_connector_type(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
x1 = result["connectors"]["X1"]
|
|
assert x1["type"] == "power_supply"
|
|
|
|
def test_instance_pinlabels(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
x2 = result["connectors"]["X2"]
|
|
assert "VIN" in x2["pinlabels"]
|
|
assert "GND" in x2["pinlabels"]
|
|
assert "VOUT" in x2["pinlabels"]
|
|
|
|
def test_cables_for_shared_nets(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
# Should have cables connecting modules via shared nets
|
|
cables = result["cables"]
|
|
assert len(cables) >= 1
|
|
|
|
def test_connections_exist(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
connections = result["connections"]
|
|
assert len(connections) >= 1
|
|
|
|
def test_traceability_notes(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
x1 = result["connectors"]["X1"]
|
|
assert "SPICE instance" in x1["notes"]
|
|
|
|
def test_top_level_test_point_style(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
tp = result["connectors"]["TP_VCC"]
|
|
assert tp.get("style") == "simple"
|
|
|
|
|
|
class TestInterModuleFiltering:
|
|
def test_exclude_instance(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
config = FilterConfig(exclude_refs=["X1"])
|
|
result = map_inter_module(netlist, config)
|
|
|
|
assert "X1" not in result["connectors"]
|
|
assert "X2" in result["connectors"]
|
|
|
|
def test_exclude_subcircuit(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
config = FilterConfig(exclude_subcircuits=["power_supply"])
|
|
result = map_inter_module(netlist, config)
|
|
|
|
assert "X1" not in result["connectors"]
|
|
|
|
def test_no_ground_filter(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
config = FilterConfig(show_ground=False)
|
|
result = map_inter_module(netlist, config)
|
|
|
|
# GND should not appear in any connector's pinlabels
|
|
for name, conn in result["connectors"].items():
|
|
if "pinlabels" in conn:
|
|
assert "GND" not in conn["pinlabels"], f"GND found in {name}"
|
|
|
|
def test_no_power_filter(self):
|
|
netlist = parse_netlist(FIXTURES / "multi_board.net")
|
|
config = FilterConfig(show_power=False)
|
|
result = map_inter_module(netlist, config)
|
|
|
|
for name, conn in result["connectors"].items():
|
|
if "pinlabels" in conn:
|
|
assert "VCC" not in conn["pinlabels"], f"VCC found in {name}"
|
|
|
|
|
|
class TestHierarchicalInterModule:
|
|
def test_hierarchical_instances(self):
|
|
netlist = parse_netlist(FIXTURES / "hierarchical.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
connectors = result["connectors"]
|
|
assert "X_REG" in connectors
|
|
assert "X_SENSOR" in connectors
|
|
assert "X_MAIN" in connectors
|
|
|
|
def test_external_connectors(self):
|
|
netlist = parse_netlist(FIXTURES / "hierarchical.net")
|
|
result = map_inter_module(netlist)
|
|
|
|
connectors = result["connectors"]
|
|
assert "J_PWR" in connectors
|
|
assert "J_USB" in connectors
|
|
assert "TP_3V3" in connectors
|
|
|
|
def test_grouped_parallel_wires(self):
|
|
"""Parallel wires between same modules should be grouped."""
|
|
netlist = parse_netlist(FIXTURES / "hierarchical.net")
|
|
config = FilterConfig(group_parallel_wires=True)
|
|
result = map_inter_module(netlist, config)
|
|
|
|
# With grouping, fewer cables than total net connections
|
|
cables = result["cables"]
|
|
connections = result["connections"]
|
|
assert len(cables) == len(connections)
|
|
|
|
def test_ungrouped_wires(self):
|
|
"""Without grouping, each wire gets its own cable."""
|
|
netlist = parse_netlist(FIXTURES / "hierarchical.net")
|
|
config = FilterConfig(group_parallel_wires=False)
|
|
result = map_inter_module(netlist, config)
|
|
|
|
# Each cable should have wirecount=1
|
|
for cable in result["cables"].values():
|
|
assert cable.get("wirecount", 1) == 1 or len(cable.get("colors", [])) == 1
|