app
service-kicad
Public Made by Adomby adom
Shared headless KiCad 10: DRC, ERC, SVG/Gerber/STEP export, Altium library conversion, symbol/footprint/3D-model lookup. Every Adom tool shells to this CLI instead of calling the HTTP API directly.
master
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
#!/usr/bin/env python3
"""Exercise `service-kicad pcb drc` / `sch erc` against a LIVE service using
the fixtures attached to adom/service-kicad#2 and #3.
python3 verification/test_report_cli.py [path/to/service-kicad]
Needs network access to the service the CLI resolves (SERVICE_KICAD_URL
honoured). Asserts the count policy, the exit-code contract, the native
passthrough, and the ERC coordinate-scale hint. It does not deploy or
change anything.
"""
import json, os, subprocess, sys, tempfile
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FIX = os.path.join(ROOT, "report", "tests", "fixtures")
CLI = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, "target", "release", "service-kicad")
BOARD = os.path.join(FIX, "aperture-check.kicad_pcb")
SCH = os.path.join(FIX, "erc-source-scaled.kicad_sch")
def run(*args):
p = subprocess.run([CLI, *args], capture_output=True, text=True)
return p.returncode, p.stdout, p.stderr
failures = []
def check(name, cond, detail=""):
print(("ok " if cond else "FAIL ") + name + (f" {detail}" if detail and not cond else ""))
if not cond: failures.append(name)
# issue 3: counts from findings, explicit verdict, exit 5
code, out, err = run("pcb", "drc", BOARD)
check("drc: exit 5 on error findings", code == 5, f"exit={code} err={err}")
check("drc: verdict line says FAILED with 2 errors, 1 warning", out.startswith("DRC FAILED: 2 errors, 1 warning"), out.splitlines()[:1])
check("drc: provenance line has kicad version + sha256", "kicad 10." in out and "sha256" in out, out.splitlines()[1:2])
check("drc: DRC_FAILED hint", "hint [DRC_FAILED]" in out)
code, out, err = run("pcb", "drc", BOARD, "--exit-zero")
check("drc: --exit-zero exits 0 and says so", code == 0 and "--exit-zero" in err, f"exit={code}")
with tempfile.TemporaryDirectory() as d:
j = os.path.join(d, "r.json"); n = os.path.join(d, "n.json")
code, out, err = run("pcb", "drc", BOARD, "--format", "json", "--out", j)
doc = json.load(open(j)); a = doc.get("adom_summary", {})
check("drc json: adom_summary added, native keys intact", "violations" in doc and a.get("errors") == 2 and a.get("status") == "failed")
check("drc json: exit_code in summary matches process", a.get("exit_code") == code == 5)
check("drc json: input sha256 + service provenance", len(a.get("input", {}).get("sha256", "")) == 64 and a.get("service", {}).get("version"))
code, out, err = run("pcb", "drc", BOARD, "--format", "native", "--out", n)
nat = json.load(open(n))
check("drc native: untouched report (no adom_summary), still exit 5", "adom_summary" not in nat and code == 5)
# issue 2: ERC scale hint, nothing rescaled
code, out, err = run("sch", "erc", SCH, "--format", "json")
doc = json.loads(out); a = doc["adom_summary"]
check("erc: exit 5 (incomplete circuit has real errors)", code == 5)
check("erc: scale check says scaled by 100", a["erc_scale"]["status"] == "scaled" and a["erc_scale"]["factor"] == 100.0, a["erc_scale"])
check("erc: ERC_COORDINATES_SCALED hint first", a["hints"][0]["code"] == "ERC_COORDINATES_SCALED")
first = doc["sheets"][0]["violations"][0]["items"][0]["pos"]
check("erc: raw positions preserved (not rescaled)", first["x"] < 5, first)
# malformed input: diagnosed locally, exit 1
with tempfile.TemporaryDirectory() as d:
broken = os.path.join(d, "broken.kicad_sch")
open(broken, "wb").write(open(SCH, "rb").read()[:2000])
code, out, err = run("sch", "erc", broken)
check("truncated schematic: exit 1 with INPUT_UNLOADABLE + paren balance", code == 1 and "INPUT_UNLOADABLE" in err and "paren balance" in err, err)
wrong = os.path.join(d, "board.kicad_sch")
open(wrong, "wb").write(open(BOARD, "rb").read())
code, out, err = run("sch", "erc", wrong)
check("board posted as schematic: named as such, exit 1", code == 1 and "not a schematic" in err, err)
print(f"\n{len(failures)} failure(s)" if failures else "\nall checks passed")
sys.exit(1 if failures else 0)