"""ASV benchmarks for built-in git tools. Measures the performance of: - git status, diff, log, and blame operations - Git tool registration - Path validation overhead """ from __future__ import annotations import os import shutil import subprocess import sys import tempfile from pathlib import Path try: from cleveragents.tool.builtins import register_git_tools from cleveragents.tool.builtins.git_tools import validate_repo_path from cleveragents.tool.registry import ToolRegistry from cleveragents.tool.runner import ToolRunner except ModuleNotFoundError: sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from cleveragents.tool.builtins import register_git_tools from cleveragents.tool.builtins.git_tools import validate_repo_path from cleveragents.tool.registry import ToolRegistry from cleveragents.tool.runner import ToolRunner def _git(args: list[str], cwd: str) -> None: """Run a git command with CI-safe environment variables. Sets ``safe.directory`` so git does not refuse to operate in temp directories owned by a different user (common in Docker / CI). """ env = { **os.environ, "GIT_CONFIG_NOSYSTEM": "1", "GIT_TERMINAL_PROMPT": "0", "GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": "safe.directory", "GIT_CONFIG_VALUE_0": cwd, } subprocess.run( ["git", *args], cwd=cwd, capture_output=True, text=True, check=True, timeout=30, env=env, ) def _make_repo() -> str: """Create a temporary git repository with several commits.""" repo_dir = tempfile.mkdtemp() _git(["init"], cwd=repo_dir) _git(["config", "user.email", "bench@test.com"], cwd=repo_dir) _git(["config", "user.name", "Bench User"], cwd=repo_dir) for i in range(5): f = Path(repo_dir) / f"file_{i}.txt" f.write_text(f"content line {i}\n" * 10) _git(["add", "."], cwd=repo_dir) _git(["commit", "-m", f"Commit {i}"], cwd=repo_dir) return repo_dir class GitToolSuite: """Benchmark built-in git tool operations.""" def setup(self) -> None: self.repo_dir = _make_repo() self.registry = ToolRegistry() register_git_tools(self.registry) self.runner = ToolRunner(self.registry) def teardown(self) -> None: shutil.rmtree(self.repo_dir, ignore_errors=True) def time_git_status(self) -> None: self.runner.execute( "builtin/git-status", {"repo_path": self.repo_dir, "_sandbox_root": self.repo_dir}, ) def time_git_diff(self) -> None: self.runner.execute( "builtin/git-diff", {"repo_path": self.repo_dir, "_sandbox_root": self.repo_dir}, ) def time_git_log(self) -> None: self.runner.execute( "builtin/git-log", { "repo_path": self.repo_dir, "max_count": 5, "_sandbox_root": self.repo_dir, }, ) def time_git_blame(self) -> None: self.runner.execute( "builtin/git-blame", { "repo_path": self.repo_dir, "path": "file_0.txt", "_sandbox_root": self.repo_dir, }, ) class GitRegistrationSuite: """Benchmark git tool registration.""" def time_register_git_tools(self) -> None: registry = ToolRegistry() register_git_tools(registry) class PathValidationSuite: """Benchmark path validation overhead.""" def time_validate_none_path(self) -> None: validate_repo_path(None) def time_validate_absolute_path(self) -> None: validate_repo_path("/tmp/some/path", sandbox_root=Path("/tmp")) def time_validate_relative_path(self) -> None: validate_repo_path("subdir")