import contextlib import io import json import os from pathlib import Path import shutil import sys import tempfile import unittest from unittest.mock import patch sys.path.insert(0, str(Path(__file__).resolve().parent)) import fast_source_search as search class FastSourceSearchTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.root = Path(self.temp.name) self.batch = self.root / "batch.json" self.real_rg = shutil.which("rg") self.enterContext(patch.object(search, "ROOT", self.root)) self.enterContext(patch.object(search, "BATCH", self.batch)) self.enterContext(patch.dict(os.environ, {"FREECAD_REAL_RG": self.real_rg, "FREECAD_SOURCE_BUDGET_MARKER": ""})) def file(self, name, content="needle\n"): target = self.root / name target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content) return target def test_batch_resolves_both_repository_and_desktop_source_paths(self): web = self.file("web/src/techdraw.worker.ts") native = self.file("freecad-source/src/Mod/TechDraw/App/DrawPage.cpp") tool = self.file("tools/cases.mjs") self.batch.write_text(json.dumps({"records": [["a", "b", "c", p] for p in [ "web/src/techdraw.worker.ts", "src/Mod/TechDraw/App/DrawPage.cpp", "tools/cases.mjs", "../../outside.cpp", ]]})) self.assertEqual(search.batch_paths(), sorted([web, native, tool])) def test_scoped_search_bypasses_scheduler_wrapper_and_does_not_omit_late_files(self): wrapper = self.file("tools/scheduler-bin/rg", "#!/bin/sh\nexit 2\n") wrapper.chmod(0o755) for index in range(205): self.file(f"web/src/a{index:03}.ts", "irrelevant\n") target = self.file("web/src/techdraw.worker.ts") output = io.StringIO() with patch.dict(os.environ, {"PATH": str(wrapper.parent) + os.pathsep + os.environ["PATH"]}), patch( "sys.argv", ["fast_source_search.py", "needle", "--path", "web/src"] ), contextlib.redirect_stdout(output), contextlib.redirect_stderr(io.StringIO()): self.assertEqual(search.main(), 0) self.assertIn(str(target), output.getvalue()) with patch.dict(os.environ, {"FREECAD_REAL_RG": ""}): self.assertNotEqual(search.ripgrep(), str(wrapper)) def test_oversized_scope_is_reported_instead_of_silently_truncated(self): self.file("web/a.ts") self.file("web/z.ts") with self.assertRaisesRegex(SystemExit, "narrower --path"): search.list_source_files(self.root / "web", limit=1) def test_named_pattern_uses_existing_scoped_file(self): target = self.file("web/src/draft.ts") output = io.StringIO() with patch("sys.argv", ["fast_source_search.py", "--pattern", "needle", "--path", "web/src/draft.ts"]), \ contextlib.redirect_stdout(output), contextlib.redirect_stderr(io.StringIO()): self.assertEqual(search.main(), 0) self.assertIn(str(target), output.getvalue()) def test_isolated_packet_search_includes_native_support_paths(self): worker = self.file("web/src/draft.worker.ts", "dispatch(request)\n") native = self.file("freecad-source/src/WebAssembly/Draft.cpp", "draft.formatBatch\n") self.batch.write_text(json.dumps({"records": [["Draft:format", "format", "pending", "web/src/draft.worker.ts"]], "sourcePaths": ["freecad-source/src/WebAssembly/Draft.cpp", "../outside.cpp"]})) self.assertEqual(search.batch_paths(), sorted([worker, native])) if __name__ == "__main__": unittest.main()