"""Schematic writer regression: synthetic SCH_PAGE → kicad_sch → re-parse.""" from tools.epro2.kicad._sexpr_reader import parse from tools.epro2.kicad.sch_writer import MIL_TO_MM, write_sch_page from tools.epro2.replay import Document def _doc(objs, doc_uuid="d_test"): d = Document(doc_uuid=doc_uuid, doc_type="SCH_PAGE") d.head = {"docType": "SCH_PAGE", "editVersion": "3.2.91"} for k, v in objs: d.objects[k] = v return d def _block(parsed, name): """Return all top-level child blocks of a kicad_sch named ``name``.""" return [c for c in parsed if isinstance(c, list) and c and c[0] == name] def test_writer_emits_paper_title_and_lib_symbols(): d = _doc([("META", {"_type": "META", "title": "Test"})]) text = write_sch_page(d) p = parse(text) assert p[0] == "kicad_sch" assert _block(p, "paper")[0][1] == "A4" title = _block(p, "title_block")[0] assert title[1] == ["title", "Test"] # lib_symbols stub present (Phase 1) assert _block(p, "lib_symbols") == [["lib_symbols"]] def test_wires_emit_one_block_per_line_with_mil_to_mm_conversion(): d = _doc([ ("ln1", {"_type": "LINE", "lineGroup": "w1", "startX": 0, "startY": 0, "endX": 1000, "endY": 0}), ]) text = write_sch_page(d, sheet_origin_mm=(0.0, 0.0)) p = parse(text) wires = _block(p, "wire") assert len(wires) == 1 pts = wires[0][1] # ['pts', ['xy', x1, y1], ['xy', x2, y2]] assert pts[0] == "pts" assert pts[1][1:] == [0.0, 0.0] assert pts[2][1] == 1000 * MIL_TO_MM # 25.4 mm assert pts[2][2] == 0.0 assert getattr(write_sch_page, "last_stats").wires == 1 def test_zero_length_wire_skipped(): d = _doc([ ("ln1", {"_type": "LINE", "startX": 5, "startY": 5, "endX": 5, "endY": 5}), ]) text = write_sch_page(d) p = parse(text) assert _block(p, "wire") == [] stats = getattr(write_sch_page, "last_stats") assert stats.wires == 0 assert stats.skipped == 1 def test_component_emits_symbol_placement_with_designator(): d = _doc([ ("e1", {"_type": "COMPONENT", "partId": "MyPart.1", "x": 100, "y": 200, "rotation": 90}), ("a1", {"_type": "ATTR", "parentId": "e1", "key": "Designator", "value": "R1"}), ("a2", {"_type": "ATTR", "parentId": "e1", "key": "Value", "value": "10kΩ"}), ]) text = write_sch_page(d, sheet_origin_mm=(0.0, 0.0)) p = parse(text) syms = _block(p, "symbol") assert len(syms) == 1 sym = syms[0] # lib_id contains our partId lib_id = next(c for c in sym if isinstance(c, list) and c[0] == "lib_id") assert "MyPart.1" in lib_id[1] # at xy applied at = next(c for c in sym if isinstance(c, list) and c[0] == "at") assert at[1] == 100 * MIL_TO_MM assert at[2] == 200 * MIL_TO_MM assert at[3] == 90.0 # properties carry ATTR values props = [c for c in sym if isinstance(c, list) and c[0] == "property"] by_name = {p[1]: p[2] for p in props} assert by_name["Reference"] == "R1" assert by_name["Value"] == "10kΩ" def test_text_object_emits_text_block_when_non_empty(): d = _doc([ ("t1", {"_type": "TEXT", "value": "Hello", "x": 50, "y": 50}), ("t2", {"_type": "TEXT", "value": "", "x": 60, "y": 60}), # skipped ]) text = write_sch_page(d, sheet_origin_mm=(0.0, 0.0)) p = parse(text) texts = _block(p, "text") assert len(texts) == 1 assert texts[0][1] == "Hello" def test_non_sch_page_doc_rejected(): d = Document(doc_uuid="x", doc_type="PCB") try: write_sch_page(d) except ValueError: return raise AssertionError("expected ValueError for non-SCH_PAGE doc")