app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
main
John Lauer
Publish 0.2.3
6e27c4b
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
"""Issue #73, offline (no service): the outline takes the mark's line split (--lines, else the etch's own
<etched>.mark-plan.json, which keeps a forced --lines split), records it on the SVG, and the gate fails an
outline whose text lines differ from the model's mark.
Run: python3 -m unittest -v test_outline_lines (from tools/laser-etch)"""
import json, os, sys, tempfile, unittest
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import mark_check as MC
import mark_outline as MO
PLAN = {"text": {"lines": ["ABC-D", "1E-04B"]}}
SIDECAR = {"plan": PLAN, "etch": {"lines": ["ABC-D1E", "04B"]}}
def svg(lines=None):
attr = f' data-lines="{lines}"' if lines is not None else ""
return f'<svg viewBox="0 0 320 200"><polyline points="0,0 10,10"/><g id="adom-mark-name"{attr}><polyline points="1,1 2,2"/></g></svg>'
class OutlineLines(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.etched = os.path.join(self.tmp.name, "part-etched.step")
open(self.etched, "w").write("ISO-10303-21;\n")
def tearDown(self):
self.tmp.cleanup()
def write_sidecar(self, v=SIDECAR):
json.dump(v, open(os.path.splitext(self.etched)[0] + ".mark-plan.json", "w"))
def test_forced_split_is_read_from_the_etch_sidecar(self):
self.write_sidecar()
self.assertEqual(MO.resolve_lines(None, self.etched), (["ABC-D1E", "04B"], os.path.splitext(self.etched)[0] + ".mark-plan.json"))
def test_lines_flag_wins_and_warns_when_it_differs(self):
self.write_sidecar()
logs = []
got, src = MO.resolve_lines("ABC|D1E-04B", self.etched, log=logs.append)
self.assertEqual((got, src), (["ABC", "D1E-04B"], "--lines"))
self.assertTrue(logs and "differs from the etch's own lines" in logs[0])
def test_explicit_plan_path_and_no_record(self):
side = os.path.join(self.tmp.name, "other.mark-plan.json")
json.dump(SIDECAR, open(side, "w"))
self.assertEqual(MO.resolve_lines(None, None, side)[0], ["ABC-D1E", "04B"])
self.assertEqual(MO.resolve_lines(None, self.etched), (None, None))
# an older sidecar with no etch record falls back to its plan's lines
self.write_sidecar({"plan": PLAN})
self.assertEqual(MO.resolve_lines(None, self.etched)[0], ["ABC-D", "1E-04B"])
def test_mark_lines_prefers_the_etch_record(self):
self.assertEqual(MC.mark_lines(PLAN, SIDECAR), ["ABC-D1E", "04B"])
self.assertEqual(MC.mark_lines(PLAN, {"plan": PLAN}), ["ABC-D", "1E-04B"])
self.assertEqual(MC.mark_lines(PLAN, SIDECAR, ["X", "Y"]), ["X", "Y"])
def test_gate_fails_an_outline_with_a_different_split(self):
st, hint = MC.lines_check(svg("ABC-D|1E-04B"), ["ABC-D1E", "04B"])
self.assertEqual(st, "FAIL")
self.assertIn("ABC-D / 1E-04B differ from the model's mark ABC-D1E / 04B", hint)
self.assertEqual(MC.lines_check(svg("ABC-D1E|04B"), ["ABC-D1E", "04B"]), ("PASS", None))
self.assertEqual(MC.lines_check(svg(None), ["ABC-D1E", "04B"])[0], "WARN")
def test_record_survives_escaping(self):
import html
self.assertEqual(MC.svg_lines(svg(html.escape('A&B"|C<D', quote=True))), ['A&B"', "C<D"])
if __name__ == "__main__":
unittest.main()