583e6b7ea2
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 54s
CI / unit_tests (pull_request) Successful in 2m39s
CI / integration_tests (pull_request) Successful in 3m10s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 5m0s
CI / lint (push) Successful in 14s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 34s
CI / typecheck (push) Successful in 35s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m23s
CI / docker (push) Successful in 38s
CI / integration_tests (push) Successful in 3m8s
CI / coverage (push) Successful in 5m10s
CI / benchmark-publish (push) Successful in 16m26s
CI / benchmark-regression (pull_request) Successful in 30m12s
Implement spec-mandated Layer 4 Predictive Error Prevention system: - ErrorPattern domain model with pattern text, historical failures, preventive checks, frequency tracking, and keyword-based matching. - ErrorPatternRepository with in-memory CRUD + context-matching query. - ErrorPatternService with record_failure(), match_patterns(), and get_statistics() methods. - Wire into plan execution via plan_lifecycle_service pre-execution hook. - Add error pattern statistics to CLI diagnostics output. Behave BDD: 11 scenarios covering recording, matching, formatting, stats. Robot Framework: 3 integration smoke tests. ASV benchmarks: pattern matching performance. ISSUES CLOSED: #571
161 lines
4.9 KiB
Python
161 lines
4.9 KiB
Python
"""ASV benchmarks for devcontainer handler auto-discovery throughput.
|
|
|
|
Measures the performance of:
|
|
- Devcontainer discovery on projects with many subdirectories
|
|
- Handler resolver cache performance for DevcontainerHandler
|
|
- Discovery result construction overhead
|
|
- is_trigger_type check throughput
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import ClassVar
|
|
|
|
try:
|
|
from cleveragents.resource.handlers.discovery import (
|
|
DevcontainerDiscoveryResult,
|
|
discover_devcontainers,
|
|
is_trigger_type,
|
|
)
|
|
from cleveragents.resource.handlers.resolver import (
|
|
clear_handler_cache,
|
|
resolve_handler,
|
|
)
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.resource.handlers.discovery import (
|
|
DevcontainerDiscoveryResult,
|
|
discover_devcontainers,
|
|
is_trigger_type,
|
|
)
|
|
from cleveragents.resource.handlers.resolver import (
|
|
clear_handler_cache,
|
|
resolve_handler,
|
|
)
|
|
|
|
_VALID_DC_JSON: str = json.dumps(
|
|
{"name": "bench-devcontainer", "image": "ubuntu:latest"}
|
|
)
|
|
|
|
|
|
class TimeDiscoveryThroughput:
|
|
"""Benchmark auto-discovery throughput on projects with many subdirs."""
|
|
|
|
timeout = 60
|
|
params: ClassVar[list[int]] = [10, 50, 100]
|
|
param_names: ClassVar[list[str]] = ["num_subdirs"]
|
|
|
|
def setup(self, num_subdirs: int) -> None:
|
|
"""Create a temp directory with many subdirectories."""
|
|
self.tmp_dir = tempfile.mkdtemp()
|
|
base = Path(self.tmp_dir)
|
|
# Create the devcontainer config
|
|
dc_dir = base / ".devcontainer"
|
|
dc_dir.mkdir()
|
|
(dc_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
|
|
# Create many sibling directories (noise)
|
|
for i in range(num_subdirs):
|
|
(base / f"subdir_{i:04d}").mkdir()
|
|
|
|
def teardown(self, num_subdirs: int) -> None:
|
|
"""Clean up temp directory."""
|
|
import shutil
|
|
|
|
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
|
|
def time_discovery_with_subdirs(self, num_subdirs: int) -> None:
|
|
"""Time discovery scanning with many peer directories."""
|
|
discover_devcontainers(self.tmp_dir, "git-checkout")
|
|
|
|
|
|
class TimeDiscoveryEmpty:
|
|
"""Benchmark discovery on directory with no devcontainer."""
|
|
|
|
timeout = 30
|
|
|
|
def setup(self) -> None:
|
|
"""Create empty temp directory."""
|
|
self.tmp_dir = tempfile.mkdtemp()
|
|
base = Path(self.tmp_dir)
|
|
for i in range(50):
|
|
(base / f"subdir_{i:04d}").mkdir()
|
|
|
|
def teardown(self) -> None:
|
|
"""Clean up."""
|
|
import shutil
|
|
|
|
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
|
|
def time_discovery_no_match(self) -> None:
|
|
"""Time discovery when no devcontainer exists."""
|
|
discover_devcontainers(self.tmp_dir, "git-checkout")
|
|
|
|
|
|
class TimeHandlerResolver:
|
|
"""Benchmark handler resolver cache for DevcontainerHandler."""
|
|
|
|
timeout = 30
|
|
_ref = "cleveragents.resource.handlers.devcontainer:DevcontainerHandler"
|
|
|
|
def setup(self) -> None:
|
|
"""Pre-warm resolver cache."""
|
|
clear_handler_cache()
|
|
resolve_handler(self._ref)
|
|
|
|
def time_cached_resolution(self) -> None:
|
|
"""Time cached handler resolution (should be near-zero)."""
|
|
resolve_handler(self._ref)
|
|
|
|
def time_cold_resolution(self) -> None:
|
|
"""Time cold handler resolution (import + instantiate)."""
|
|
clear_handler_cache()
|
|
resolve_handler(self._ref)
|
|
|
|
|
|
class TimeIsTriggerType:
|
|
"""Benchmark is_trigger_type check throughput."""
|
|
|
|
timeout = 10
|
|
|
|
def time_trigger_type_positive(self) -> None:
|
|
"""Time is_trigger_type for a trigger type."""
|
|
for _ in range(1000):
|
|
is_trigger_type("git-checkout")
|
|
|
|
def time_trigger_type_negative(self) -> None:
|
|
"""Time is_trigger_type for a non-trigger type."""
|
|
for _ in range(1000):
|
|
is_trigger_type("devcontainer-instance")
|
|
|
|
|
|
class TimeDiscoveryResult:
|
|
"""Benchmark DevcontainerDiscoveryResult construction."""
|
|
|
|
timeout = 30
|
|
|
|
def setup(self) -> None:
|
|
"""Create temp file for result construction."""
|
|
self.tmp_dir = tempfile.mkdtemp()
|
|
self.dc_file = Path(self.tmp_dir) / "devcontainer.json"
|
|
self.dc_file.write_text(_VALID_DC_JSON, encoding="utf-8")
|
|
self.config_data: dict[str, object] = json.loads(_VALID_DC_JSON)
|
|
|
|
def teardown(self) -> None:
|
|
"""Clean up."""
|
|
import shutil
|
|
|
|
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
|
|
def time_result_construction(self) -> None:
|
|
"""Time DevcontainerDiscoveryResult instantiation."""
|
|
for _ in range(1000):
|
|
DevcontainerDiscoveryResult(
|
|
config_path=self.dc_file,
|
|
config_data=self.config_data,
|
|
parent_location=self.tmp_dir,
|
|
)
|