freeroute/tests/board/test_board.py
Ryan Malloy dd40aaba5d Add board data-model foundation: units, layers, transform, nets, clearance
Ports the foundational board classes: Unit (mil/inch/mm/um with
micrometer scaling), Layer/LayerStructure (the layer stack with name and
signal-layer lookups), CoordinateTransform (DSN<->board scaling by the
resolution), Net/Nets (connectivity keyed by (name, subnet) and a board-
unique net number), and ClearanceMatrix (class x class spacing with a
reserved null class and a default class).

The per-layer axis of ClearanceMatrix is simplified to a single value per
class pair (DSN default clearance rules are layer-independent for the
boards we build); the router phase can add the layer dimension.

Unit-tested: unit scaling/parsing, layer lookups, transform round-trip,
net registration/lookup, and clearance default/append/symmetry.
2026-07-12 08:29:32 -06:00

70 lines
2.1 KiB
Python

"""Unit tests for the board data model (layers, units, transform, nets, clearance)."""
from __future__ import annotations
from freeroute.board import (
ClearanceMatrix,
CoordinateTransform,
Layer,
LayerStructure,
Nets,
Unit,
)
def test_unit_scale_and_parse():
assert Unit.from_string("um") is Unit.UM
assert Unit.from_string("MM") is Unit.MM
assert Unit.from_string("bogus") is None
assert Unit.scale(1.0, Unit.MM, Unit.UM) == 1000.0
assert Unit.scale(1.0, Unit.INCH, Unit.MIL) == 1000.0
assert str(Unit.MIL) == "mil"
def test_layer_structure_lookup():
ls = LayerStructure([Layer("F.Cu", True), Layer("In1", False), Layer("B.Cu", True)])
assert len(ls) == 3
assert ls.get_no("F.Cu") == 0
assert ls.get_no("B.Cu") == 2
assert ls.get_no("missing") == -1
assert ls.signal_layer_count() == 2
assert ls.get_signal_layer(1).name == "B.Cu"
def test_coordinate_transform_roundtrip():
t = CoordinateTransform(10)
assert t.dsn_to_board(5) == 50
assert t.board_to_dsn(50) == 5
p = t.dsn_to_board_point(100, -200)
assert (p.x, p.y) == (1000, -2000)
back = t.board_to_dsn_point(p)
assert (back.x, back.y) == (100.0, -200.0)
def test_nets_add_and_lookup():
nets = Nets()
a = nets.add("GND")
b = nets.add("VCC", subnet_number=1)
assert a.net_number == 1
assert b.net_number == 2
assert nets.max_net_no() == 2
assert nets.get("GND") is a
assert nets.get_by_number(2) is b
assert nets.get("missing") is None
assert nets.names() == {"GND", "VCC"}
assert len(nets) == 2
def test_clearance_matrix_default_and_classes():
cm = ClearanceMatrix.default_instance(200)
assert cm.get_class_count() == 2 # null + default
assert cm.get_name(0) == "null"
assert cm.get_name(1) == "default"
assert cm.get_value(1, 1) == 200
assert cm.get_value(0, 1) == 0 # null class has no clearance
via = cm.append_class("via")
assert cm.get_no("via") == via
cm.set_value(1, via, 300)
assert cm.get_value(via, 1) == 300 # symmetric
assert cm.get_value(1, 1, add_safety_margin=True) == 216 # +16 margin