import json import sys import unittest from pathlib import Path from tempfile import TemporaryDirectory sys.path.insert(0, str(Path(__file__).resolve().parent)) from generate_kinematics_config_graph import generate class KinematicsConfigGraphTests(unittest.TestCase): def make_tree(self): directory = TemporaryDirectory() root = Path(directory.name) coverage = root / "coverage" coverage.mkdir() config = root / "linuxcnc/configs/sim/demo" config.mkdir(parents=True) (config / "demo.ini").write_text( "[KINS]\nKINEMATICS = trivkins\n[HAL]\nHALFILE = demo.hal\n", encoding="utf-8", ) (config / "demo.hal").write_text( "loadrt [KINS]KINEMATICS\nnet select => motion.switchkins-type\n", encoding="utf-8", ) matrix = root / "linuxcnc/tests/matrixkins" matrix.mkdir(parents=True) (matrix / "test.ini").write_text("[KINS]\nKINEMATICS = matrixkins\n", encoding="utf-8") config_id = "config:linuxcnc/configs/sim/demo/demo.ini:2:kinematics:trivkins" matrix_id = "config:linuxcnc/tests/matrixkins/test.ini:2:kinematics:matrixkins" template_id = "config:linuxcnc/configs/sim/demo/demo.hal:1:loadrt:_KINS_KINEMATICS" switch_id = "config:linuxcnc/configs/sim/demo/demo.hal:2:switchkins:motion.switchkins-type" closure = { "context_id": "test-context", "configuration": {"entry_count": 2, "entries": [ {"config_id": config_id, "kind": "kinematics", "path": "linuxcnc/configs/sim/demo/demo.ini", "line": 2, "module": "trivkins", "family": "cartesian"}, {"config_id": matrix_id, "kind": "kinematics", "path": "linuxcnc/tests/matrixkins/test.ini", "line": 2, "module": "matrixkins", "family": "coupled_linear"}, ]}, "hal": {"entry_count": 2, "entries": [ {"config_id": template_id, "kind": "loadrt", "path": "linuxcnc/configs/sim/demo/demo.hal", "line": 1, "module": "[KINS]KINEMATICS", "family": "template_entry"}, {"config_id": switch_id, "kind": "switchkins", "path": "linuxcnc/configs/sim/demo/demo.hal", "line": 2, "module": "motion.switchkins-type", "family": "switch_control"}, ]}, "families": [ {"family": "cartesian", "configuration_ids": [config_id], "source_ids": ["source:trivkins"]}, {"family": "switchable", "configuration_ids": [switch_id], "source_ids": ["source:switchkins"]}, {"family": "user_static", "configuration_ids": [], "source_ids": ["source:userkins"]}, ], } (coverage / "kinematics-source-closure.report.json").write_text(json.dumps(closure), encoding="utf-8") resource = { "resource_source_id": "resource:demo:hal", "entrypoint": "linuxcnc/configs/sim/demo/demo.ini", "path": "linuxcnc/configs/sim/demo/demo.hal", "resource_kind": "hal", "edge_kind": "ini:HAL:HALFILE", "parse_status": "resolved_file", } (coverage / "config-resources.jsonl").write_text(json.dumps(resource) + "\n", encoding="utf-8") (coverage / "source-index.jsonl").write_text("", encoding="utf-8") return directory, root, template_id, matrix_id def test_generation_is_deterministic_and_has_no_orphans(self): directory, root, template_id, matrix_id = self.make_tree() self.addCleanup(directory.cleanup) output, samples_output = root / "report.json", root / "samples.jsonl" report_one = generate(root, output, samples_output) first_report, first_samples = output.read_bytes(), samples_output.read_bytes() report_two = generate(root, output, samples_output) self.assertEqual(first_report, output.read_bytes()) self.assertEqual(first_samples, samples_output.read_bytes()) self.assertEqual(report_one["closure"]["orphan_entry_ids"], []) self.assertEqual(report_one["closure"]["unowned_template_ids"], []) self.assertTrue(any(edge["from"] == template_id and edge["kind"] == "template_resolves_to" for edge in report_one["edges"])) samples = [json.loads(line) for line in samples_output.read_text(encoding="utf-8").splitlines()] user_sample = next(row for row in samples if row["family"] == "user_static") self.assertEqual(user_sample["configuration_id"], matrix_id) self.assertEqual(report_one, report_two) if __name__ == "__main__": unittest.main()