#!/usr/bin/env python3 import json import sys import unittest from pathlib import Path from tempfile import TemporaryDirectory sys.path.insert(0, str(Path(__file__).resolve().parent)) from normalize_sources import generate class NormalizeSourcesTests(unittest.TestCase): def create_tree(self): directory = TemporaryDirectory() root = Path(directory.name) (root / "coverage").mkdir() (root / "linuxcnc/src").mkdir(parents=True) (root / "docs").mkdir() (root / "linuxcnc/configs/sim/demo").mkdir(parents=True) (root / "linuxcnc/tests/demo").mkdir(parents=True) (root / "linuxcnc/src/demo.c").write_text("int main(void) { return 0; }\n", encoding="utf-8") (root / "docs/demo.md").write_text("# Demo\n", encoding="utf-8") (root / "linuxcnc/configs/sim/demo/main.ini").write_text("[EMC]\n", encoding="utf-8") (root / "linuxcnc/tests/demo/test.sh").write_text("#!/bin/sh\n", encoding="utf-8") indexes = { "source-index.jsonl": [{"bytes": 1, "kind": "source", "path": "linuxcnc/src/demo.c", "sha256": "a", "source_id": "build:demo"}], "document-capabilities.jsonl": [{"anchor": "demo", "capability_source_id": "doc:demo#demo", "document": "docs/demo.md", "domain": "demo", "source_sha": "b", "title": "Demo"}], "source-symbols.jsonl": [{"language": "c", "line": 1, "parse_status": "parsed", "path": "linuxcnc/src/demo.c", "source_sha": "a", "symbol": "main", "symbol_kind": "function", "symbol_source_id": "symbol:demo"}], "config-resources.jsonl": [{"edge_kind": "ini:EMC", "entrypoint": "linuxcnc/configs/sim/demo/main.ini", "line": 1, "origin_path": "linuxcnc/configs/sim/demo/main.ini", "parse_status": "resolved_file", "path": "linuxcnc/configs/sim/demo/main.ini", "resource_kind": "ini", "resource_source_id": "resource:demo", "source_sha": "c", "target_sha256": "c"}], "test-entries.jsonl": [{"command": "bash linuxcnc/tests/demo/test.sh", "config_refs": [], "expected": [], "inputs": [], "parse_status": "parsed", "path": "linuxcnc/tests/demo/test.sh", "portability": "portable_candidate", "source_sha": "d", "test_kind": "shell_test", "test_source_id": "test:demo"}], } for name, rows in indexes.items(): (root / "coverage" / name).write_text("".join(json.dumps(row) + "\n" for row in rows), encoding="utf-8") return directory, root def test_normalized_ids_are_unique_and_link_files(self): directory, root = self.create_tree() self.addCleanup(directory.cleanup) output = root / "coverage/normalized.jsonl" count, _ = generate(root, output, root / "coverage/report.json") rows = [json.loads(line) for line in output.read_text(encoding="utf-8").splitlines()] self.assertEqual(count, 5) self.assertEqual(len(rows), len({row["source_id"] for row in rows})) symbol = next(row for row in rows if row["origin_index"] == "symbol") self.assertIn("src:file:linuxcnc/src/demo.c", symbol["related_source_ids"]) def test_missing_and_hash_conflicts_are_reported(self): directory, root = self.create_tree() self.addCleanup(directory.cleanup) source = root / "coverage/source-index.jsonl" source.write_text(json.dumps({"bytes": 1, "kind": "source", "path": "linuxcnc/src/demo.c", "sha256": "different", "source_id": "build:demo"}) + "\n", encoding="utf-8") report_path = root / "coverage/report.json" generate(root, root / "out.jsonl", report_path) report = json.loads(report_path.read_text(encoding="utf-8")) self.assertTrue(report["hash_conflicts"]) self.assertFalse(report["missing_paths"]) def test_generation_is_deterministic(self): directory, root = self.create_tree() self.addCleanup(directory.cleanup) _, first = generate(root, root / "one.jsonl", root / "report.json") _, second = generate(root, root / "two.jsonl", root / "report.json") self.assertEqual(first, second) self.assertEqual((root / "one.jsonl").read_bytes(), (root / "two.jsonl").read_bytes()) if __name__ == "__main__": unittest.main()