diff --git a/misc/qet-mcp/qet_mcp.py b/misc/qet-mcp/qet_mcp.py index 2372b28d0..9763016f5 100755 --- a/misc/qet-mcp/qet_mcp.py +++ b/misc/qet-mcp/qet_mcp.py @@ -118,10 +118,17 @@ def _elements(root: ET.Element): yield i, el +def _wires(diagram: ET.Element): + """The folio's conductors: children of only. A folio's wire + numbering rule is also saved as a tag, under , and + is not a wire.""" + return diagram.findall("conductors/conductor") + + def _conductors(root: ET.Element): for i, d in _folios(root): index = _terminal_index(d) - for c in d.iter("conductor"): + for c in _wires(d): yield i, c, index @@ -245,7 +252,7 @@ def tool_project_info(path: str) -> dict: "index": i, "title": d.get("title", ""), "elements": sum(1 for _ in d.iter("element")), - "conductors": sum(1 for _ in d.iter("conductor")), + "conductors": len(_wires(d)), }) return { "file": str(Path(path).expanduser()), diff --git a/misc/qet-mcp/test_qet_mcp.py b/misc/qet-mcp/test_qet_mcp.py index e446ed3bc..1328c8ecc 100644 --- a/misc/qet-mcp/test_qet_mcp.py +++ b/misc/qet-mcp/test_qet_mcp.py @@ -1308,6 +1308,23 @@ class ReadTools(unittest.TestCase): # (value, how many nodes carry it) self.assertEqual([tuple(v) for v in scan["distinct_values"]], [("C1", 1)]) + def test_a_wire_numbering_rule_is_not_a_wire(self): + """A folio's conductor numbering rule is saved as ; + counting every tag reported it as a wire with no ends.""" + with tempfile.TemporaryDirectory() as tmp: + rule = ('' + '') + wire = '' + a, b = Path(tmp) / "a.qet", Path(tmp) / "b.qet" + a.write_text(f'{rule}{wire}') + b.write_text(f'{wire}') + self.assertEqual(m.tool_project_info(str(a))["folios"][0]["conductors"], 1) + self.assertEqual(m.tool_project_info(str(a))["conductor_count"], 1) + self.assertEqual([c["num"] for c in m.tool_conductors(str(a))["conductors"]], ["W1"]) + # Removing only the rule changes no wire. + c = m.tool_diff(str(a), str(b))["conductors"] + self.assertEqual((c["added"], c["removed"]), ([], [])) + # ========================================================================== # protocol @@ -2804,6 +2821,28 @@ class PlcIntegration(unittest.TestCase): @needs_examples class CorpusIntegration(unittest.TestCase): + def test_folio_counts_match_what_qelectrotech_itself_holds(self): + """Element and conductor counts per folio, from the file, against + QElectroTech's own counts after loading it -- over every example. + schema_indus.qet caught a numbering rule counted as a wire.""" + script = ('var o = [];' + 'for (var f = 0; f < qet.folioCount(); f++)' + ' o.push([qet.elementCount(f), qet.conductorCount(f)]);' + 'qet.log("COUNTS " + JSON.stringify(o));') + checked = 0 + for f in sorted(Path(EXAMPLES).glob("*.qet")): + with self.subTest(project=f.name): + r = m._run_qet(BINARY, [str(f)], elements_dir=ELEMENTS or None, + script=script, tail=200_000) + line = next(ln for ln in (r["stdout"] + "\n" + r["stderr"]).splitlines() + if "COUNTS " in ln) + qet = json.loads(line.split("COUNTS ", 1)[1]) + mcp = [[x["elements"], x["conductors"]] + for x in m.tool_project_info(str(f))["folios"]] + self.assertEqual(mcp, qet) + checked += 1 + self.assertGreater(checked, 20) + def test_conductor_keys_never_collide_across_the_shipped_examples(self): """Keying on terminal geometry alone merged nine distinct conductors of schema_indus.qet; the shipped corpus is the check."""