"""Tests for shove-and-retry on the exact-geometry track. Shove moves an existing trace aside to make room for a net that would otherwise be dropped, without ever producing a clearance violation. The dense fixture ``shove_needed.dsn`` has one signal layer (no vias): with greedy net ordering NET_A routes as a straight wall that boxes NET_C out, so without shove NET_C is dropped; with shove NET_A is moved aside and both connect, DRC-clean. (Rip-up would also reroute NET_A here; the shove test uses greedy ordering to isolate the shove mechanism.) """ from __future__ import annotations from pathlib import Path import sys import pytest from freeroute.dsn import parse_dsn from freeroute.dsn.sexp import parse from freeroute.route import route, route_dsn_board_exact from freeroute.route.pipeline import _rule_clearance_dsn sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) FIXTURES = Path(__file__).resolve().parent.parent / "dsn" / "fixtures" SHOVE = FIXTURES / "shove_needed.dsn" REGRESSION = ["simple_2net", "crossing_2net", "ripup_needed", "kicad_routable"] def _exact(fixture, shove): return route_dsn_board_exact(parse_dsn(fixture.read_text()), shove=shove) # --- the headline shove contrast (greedy ordering isolates the mechanism) ---- def test_without_shove_a_net_is_dropped(): # greedy exact (no shove) cannot fit NET_C past NET_A's wall off, _, _ = route_dsn_board_exact(parse_dsn(SHOVE.read_text()), rip_up=False, shove=False) assert len(off.routed_net_numbers) < 2 assert off.drc_clean # still clean, just incomplete def test_shove_connects_all_and_stays_drc_clean(): on, scale, _ = route_dsn_board_exact(parse_dsn(SHOVE.read_text()), rip_up=False, shove=True) assert on.routed_net_numbers == {1, 2} # both nets connected assert on.shoves >= 1 # at least one trace was moved aside assert on.drc_clean clearance = round(_rule_clearance_dsn(parse_dsn(SHOVE.read_text())) * scale) assert on.tree.has_violation(clearance) is None # exact DRC check def test_shove_result_is_deterministic(): a, _, _ = route_dsn_board_exact(parse_dsn(SHOVE.read_text()), rip_up=False, shove=True) b, _, _ = route_dsn_board_exact(parse_dsn(SHOVE.read_text()), rip_up=False, shove=True) def key(r): return { n: [(layer, [(p.x, p.y) for p in pts]) for layer, pts in segs] for n, segs in r.result.wires.items() } assert key(a) == key(b) def test_shoved_trace_endpoints_still_on_pads(): from freeroute.board import build_board on, _, _ = route_dsn_board_exact(parse_dsn(SHOVE.read_text()), rip_up=False, shove=True) board = build_board(parse_dsn(SHOVE.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, segments in on.result.wires.items(): pts = [(p.x, p.y) for _, seg in segments for p in seg] assert {pts[0], pts[-1]} == pins_by_net[net_no] def test_shove_emits_valid_ses(): ses = route(SHOVE.read_text(), engine="exact", shove=True) top = parse(ses) assert top.head == "session" nets = top.child("routes").child("network_out").children("net") assert {n.values()[0].text for n in nets} == {"NET_A", "NET_C"} # --- regression: shove is a no-op where it isn't needed ---------------------- @pytest.mark.parametrize("name", REGRESSION) def test_shove_does_not_change_or_break_normal_boards(name): fixture = FIXTURES / f"{name}.dsn" off, _, _ = _exact(fixture, shove=False) on, _, _ = _exact(fixture, shove=True) # same connectivity, both DRC-clean, and no shove was needed assert on.routed_net_numbers == off.routed_net_numbers assert off.drc_clean and on.drc_clean assert on.shoves == 0 # --- oracle parity ---------------------------------------------------------- @pytest.mark.oracle def test_shove_connectivity_parity_with_oracle(): from oracle import HAS_ORACLE, route_dsn, routed_net_set if not HAS_ORACLE: pytest.skip("FreeRouting oracle unavailable") ours = routed_net_set(route(SHOVE.read_text(), engine="exact", shove=True)) theirs = routed_net_set(route_dsn(SHOVE, max_passes=3, timeout=300)) if not theirs: pytest.skip("reference JAR routed nothing on the synthetic single-layer board") assert theirs <= ours