Files
mxpic_router/tests/test_eda_router_contract.py
T

746 lines
28 KiB
Python

import os
import sys
import unittest
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
class EdaRouterPinsContractTest(unittest.TestCase):
def test_loader_uses_top_level_pins_and_ignores_legacy_ports(self):
from mxpic_router.eda_loader import parse_cell_dict
spec = parse_cell_dict({
"name": "cell",
"pins": [
{"name": "input_io1", "x": 1, "y": 2, "angle": 90, "width": 0.6},
],
"ports": [
{"name": "legacy_port", "x": 9, "y": 9, "angle": 0},
],
})
self.assertIn("input_io1", spec.pins)
self.assertNotIn("legacy_port", spec.pins)
self.assertEqual(spec.pins["input_io1"].angle, 90.0)
def test_loader_accepts_pin_links_with_route_metadata(self):
from mxpic_router.eda_loader import parse_cell_dict
spec = parse_cell_dict({
"name": "cell_a",
"instances": {
"inst_a": {"component": "mmi", "x": 0, "y": 0},
"inst_b": {"component": "mmi", "x": 100, "y": 0},
},
"bundles": {
"output_bus": {
"links": [{
"from": "inst_a:out",
"to": "inst_b:in",
"xsection": "metal_1",
"family": "electrical",
"width": 5,
"radius": 20,
"routing_type": "standard_bend",
"points": [{"x": 0, "y": 0}, {"x": 50, "y": 0}],
}]
}
}
})
link = spec.bundles["output_bus"].links[0]
self.assertEqual(link.src_inst, "inst_a")
self.assertEqual(link.src_pin, "out")
self.assertEqual(link.dst_inst, "inst_b")
self.assertEqual(link.dst_pin, "in")
self.assertEqual(link.xsection, "metal_1")
self.assertEqual(link.width, 5)
self.assertEqual(link.bundle, "output_bus")
self.assertEqual(link.points[1], {"x": 50.0, "y": 0.0})
def test_loader_accepts_explicit_link_route_group_metadata(self):
from mxpic_router.eda_loader import parse_cell_dict
spec = parse_cell_dict({
"name": "cell_a",
"bundles": {
"output_bus": {
"links": [{
"from": "inst_a:out",
"to": "inst_b:in",
"route_group": "stage_1",
}]
}
}
})
link = spec.bundles["output_bus"].links[0]
self.assertEqual(link.bundle, "output_bus")
self.assertEqual(link.route_group, "stage_1")
def test_builder_uses_frontend_bundle_key_as_explicit_route_group(self):
from mxpic_router.builder import _link_explicit_route_group
from mxpic_router.eda_loader import LinkSpec
self.assertEqual(
_link_explicit_route_group(LinkSpec(bundle="optical_bus")),
"optical_bus",
)
self.assertEqual(
_link_explicit_route_group(LinkSpec(bundle="free_wires")),
"",
)
self.assertEqual(
_link_explicit_route_group(LinkSpec(bundle="free_wires_metal_1")),
"",
)
self.assertEqual(
_link_explicit_route_group(LinkSpec(bundle="output_bus")),
"",
)
self.assertEqual(
_link_explicit_route_group(LinkSpec(bundle="output_bus", route_group="stage_1")),
"stage_1",
)
def test_port_element_creates_named_io_pins_and_inside_route_pins(self):
from mxpic_router.builder import _register_element_pins
from mxpic_router.eda_loader import parse_cell_dict
class FakePlacedPin:
def __init__(self, name, x, y, a):
self.name = name
self.x = x
self.y = y
self.a = a
def move(self, dx, dy, da):
return FakePlacedPin(f"{self.name}_in", self.x + dx, self.y + dy, self.a + da)
class FakePin:
created_names = []
current_cell = None
def __init__(self, name, width=None, xs=None):
self.name = name
self.width = width
self.xs = xs
self.created_names.append(name)
def put(self, x, y, a):
placed = FakePlacedPin(self.name, x, y, a)
if FakePin.current_cell is not None:
FakePin.current_cell.pin[self.name] = placed
return placed
class FakeCell:
put_calls = []
def __init__(self, name=None, instantiate=True):
self.name = name
self.pin = {}
def __enter__(self):
FakePin.current_cell = self
return self
def __exit__(self, exc_type, exc, tb):
FakePin.current_cell = None
return False
def put(self, x, y, a):
self.put_calls.append((self.name, x, y, a))
placed = FakeCell(name=f"{self.name}_placed")
placed.pin = {
name: FakePlacedPin(name, pin.x + x, pin.y + y, pin.a + a)
for name, pin in self.pin.items()
}
return placed
class FakeNazca:
Pin = FakePin
Cell = FakeCell
spec = parse_cell_dict({
"name": "cell",
"elements": {
"input": {
"type": "port",
"x": 5,
"y": 10,
"angle": 90,
"width": 0.5,
"port_number": 2,
"pitch": 12,
"pins": [
{"name": "input_io1", "role": "io1"},
{"name": "input_io2", "role": "io2"},
],
},
},
})
pin_map = {}
_register_element_pins(pin_map, "input", spec.elements["input"], FakeNazca)
self.assertEqual(FakePin.created_names, ["input_io1", "input_io1_in", "input_io2", "input_io2_in"])
self.assertEqual(FakeCell.put_calls, [("element_input", 5.0, 10.0, 90.0)])
self.assertEqual(pin_map[("input", "input_io1")].a, 450.0)
self.assertEqual(pin_map[("input", "input_io2")].a, 450.0)
self.assertEqual((pin_map[("input", "input_io1")].x, pin_map[("input", "input_io1")].y), (5.0, 16.0))
self.assertEqual((pin_map[("input", "input_io2")].x, pin_map[("input", "input_io2")].y), (5.0, 4.0))
def test_anchor_element_defaults_to_named_a_b_pins(self):
from mxpic_router.builder import _register_element_pins
from mxpic_router.eda_loader import parse_cell_dict
class FakePlacedPin:
def __init__(self, name, x, y, a):
self.name = name
self.x = x
self.y = y
self.a = a
class FakePin:
current_cell = None
def __init__(self, name, width=None, xs=None):
self.name = name
def put(self, x, y, a):
placed = FakePlacedPin(self.name, x, y, a)
if FakePin.current_cell is not None:
FakePin.current_cell.pin[self.name] = placed
return placed
class FakeCell:
def __init__(self, name=None, instantiate=True):
self.name = name
self.pin = {}
def __enter__(self):
FakePin.current_cell = self
return self
def __exit__(self, exc_type, exc, tb):
FakePin.current_cell = None
return False
def put(self, x, y, a):
placed = FakeCell(name=f"{self.name}_placed")
placed.pin = {
name: FakePlacedPin(name, pin.x + x, pin.y + y, pin.a + a)
for name, pin in self.pin.items()
}
return placed
class FakeNazca:
Pin = FakePin
Cell = FakeCell
spec = parse_cell_dict({
"name": "cell",
"elements": {
"anchor_1": {"type": "anchor", "x": 10, "y": 20, "angle": 0, "port_number": 2, "pitch": 12},
},
})
pin_map = {}
_register_element_pins(pin_map, "anchor_1", spec.elements["anchor_1"], FakeNazca)
self.assertIn(("anchor_1", "anchor_1_a1"), pin_map)
self.assertIn(("anchor_1", "anchor_1_b1"), pin_map)
self.assertIn(("anchor_1", "anchor_1_a2"), pin_map)
self.assertIn(("anchor_1", "anchor_1_b2"), pin_map)
self.assertEqual(pin_map[("anchor_1", "anchor_1_a1")].a, 180.0)
self.assertEqual(pin_map[("anchor_1", "anchor_1_b1")].a, 0.0)
def test_pdk_metadata_ports_are_registered_as_pins_for_allowed_pdk_roots(self):
from mxpic_router.builder import _metadata_pins
metadata = {"ports": {"a1": {"x": 0, "y": 0, "a": 180}}}
self.assertEqual(
_metadata_pins(metadata, r"D:\mxpic\opt_pdk_public\foundries"),
metadata["ports"],
)
self.assertEqual(_metadata_pins(metadata, r"D:\mxpic\some_project_layout"), {})
def test_eda_rotation_is_converted_to_layout_rotation_for_metadata_pins(self):
from mxpic_router.builder import _layout_rotation, _transform_port
self.assertEqual(_layout_rotation(90), -90.0)
x, y, angle = _transform_port(
39.25,
0.0,
360.0,
1719.3,
-2080.5,
_layout_rotation(90),
)
self.assertAlmostEqual(x, 1719.3)
self.assertAlmostEqual(y, -2119.75)
self.assertEqual(angle, 270.0)
def test_perpendicular_pin_rotations_use_straight_bend_straight_route(self):
from mxpic_router.builder import _route_method_name_for_pins
class FakePin:
def __init__(self, angle):
self.a = angle
self.assertEqual(
_route_method_name_for_pins(FakePin(0), FakePin(90)),
"strt_bend_strt_p2p",
)
self.assertEqual(
_route_method_name_for_pins(FakePin(0), FakePin(270)),
"strt_bend_strt_p2p",
)
def test_route_spacing_detection_handles_crossing_and_endpoint_exceptions(self):
from mxpic_router.builder import (
ROUTE_ENDPOINT_SPACING_IGNORE,
ROUTE_MIN_SPACING,
_first_spacing_adjusted_route,
_orthogonal_elbow,
_polyline_spacing,
_route_spacing_reference_points,
_route_spacing_check_points,
_spacing_offsets,
_spacing_adjusted_route_points,
)
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
self.assertGreaterEqual(
_polyline_spacing(
[{"x": 0, "y": 0}, {"x": 100, "y": 0}],
[{"x": 0, "y": 12}, {"x": 100, "y": 12}],
),
ROUTE_MIN_SPACING,
)
self.assertLess(
_polyline_spacing(
[{"x": 0, "y": 0}, {"x": 100, "y": 0}],
[{"x": 50, "y": -20}, {"x": 50, "y": 20}],
),
ROUTE_MIN_SPACING,
)
device_plan = {
"link": LinkSpec(src_inst="mmi1", src_pin="o1", dst_inst="mmi2", dst_pin="i1"),
"points": [{"x": 0, "y": 0}, {"x": 100, "y": 0}],
}
nearby_port_plan = {
"link": LinkSpec(src_inst="port_1", src_pin="io1", dst_inst="mmi2", dst_pin="i1"),
"points": [{"x": 0, "y": 5}, {"x": 100, "y": 5}],
}
device_check_points = _route_spacing_check_points(device_plan)
nearby_port_check_points = _route_spacing_check_points(nearby_port_plan)
self.assertEqual(device_check_points[0], {"x": ROUTE_ENDPOINT_SPACING_IGNORE, "y": 0.0})
self.assertEqual(nearby_port_check_points[0], {"x": ROUTE_ENDPOINT_SPACING_IGNORE, "y": 5.0})
self.assertLess(_polyline_spacing(device_check_points, nearby_port_check_points), ROUTE_MIN_SPACING)
adjusted = _spacing_adjusted_route_points(
LinkSpec(width=0.5, radius=10),
FakePin(0, 0, 0),
FakePin(100, 0, 180),
offset=ROUTE_MIN_SPACING,
)
self.assertEqual(adjusted[0], {"x": 0.0, "y": 0.0})
self.assertEqual(adjusted[-1], {"x": 100.0, "y": 0.0})
self.assertEqual(adjusted[2]["y"], ROUTE_MIN_SPACING)
self.assertEqual(adjusted[3]["y"], ROUTE_MIN_SPACING)
current_plan = {
"link": LinkSpec(src_inst="a", src_pin="o1", dst_inst="b", dst_pin="i1"),
"pin1": FakePin(0, 5, 0),
"pin2": FakePin(100, 5, 180),
"points": [{"x": 0, "y": 5}, {"x": 100, "y": 5}],
}
accepted_below = [{
"check_points": [{"x": 10, "y": 0}, {"x": 90, "y": 0}],
}]
accepted_above = [{
"check_points": [{"x": 10, "y": 10}, {"x": 90, "y": 10}],
}]
self.assertEqual(_spacing_offsets(current_plan, accepted_below)[0], ROUTE_MIN_SPACING / 2.0)
self.assertEqual(_spacing_offsets(current_plan, accepted_above)[0], -ROUTE_MIN_SPACING / 2.0)
adjusted_from_close_route = _first_spacing_adjusted_route(current_plan, accepted_below)
self.assertIsNotNone(adjusted_from_close_route)
self.assertEqual(adjusted_from_close_route["points"][2]["y"], 10.0)
elbow = _orthogonal_elbow({"x": 0, "y": 0}, 0, {"x": 20, "y": 30}, 270)
self.assertEqual(elbow, {"x": 20.0, "y": 0.0})
reference = _route_spacing_reference_points(
LinkSpec(),
FakePin(0, 0, 0),
FakePin(20, 30, 270),
)
self.assertEqual(reference, [{"x": 0.0, "y": 0.0}, {"x": 20.0, "y": 0.0}, {"x": 20.0, "y": 30.0}])
def test_automatic_p2p_spacing_detection_does_not_change_geometry_by_default(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def strt_p2p(self, **kwargs):
self.calls.append(("strt_p2p", kwargs))
return self
def strt_bend_strt_p2p(self, **kwargs):
self.calls.append(("strt_bend_strt_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="mmi1", src_pin="b1", dst_inst="port_1", dst_pin="io1"),
LinkSpec(src_inst="mmi2", src_pin="b1", dst_inst="port_1", dst_pin="io2"),
]
pin_map = {
("mmi1", "b1"): FakePin(0, 0, 0),
("port_1", "io1"): FakePin(100, 0, 180),
("mmi2", "b1"): FakePin(0, 5, 0),
("port_1", "io2"): FakePin(100, 5, 180),
}
warnings = []
_route_bundle_links(links, pin_map, FakeRoute, warnings)
self.assertEqual([call[0] for call in FakeRoute.calls], ["sbend_p2p", "put", "sbend_p2p", "put"])
self.assertTrue(any("Detected route spacing below 10um" in warning for warning in warnings))
self.assertEqual(FakeRoute.calls[0][1]["Lstart"], 10.5)
self.assertEqual(FakeRoute.calls[2][1]["Lstart"], 10.5)
call_names = [call[0] for call in FakeRoute.calls]
self.assertNotIn("strt_bend_strt_p2p", call_names)
self.assertNotIn("strt_p2p", call_names)
def test_sbend_lstart_spacing_uses_whole_bundle_inner_outer_order(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="mmi_top", src_pin="b1", dst_inst="port_top", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi_top", src_pin="b2", dst_inst="port_mid_top", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi_bot", src_pin="b1", dst_inst="port_mid_bot", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi_bot", src_pin="b2", dst_inst="port_bot", dst_pin="io1", width=0.5, radius=10),
]
pin_map = {
("mmi_top", "b1"): FakePin(0, 12, 0),
("port_top", "io1"): FakePin(100, 12, 180),
("mmi_top", "b2"): FakePin(0, 4, 0),
("port_mid_top", "io1"): FakePin(100, 4, 180),
("mmi_bot", "b1"): FakePin(0, -4, 0),
("port_mid_bot", "io1"): FakePin(100, -4, 180),
("mmi_bot", "b2"): FakePin(0, -12, 0),
("port_bot", "io1"): FakePin(100, -12, 180),
}
_route_bundle_links(links, pin_map, FakeRoute, [])
lstarts = [call[1]["Lstart"] for call in FakeRoute.calls if call[0] == "sbend_p2p"]
self.assertEqual(lstarts, [21.0, 10.5, 10.5, 21.0])
def test_sbend_lstart_spacing_uses_monotonic_order_for_same_direction_fanout(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="mmi1", src_pin="b1", dst_inst="port1", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi1", src_pin="b2", dst_inst="port2", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi2", src_pin="b1", dst_inst="port3", dst_pin="io1", width=0.5, radius=10),
LinkSpec(src_inst="mmi2", src_pin="b2", dst_inst="port4", dst_pin="io1", width=0.5, radius=10),
]
pin_map = {
("mmi1", "b1"): FakePin(0, 40, 0),
("port1", "io1"): FakePin(100, 20, 180),
("mmi1", "b2"): FakePin(0, 32, 0),
("port2", "io1"): FakePin(100, 12, 180),
("mmi2", "b1"): FakePin(0, 24, 0),
("port3", "io1"): FakePin(100, 4, 180),
("mmi2", "b2"): FakePin(0, 16, 0),
("port4", "io1"): FakePin(100, -4, 180),
}
_route_bundle_links(links, pin_map, FakeRoute, [])
lstarts = [call[1]["Lstart"] for call in FakeRoute.calls if call[0] == "sbend_p2p"]
self.assertEqual(lstarts, [42.0, 31.5, 21.0, 10.5])
def test_sbend_lstart_step_includes_route_width(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="r1", src_pin="b1", dst_inst="p1", dst_pin="io1", width=40, radius=10),
LinkSpec(src_inst="r2", src_pin="b1", dst_inst="p2", dst_pin="io1", width=40, radius=10),
]
pin_map = {
("r1", "b1"): FakePin(0, 0, 0),
("p1", "io1"): FakePin(100, 0, 180),
("r2", "b1"): FakePin(0, 100, 0),
("p2", "io1"): FakePin(100, 100, 180),
}
_route_bundle_links(links, pin_map, FakeRoute, [])
lstarts = [call[1]["Lstart"] for call in FakeRoute.calls if call[0] == "sbend_p2p"]
self.assertEqual(lstarts, [50.0, 50.0])
def test_sbend_lstart_spacing_handles_same_source_mmi_fanout(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="MMI_1", src_pin="b1", dst_inst="port_1", dst_pin="port_1_io2", width=0.7, radius=10),
LinkSpec(src_inst="MMI_1", src_pin="b2", dst_inst="port_1", dst_pin="port_1_io1", width=0.7, radius=10),
]
pin_map = {
("MMI_1", "b1"): FakePin(1963.1, -1931.15, 0),
("port_1", "port_1_io2"): FakePin(2047.2, -2056.3, 180),
("MMI_1", "b2"): FakePin(1963.1, -1939.85, 0),
("port_1", "port_1_io1"): FakePin(2047.2, -2066.3, 180),
}
_route_bundle_links(links, pin_map, FakeRoute, [])
lstarts = [call[1]["Lstart"] for call in FakeRoute.calls if call[0] == "sbend_p2p"]
self.assertEqual(lstarts, [21.4, 10.7])
def test_sbend_lstart_spacing_keeps_separate_route_stages_ordered(self):
from mxpic_router.builder import _route_bundle_links
from mxpic_router.eda_loader import LinkSpec
class FakePin:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.a = angle
class FakeRoute:
calls = []
def __init__(self, **kwargs):
pass
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return self
def put(self):
self.calls.append(("put", {}))
FakeRoute.calls = []
links = [
LinkSpec(src_inst="r1", src_pin="en1", dst_inst="r5", dst_pin="ep1", width=40, radius=10),
LinkSpec(src_inst="r2", src_pin="en1", dst_inst="r6", dst_pin="ep1", width=40, radius=10),
LinkSpec(src_inst="r3", src_pin="en1", dst_inst="r7", dst_pin="ep1", width=40, radius=10),
LinkSpec(src_inst="r4", src_pin="en1", dst_inst="r8", dst_pin="ep1", width=40, radius=10),
LinkSpec(src_inst="r6", src_pin="en1", dst_inst="port", dst_pin="io3", width=40, radius=10),
LinkSpec(src_inst="r5", src_pin="en1", dst_inst="port", dst_pin="io4", width=40, radius=10),
LinkSpec(src_inst="r7", src_pin="en1", dst_inst="port", dst_pin="io2", width=40, radius=10),
LinkSpec(src_inst="r8", src_pin="en1", dst_inst="port", dst_pin="io1", width=40, radius=10),
]
pin_map = {
("r1", "en1"): FakePin(0, 40, 0),
("r5", "ep1"): FakePin(100, 70, 180),
("r2", "en1"): FakePin(0, 30, 0),
("r6", "ep1"): FakePin(100, 60, 180),
("r3", "en1"): FakePin(0, 20, 0),
("r7", "ep1"): FakePin(100, 50, 180),
("r4", "en1"): FakePin(0, 10, 0),
("r8", "ep1"): FakePin(100, 40, 180),
("r6", "en1"): FakePin(100, 60, 0),
("port", "io3"): FakePin(200, 30, 180),
("r5", "en1"): FakePin(100, 70, 0),
("port", "io4"): FakePin(200, 40, 180),
("r7", "en1"): FakePin(100, 50, 0),
("port", "io2"): FakePin(200, 20, 180),
("r8", "en1"): FakePin(100, 40, 0),
("port", "io1"): FakePin(200, 10, 180),
}
_route_bundle_links(links, pin_map, FakeRoute, [])
lstarts = [call[1]["Lstart"] for call in FakeRoute.calls if call[0] == "sbend_p2p"]
self.assertEqual(lstarts[:4], [50.0, 100.0, 150.0, 200.0])
self.assertEqual(lstarts[4:], [150.0, 200.0, 100.0, 50.0])
def test_route_backend_falls_back_to_nazca_interconnect_when_forge_is_absent(self):
import mxpic_router.builder as builder
class FakeInterconnect:
calls = []
def __init__(self, radius=None, width=None, xs=None, PCB=False):
self.radius = radius
self.width = width
self.xs = xs
self.PCB = PCB
def strt_p2p(self, **kwargs):
self.calls.append(("strt_p2p", kwargs))
return "straight"
def sbend_p2p(self, **kwargs):
self.calls.append(("sbend_p2p", kwargs))
return "sbend"
def ubend_p2p(self, **kwargs):
self.calls.append(("ubend_p2p", kwargs))
return "ubend"
def bend_strt_bend_p2p(self, **kwargs):
self.calls.append(("bend_strt_bend_p2p", kwargs))
return "bend"
def strt_bend_strt_p2p(self, **kwargs):
self.calls.append(("strt_bend_strt_p2p", kwargs))
return "straight_bend_straight"
class FakeInterconnects:
Interconnect = FakeInterconnect
class FakeNazca:
interconnects = FakeInterconnects
original_import = builder._import_mxpic_forge_route
original_create = builder._NazcaInterconnectRoute._create_interconnect
try:
builder._import_mxpic_forge_route = lambda: (_ for _ in ()).throw(ImportError("no forge"))
builder._NazcaInterconnectRoute._create_interconnect = staticmethod(
lambda **kwargs: FakeInterconnect(**kwargs)
)
route_backend = builder._import_route_backend(FakeNazca)
route = route_backend(radius=20, width=0.7, xs="strip", PCB=False)
self.assertEqual(route.backend_name, "nazca Interconnect")
self.assertEqual(route.strt_p2p(pin1="a", pin2="b", arrow=False), "straight")
self.assertEqual(
route.strt_bend_strt_p2p(pin1="a", pin2="b", radius=12, arrow=False),
"straight_bend_straight",
)
self.assertEqual(route.bend_p2p(pin1="a", pin2="b", radius=10, arrow=False), "bend")
self.assertEqual(FakeInterconnect.calls[0][0], "strt_p2p")
self.assertEqual(FakeInterconnect.calls[0][1]["width"], 0.7)
self.assertEqual(FakeInterconnect.calls[1][0], "strt_bend_strt_p2p")
self.assertEqual(FakeInterconnect.calls[1][1]["radius"], 12)
self.assertEqual(FakeInterconnect.calls[2][0], "bend_strt_bend_p2p")
self.assertEqual(FakeInterconnect.calls[2][1]["radius"], 10)
finally:
builder._import_mxpic_forge_route = original_import
builder._NazcaInterconnectRoute._create_interconnect = original_create
if __name__ == "__main__":
unittest.main()