"""Tests for the MVP grid maze router and the DSN -> SES pipeline. Fast tests assert the routing invariants (traces connect their pads, stay on a valid layer, stay in bounds). An oracle-gated test checks connectivity parity against the reference FreeRouting JAR on the simple board. Fixtures: ``tests/dsn/fixtures/simple_2net.dsn`` (guaranteed routable). """ from __future__ import annotations from pathlib import Path import sys import pytest from freeroute.board import build_board from freeroute.dsn import parse_dsn from freeroute.dsn.sexp import parse from freeroute.route import route, route_dsn_board sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) FIXTURES = Path(__file__).resolve().parent.parent / "dsn" / "fixtures" SIMPLE = FIXTURES / "simple_2net.dsn" def _net_out(ses_text: str): root = parse(ses_text) routes = root.child("routes") network = routes.child("network_out") return {n.values()[0].text: n.children("wire") for n in network.children("net")} def _wire_paths(wire): path = wire.child("path") vals = [v.text for v in path.values()] layer = vals[0] nums = [float(v) for v in vals[2:]] coords = list(zip(nums[0::2], nums[1::2], strict=False)) return layer, coords # --- connectivity ----------------------------------------------------------- def test_simple_board_routes_both_nets(): result, scale, layers = route_dsn_board(parse_dsn(SIMPLE.read_text())) assert result.routed_net_numbers == {1, 2} # NET_A, NET_B def test_pipeline_emits_valid_ses_with_both_nets(): ses = route(SIMPLE.read_text()) top = parse(ses) # must be a single balanced S-expression assert top.head == "session" nets = _net_out(ses) assert set(nets) == {"NET_A", "NET_B"} assert all(wires for wires in nets.values()) # each net has >= 1 wire # --- invariants ------------------------------------------------------------- def test_traces_stay_on_a_valid_layer(): ses = route(SIMPLE.read_text()) layer_names = {"Top", "Bottom"} for wires in _net_out(ses).values(): for wire in wires: layer, _ = _wire_paths(wire) assert layer in layer_names def test_trace_endpoints_sit_on_the_net_pads(): # DSN pad locations per net (component place coords; pin offset is 0,0) pads = { "NET_A": {(20000.0, -20000.0), (180000.0, -20000.0)}, "NET_B": {(20000.0, -60000.0), (180000.0, -60000.0)}, } ses = route(SIMPLE.read_text()) for net_name, wires in _net_out(ses).items(): endpoints = set() for wire in wires: _, coords = _wire_paths(wire) assert len(coords) >= 2, "a trace needs at least two points" endpoints.add(coords[0]) endpoints.add(coords[-1]) # every trace endpoint is one of the net's pads assert endpoints <= pads[net_name] # both pads of the net are touched assert endpoints == pads[net_name] def test_traces_stay_within_the_board_outline(): ses = route(SIMPLE.read_text()) # simple_2net boundary: x in [0, 200000], y in [-80000, 0] for wires in _net_out(ses).values(): for wire in wires: _, coords = _wire_paths(wire) for x, y in coords: assert 0 <= x <= 200000 assert -80000 <= y <= 0 def test_every_routed_net_joins_its_pin_pair(): # a routed net's trace path must actually connect its two pins in board units result, scale, _ = route_dsn_board(parse_dsn(SIMPLE.read_text())) board = build_board(parse_dsn(SIMPLE.read_text())) pins_by_net: dict[int, set[tuple[int, int]]] = {} for pin in board.get_pins(): for net_no in pin.net_nos: pins_by_net.setdefault(net_no, set()).add((pin.location.x, pin.location.y)) for net_no, paths in result.wires.items(): touched = set() for path in paths: touched.add((path[0].x, path[0].y)) touched.add((path[-1].x, path[-1].y)) assert touched == pins_by_net[net_no] # --- robustness ------------------------------------------------------------- def test_router_does_not_crash_on_larger_board(): # the real KiCad board is dense; the router must complete without error and # route at least some nets (full coverage is a later, multi-layer concern). result, _, _ = route_dsn_board(parse_dsn((FIXTURES / "kicad_routable.dsn").read_text())) assert isinstance(result.routed_net_numbers, set) # --- oracle parity ---------------------------------------------------------- @pytest.mark.oracle def test_connectivity_parity_with_oracle_on_simple_board(): from oracle import HAS_ORACLE, route_dsn, routed_net_set if not HAS_ORACLE: pytest.skip("FreeRouting oracle unavailable") our_ses = route(SIMPLE.read_text()) our_nets = routed_net_set(our_ses) oracle_ses = route_dsn(SIMPLE, max_passes=3, timeout=300) oracle_nets = routed_net_set(oracle_ses) assert oracle_nets, "oracle routed nothing on a routable board" # connectivity parity: freeroute connects every net the reference connects assert oracle_nets <= our_nets