"""Tests for netlist import tools.""" import json import os import textwrap import pytest @pytest.mark.unit class TestParseKicadXml: """Tests for KiCad XML netlist parsing.""" def test_parse_basic_netlist(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist xml_content = textwrap.dedent("""\ 10k Resistor_SMD:R_0402 4.7k Resistor_SMD:R_0402 100nF Capacitor_SMD:C_0402 """) path = os.path.join(tmp_output_dir, "test.net") with open(path, "w") as f: f.write(xml_content) result = import_netlist(source_path=path) assert result["success"] is True assert result["format_detected"] == "kicad_xml" assert result["statistics"]["net_count"] == 3 assert result["statistics"]["component_count"] == 3 assert result["statistics"]["connection_count"] == 6 # Check nets structure nets = result["nets"] assert "GND" in nets assert ["R1", "2"] in nets["GND"] assert ["C1", "2"] in nets["GND"] assert "VCC" in nets assert len(nets["VCC"]) == 2 # Check components assert "R1" in result["components"] assert result["components"]["R1"]["value"] == "10k" assert result["components"]["R1"]["lib"] == "Device" def test_auto_detects_xml_format(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist xml = '' path = os.path.join(tmp_output_dir, "test.xml") with open(path, "w") as f: f.write(xml) result = import_netlist(source_path=path, format="auto") assert result["success"] is True assert result["format_detected"] == "kicad_xml" def test_malformed_xml_returns_error(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist path = os.path.join(tmp_output_dir, "bad.net") with open(path, "w") as f: f.write("") result = import_netlist(source_path=path) assert result["success"] is False assert "XML parse error" in result["error"] def test_verify_connectivity_compatible(self, tmp_output_dir): """The nets dict is directly usable as verify_connectivity expected param.""" from mckicad.tools.netlist import import_netlist xml = textwrap.dedent("""\ IC """) path = os.path.join(tmp_output_dir, "compat.net") with open(path, "w") as f: f.write(xml) result = import_netlist(source_path=path) assert result["success"] is True # verify_connectivity expects: {"NET1": [["U1", "1"], ["R1", "2"]]} nets = result["nets"] assert "NET1" in nets assert isinstance(nets["NET1"], list) for pin_pair in nets["NET1"]: assert isinstance(pin_pair, list) assert len(pin_pair) == 2 @pytest.mark.unit class TestParseCsv: """Tests for CSV/TSV netlist parsing.""" def test_parse_csv_netlist(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist csv_content = textwrap.dedent("""\ Reference,Pin,Net R1,1,VCC R1,2,GND C1,1,VCC C1,2,GND """) path = os.path.join(tmp_output_dir, "test.csv") with open(path, "w") as f: f.write(csv_content) result = import_netlist(source_path=path) assert result["success"] is True assert result["format_detected"] == "csv" assert result["statistics"]["net_count"] == 2 assert result["statistics"]["component_count"] == 2 assert result["statistics"]["connection_count"] == 4 assert ["R1", "1"] in result["nets"]["VCC"] assert ["C1", "2"] in result["nets"]["GND"] def test_parse_tsv_netlist(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist tsv_content = "Reference\tPin\tNet\nR1\t1\tVCC\nR1\t2\tGND\n" path = os.path.join(tmp_output_dir, "test.tsv") with open(path, "w") as f: f.write(tsv_content) result = import_netlist(source_path=path, format="csv") assert result["success"] is True assert result["statistics"]["connection_count"] == 2 def test_csv_missing_columns(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist csv_content = "Name,Value\nR1,10k\n" path = os.path.join(tmp_output_dir, "bad.csv") with open(path, "w") as f: f.write(csv_content) result = import_netlist(source_path=path, format="csv") assert result["success"] is False assert "Reference" in result["error"] def test_csv_alternative_column_names(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist csv_content = "Designator,Pin Number,Net Name\nU1,3,SDA\nU1,4,SCL\n" path = os.path.join(tmp_output_dir, "alt.csv") with open(path, "w") as f: f.write(csv_content) result = import_netlist(source_path=path, format="csv") assert result["success"] is True assert "SDA" in result["nets"] assert "SCL" in result["nets"] @pytest.mark.unit class TestImportNetlistErrors: """Tests for error handling in import_netlist.""" def test_nonexistent_file(self): from mckicad.tools.netlist import import_netlist result = import_netlist(source_path="/tmp/nonexistent_12345.net") assert result["success"] is False assert "not found" in result["error"] def test_empty_file(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist path = os.path.join(tmp_output_dir, "empty.net") with open(path, "w") as f: f.write("") result = import_netlist(source_path=path) assert result["success"] is False assert "empty" in result["error"] def test_unsupported_format(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist path = os.path.join(tmp_output_dir, "test.net") with open(path, "w") as f: f.write("some content") result = import_netlist(source_path=path, format="spice") assert result["success"] is False assert "Unsupported" in result["error"] def test_unknown_format_auto(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist path = os.path.join(tmp_output_dir, "mystery.dat") with open(path, "w") as f: f.write("random binary garbage 12345") result = import_netlist(source_path=path) assert result["success"] is False assert "auto-detect" in result["error"] @pytest.mark.unit class TestOutputFile: """Tests for output file writing.""" def test_writes_output_json(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist xml = textwrap.dedent("""\ 1k """) src = os.path.join(tmp_output_dir, "test.net") with open(src, "w") as f: f.write(xml) out = os.path.join(tmp_output_dir, "output.json") result = import_netlist(source_path=src, output_path=out) assert result["success"] is True assert result["output_path"] == out assert os.path.isfile(out) with open(out) as f: data = json.load(f) assert "nets" in data assert "GND" in data["nets"] def test_default_output_path(self, tmp_output_dir): from mckicad.tools.netlist import import_netlist xml = '' src = os.path.join(tmp_output_dir, "test.net") with open(src, "w") as f: f.write(xml) result = import_netlist(source_path=src) assert result["success"] is True assert "output_path" in result assert os.path.isfile(result["output_path"])