import contextlib import io import json import os from pathlib import Path import tempfile import unittest from unittest.mock import patch import read_implementation_source as reader class SourceBudgetTests(unittest.TestCase): def setUp(self): directory = tempfile.TemporaryDirectory() self.addCleanup(directory.cleanup) self.root = Path(directory.name) current = self.root / "docs/current" current.mkdir(parents=True) self.policy = current / "execution-policy.json" self.packet = current / "packet.json" self.packet.write_text(json.dumps({"batchId": "repair", "supportFiles": [{"path": "source.cpp"}]})) (self.root / "source.cpp").write_text("".join(f"line {i}\n" for i in range(100))) self.marker = self.root / "turn" self.enterContext(patch.object(reader, "ROOT", self.root)) self.enterContext(patch.object(reader, "PACKET", self.packet)) self.enterContext(patch.dict(os.environ, FREECAD_SOURCE_BUDGET_MARKER=str(self.marker))) def read(self, start, path="source.cpp"): output = io.StringIO() with patch("sys.argv", ["reader", path, "--start", str(start), "--lines", "2", "--reason", "Resolve the native call anchor"]), \ contextlib.redirect_stdout(output), contextlib.redirect_stderr(io.StringIO()): code = reader.main() return code, json.loads(output.getvalue()) if output.getvalue() else None def test_continuous_repair_can_read_fifth_distinct_anchor_without_reloading_duplicates(self): self.policy.write_text(json.dumps({"continuousDevelopment": True})) for start in (1, 3, 5, 7, 9): self.assertEqual(self.read(start)[0], 0) self.assertEqual(self.read(9)[1]["status"], "already_in_context") state = json.loads(Path(str(self.marker) + ".windows.json").read_text()) self.assertEqual(len(state["windows"]), 5) self.assertEqual(self.read(11)[1]["remainingWindows"], 10) def test_legacy_profile_keeps_its_four_window_limit(self): for start in (1, 3, 5, 7): self.assertEqual(self.read(start)[0], 0) self.assertEqual(self.read(9)[0], 64) def test_continuous_profile_still_enforces_total_byte_budget(self): self.policy.write_text(json.dumps({"continuousDevelopment": True})) with patch.object(reader, "MAX_TOTAL_WINDOW_BYTES", 20): self.assertEqual(self.read(1)[0], 0) self.assertEqual(self.read(3)[0], 64) def test_multi_requirement_turn_can_reach_selected_native_anchors_after_sixteen_windows(self): self.policy.write_text(json.dumps({"continuousDevelopment": True})) packet = json.loads(self.packet.read_text()) packet["records"] = [{"apiId": f"Web:desktop:{i}"} for i in range(4)] self.packet.write_text(json.dumps(packet)) for start in range(1, 96, 2): self.assertEqual(self.read(start)[0], 0) self.assertEqual(self.read(95)[1]["status"], "already_in_context") self.assertEqual(self.read(97)[0], 64) self.assertEqual(reader.source_limits(packet), (48, 144000)) def test_contract_delegates_are_readable_but_unlisted_paths_are_not(self): directory = self.root / "docs/business-acceptance" directory.mkdir() (directory / "Web.json").write_text(json.dumps({"command": ["node", "entry.mjs"], "dependencies": ["delegate.mjs"], "caseDependencies": ["contract.ts"]})) packet = json.loads(self.packet.read_text()) packet["acceptanceContract"] = "docs/business-acceptance/Web.json" self.packet.write_text(json.dumps(packet)) for name in ["entry.mjs", "delegate.mjs", "contract.ts", "unlisted.ts"]: (self.root / name).write_text("export const value = 1;\n") for name in ["entry.mjs", "delegate.mjs", "contract.ts"]: self.assertEqual(self.read(1, name)[0], 0) self.assertEqual(self.read(1, "unlisted.ts")[0], 64) summary = json.loads(reader.packet_summary(packet)) self.assertIn("delegate.mjs", summary["supportPaths"]) def test_contract_dependencies_cannot_escape_the_project(self): outside = self.root.parent / (self.root.name + "-outside.ts") outside.write_text("secret\n") self.addCleanup(outside.unlink) directory = self.root / "docs/business-acceptance" directory.mkdir() (directory / "Web.json").write_text(json.dumps({"dependencies": [str(outside)]})) self.packet.write_text(json.dumps({"batchId": "repair", "acceptanceContract": "docs/business-acceptance/Web.json"})) self.assertEqual(self.read(1, str(outside))[0], 64) self.assertNotIn(str(outside), reader.packet_summary(json.loads(self.packet.read_text()))) def test_current_module_new_cases_and_native_tasks_do_not_require_coverage_declaration(self): self.policy.write_text(json.dumps({"continuousDevelopment": True})) self.packet.write_text(json.dumps({"batchId": "repair", "module": "Part"})) paths = ["tools/part-new-document-business-cases.mjs", "freecad-source/src/Mod/Part/Gui/TaskOffset.cpp", "web/src/part-new-document-task.tsx", "tools/partdesign-new-document-business-cases.mjs", "freecad-source/src/Mod/PartDesign/Gui/TaskOffset.cpp", "tools/part-private.json"] for name in paths: target = self.root / name target.parent.mkdir(parents=True, exist_ok=True) target.write_text("first line\nsecond line\n") for name in paths[:3]: self.assertEqual(self.read(1, name)[0], 0) for name in paths[3:]: self.assertEqual(self.read(1, name)[0], 64) self.assertEqual(self.read(1, paths[0])[1]["status"], "already_in_context") self.policy.write_text("{}") self.assertEqual(self.read(1, paths[0])[0], 64) def test_missing_filename_is_not_reported_as_a_scope_rejection(self): error = io.StringIO() with patch("sys.argv", ["reader", "missing.cpp", "--start", "1", "--reason", "Locate source"]), \ contextlib.redirect_stderr(error): self.assertEqual(reader.main(), 64) self.assertIn("does not exist", error.getvalue()) self.assertNotIn("packet dependency", error.getvalue()) if __name__ == "__main__": unittest.main()