spice2wireviz/tests/test_single_module.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

105 lines
3.9 KiB
Python

"""Tests for the single-module mapper."""
from pathlib import Path
import pytest
from spice2wireviz.filter import FilterConfig
from spice2wireviz.mapper.single_module import map_single_module
from spice2wireviz.parser.netlist import parse_netlist
FIXTURES = Path(__file__).parent / "fixtures"
class TestSingleModuleMapping:
def test_simple_board_connectors(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
connectors = result["connectors"]
assert "amplifier_board" in connectors # module header
assert "J1" in connectors
assert "J2" in connectors
assert "TP1" in connectors
def test_module_header_pinlabels(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
header = result["connectors"]["amplifier_board"]
assert header["pinlabels"] == ["VIN", "GND", "VOUT", "SIGNAL_IN"]
assert header["type"] == "Module Interface"
def test_connector_pinlabels(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
j1 = result["connectors"]["J1"]
assert j1["pinlabels"] == ["VIN", "GND"]
def test_test_point_style(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
tp1 = result["connectors"]["TP1"]
assert tp1.get("style") == "simple"
def test_cables_created(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
cables = result["cables"]
assert len(cables) >= 2 # At least cables for J1 and J2 connections
def test_connections_created(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
connections = result["connections"]
assert len(connections) >= 2
def test_traceability_notes(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
result = map_single_module(netlist, "amplifier_board")
header = result["connectors"]["amplifier_board"]
assert "SPICE subcircuit" in header["notes"]
def test_nonexistent_subcircuit_raises(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
with pytest.raises(ValueError, match="not found"):
map_single_module(netlist, "nonexistent")
def test_metadata_passthrough(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
meta = {"title": "Test Diagram", "author": "Test"}
result = map_single_module(netlist, "amplifier_board", metadata=meta)
assert result["metadata"]["title"] == "Test Diagram"
class TestSingleModuleFiltering:
def test_no_ground_filter(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
config = FilterConfig(show_ground=False)
result = map_single_module(netlist, "amplifier_board", config)
header = result["connectors"]["amplifier_board"]
assert "GND" not in header["pinlabels"]
def test_no_power_filter(self):
"""When power is hidden, VIN should still appear as it's a port name, not a power net."""
netlist = parse_netlist(FIXTURES / "simple_board.net")
config = FilterConfig(show_power=False)
result = map_single_module(netlist, "amplifier_board", config)
# VIN is a port name — it's not VCC/VDD so it stays
header = result["connectors"]["amplifier_board"]
assert "VIN" in header["pinlabels"]
def test_exclude_component_ref(self):
netlist = parse_netlist(FIXTURES / "simple_board.net")
config = FilterConfig(exclude_refs=["TP1"])
result = map_single_module(netlist, "amplifier_board", config)
assert "TP1" not in result["connectors"]