"""Roundtrip tests: parse SPICE -> emit YAML -> validate with WireViz. These tests verify the generated YAML is structurally valid by feeding it to wireviz.wireviz.parse() and checking it doesn't raise exceptions. """ from pathlib import Path import pytest import yaml from spice2wireviz.emitter.yaml_emitter import emit_yaml from spice2wireviz.filter import FilterConfig from spice2wireviz.mapper.inter_module import map_inter_module from spice2wireviz.mapper.single_module import map_single_module from spice2wireviz.parser.netlist import parse_netlist FIXTURES = Path(__file__).parent / "fixtures" def _has_wireviz() -> bool: try: from wireviz.wireviz import parse # noqa: F401 return True except ImportError: return False class TestYamlValidity: """Verify generated YAML is valid YAML and has required WireViz keys.""" def test_single_module_yaml_structure(self): netlist = parse_netlist(FIXTURES / "simple_board.net") result = map_single_module(netlist, "amplifier_board") yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) assert "connectors" in parsed assert "cables" in parsed assert "connections" in parsed assert isinstance(parsed["connectors"], dict) assert isinstance(parsed["cables"], dict) assert isinstance(parsed["connections"], list) def test_inter_module_yaml_structure(self): netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) assert "connectors" in parsed assert "cables" in parsed assert "connections" in parsed def test_hierarchical_yaml_structure(self): netlist = parse_netlist(FIXTURES / "hierarchical.net") result = map_inter_module(netlist) yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) assert "connectors" in parsed assert len(parsed["connectors"]) >= 6 # 3 instances + 3 top-level def test_connector_has_pin_spec(self): """Every connector must have at least one of pincount/pins/pinlabels.""" netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) for name, conn in parsed["connectors"].items(): has_pins = any(k in conn for k in ("pincount", "pins", "pinlabels")) assert has_pins, f"Connector '{name}' missing pin specification" def test_cable_has_wire_spec(self): """Every cable must have wirecount or colors.""" netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) for name, cable in parsed["cables"].items(): has_wires = "wirecount" in cable or "colors" in cable assert has_wires, f"Cable '{name}' missing wire specification" def test_connection_set_structure(self): """Each connection set should be a list of dicts.""" netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) yaml_str = emit_yaml(result) parsed = yaml.safe_load(yaml_str) for i, conn_set in enumerate(parsed["connections"]): assert isinstance(conn_set, list), f"Connection set {i} is not a list" assert len(conn_set) == 3, f"Connection set {i} doesn't have 3 elements" for entry in conn_set: assert isinstance(entry, dict), f"Connection set {i} entry is not a dict" @pytest.mark.skipif(not _has_wireviz(), reason="WireViz not installed") class TestWireVizRoundtrip: """Feed generated YAML to WireViz's parse() to verify full compatibility.""" def test_single_module_roundtrip(self): from wireviz.wireviz import parse as wv_parse netlist = parse_netlist(FIXTURES / "simple_board.net") result = map_single_module(netlist, "amplifier_board") # WireViz parse() accepts dicts directly harness = wv_parse(result, return_types="harness") assert harness is not None def test_inter_module_roundtrip(self): from wireviz.wireviz import parse as wv_parse netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) harness = wv_parse(result, return_types="harness") assert harness is not None def test_hierarchical_roundtrip(self): from wireviz.wireviz import parse as wv_parse netlist = parse_netlist(FIXTURES / "hierarchical.net") result = map_inter_module(netlist) harness = wv_parse(result, return_types="harness") assert harness is not None def test_filtered_roundtrip(self): from wireviz.wireviz import parse as wv_parse netlist = parse_netlist(FIXTURES / "hierarchical.net") config = FilterConfig(show_ground=False, show_power=False) result = map_inter_module(netlist, config) harness = wv_parse(result, return_types="harness") assert harness is not None def test_render_svg(self, tmp_path): from wireviz.wireviz import parse as wv_parse netlist = parse_netlist(FIXTURES / "multi_board.net") result = map_inter_module(netlist) svg = wv_parse(result, return_types="svg") assert svg is not None assert len(svg) > 100 # Should be a real SVG