master
John Lauer 0.9.350 notes and the model check tests 4feafd6 2mo ago
import sys, tempfile, unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from handlers import model_check, install_footprint

STEP = b"ISO-10303-21;\nHEADER;ENDSEC;\nDATA;\n#1=MANIFOLD_SOLID_BREP('x',#2);\n#2=CLOSED_SHELL('',());\nENDSEC;\nEND-ISO-10303-21;\n"

def fp_text(model_clause):
    return '(footprint "X" (version 20240108) (generator "t") (layer "F.Cu") (attr smd) (pad "1" smd rect (at 0 0) (size 1 1) (layers "F.Cu"))' + model_clause + ')'

class ModelCheck(unittest.TestCase):
    def setUp(self):
        self.tmp = tempfile.TemporaryDirectory(); d = Path(self.tmp.name)
        self.pretty = d / "Adom.pretty"; self.pretty.mkdir()
        self.base = d / "kicad"; (self.base / "share/kicad/3dmodels/Adom.3dshapes").mkdir(parents=True)
        (self.base / "share/kicad/3dmodels/Adom.3dshapes/GOOD.step").write_bytes(STEP)
        (self.pretty / "BARE.step").write_bytes(STEP)
        (self.pretty / "EMPTY.step").write_bytes(b"ISO-10303-21;\nHEADER;ENDSEC;DATA;ENDSEC;END-ISO-10303-21;")
        self.info = {"version": "10.0", "base_dir": str(self.base), "user_dir": str(d)}
        self.p1 = patch.object(model_check, "_library_dir", return_value=str(self.pretty)); self.p1.start()
        self.p2 = patch.object(model_check, "_kicad_common", return_value={}); self.p2.start()
    def tearDown(self): self.p1.stop(); self.p2.stop(); self.tmp.cleanup()
    def write(self, name, clause): (self.pretty / f"{name}.kicad_mod").write_text(fp_text(clause))
    def test_good_stock_reference(self):
        self.write("A", '(model "${KICAD10_3DMODEL_DIR}/Adom.3dshapes/GOOD.step" (offset (xyz 0 0 0)) (scale (xyz 1 1 1)) (rotate (xyz 0 0 0)))')
        r = model_check.check_footprint(self.info, "Adom", "A"); self.assertTrue(r["ok"], r); self.assertEqual(r["models"][0]["format"], "step"); self.assertGreater(r["models"][0]["geometryHints"], 0)
    def test_bare_relative_next_to_footprint_is_not_found(self):
        self.write("B", '(model "BARE.step" (offset (xyz 0 1.39 0.18)) (scale (xyz 1 1 1)) (rotate (xyz 0 0 -90)))')
        r = model_check.check_footprint(self.info, "Adom", "B"); self.assertFalse(r["ok"]); self.assertEqual(r["warnings"][0]["code"], "not_found_relative"); self.assertIn("3DMODEL_DIR", r["warnings"][0]["hint"])
    def test_absolute_empty_geometry_and_hidden_and_scale(self):
        p = str(self.pretty / "EMPTY.step").replace("\\", "/")
        self.write("C", f'(model "{p}" (hide yes) (offset (xyz 0 0 0)) (scale (xyz 0 1 1)) (rotate (xyz 0 0 0)))')
        r = model_check.check_footprint(self.info, "Adom", "C"); codes = sorted(w["code"] for w in r["warnings"]); self.assertEqual(codes, ["empty_geometry", "hidden", "implausible_scale"])
    def test_no_model_and_exempt(self):
        self.write("D", ""); r = model_check.check_footprint(self.info, "Adom", "D"); self.assertEqual(r["warnings"][0]["code"], "no_model_reference")
        self.write("TestPoint_Pad", ""); r = model_check.check_footprint(self.info, "Adom", "TestPoint_Pad"); self.assertTrue(r["ok"]); self.assertIn("none expected", r["note"])
    def test_unresolved_variable(self):
        self.write("E", '(model "${NOPE}/x.step")'); r = model_check.check_footprint(self.info, "Adom", "E"); self.assertEqual(r["warnings"][0]["code"], "unresolved_variable")
    def test_board(self):
        d = Path(self.tmp.name); board = d / "b.kicad_pcb"
        board.write_text('(kicad_pcb (version 20240108) (generator "t") (footprint "Adom:A" (layer "F.Cu") (property "Reference" "U1") (model "${KICAD10_3DMODEL_DIR}/Adom.3dshapes/GOOD.step")) (footprint "Adom:B" (layer "F.Cu") (property "Reference" "U2") (model "BARE.step")) (footprint "TestPoint:TP" (layer "F.Cu") (property "Reference" "TP1")) (footprint "Adom:C" (layer "F.Cu") (property "Reference" "U3")))')
        r = model_check.check_board(self.info, str(board)); c = r["counts"]
        self.assertEqual((c["footprints"], c["withModel"], c["noModelExpected"], c["missingModel"], c["unresolvedModel"]), (4, 2, 1, 1, 1)); self.assertEqual([x["reference"] for x in r["footprintsWithWarnings"]], ["U2", "U3"])

if __name__ == "__main__":
    unittest.main()