main
Colby Knox 0.2.52 source (not yet released, needs signing): library pins keep their electrical type (input, output, IO, power...)… 4d4e030 1d ago
#!/usr/bin/env python3
"""Regression test for tools/symbol_modes.py: every PIN's x,y (Altium's Pin.Location, the BODY end) sits on
an edge of its mode's RECT, so the pin attaches to the body and its name lands inside it. Until 0.2.51 the
outer end was written, and every IC-style pin floated one pin length off the box.

    python3 dev-skills/altium-bridge-test/test_symbol_modes.py
"""
import os, subprocess, sys, tempfile

ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
TOOL = os.path.join(ROOT, "tools", "symbol_modes.py")

PINS = [("1", "GND"), ("2", "SW"), ("3", "VIN"), ("4", "FB"), ("5", "EN"), ("6", "BOOT"), ("7", "PGOOD"), ("8", "VSS")]
SYM = '(kicad_symbol_lib (symbol "T" (symbol "T_1_1"\n' + "".join(
    f'  (pin bidirectional line (at {-5.08 if i < 4 else 5.08} {2.54 * (i % 4)} {0 if i < 4 else 180}) (length 2.54)\n'
    f'    (name "{n}" (effects (font (size 1.27 1.27)))) (number "{d}" (effects (font (size 1.27 1.27)))))\n'
    for i, (d, n) in enumerate(PINS)) + "))\n"


def main():
    with tempfile.TemporaryDirectory() as t:
        sym, spec = os.path.join(t, "t.kicad_sym"), os.path.join(t, "t.spec")
        open(sym, "w").write(SYM)
        subprocess.run([sys.executable, TOOL, sym, spec, "T", "test part"], check=True, capture_output=True)
        rects, pins = {}, []
        for line in open(spec):
            f = line.strip().split("|")
            if f[0] == "RECT":
                rects[f[1]] = tuple(int(v) for v in f[2:6])
            elif f[0] == "PIN":
                pins.append((f[1], f[2], int(f[4]), int(f[5]), f[6]))
                if len(f) < 10 or f[9] != "bidirectional":   # the KiCad pin type rides along (0.2.52)
                    raise SystemExit(f"FAIL: pin {f[2]} in mode {f[1]} lost its pin type: {line.strip()}")
    bad = []
    for mode, num, x, y, orient in pins:
        x1, y1, x2, y2 = rects[mode]
        lo_x, hi_x, lo_y, hi_y = min(x1, x2), max(x1, x2), min(y1, y2), max(y1, y2)
        on = {"left": x == lo_x and lo_y <= y <= hi_y, "right": x == hi_x and lo_y <= y <= hi_y,
              "up": y == hi_y and lo_x <= x <= hi_x, "down": y == lo_y and lo_x <= x <= hi_x}[orient]
        if not on:
            bad.append(f"mode {mode} pin {num} ({orient}) at {x},{y} is not on the body edge {rects[mode]}")
    if bad:
        print("FAIL\n  " + "\n  ".join(bad[:12]))
        sys.exit(1)
    print(f"PASS: {len(pins)} pins across {len(rects)} display modes all start on the body edge and keep their pin type")


if __name__ == "__main__":
    main()