Prompt file imported from joeycmlam/agentsb (
.github/prompts/plan-enhanceCoverageAnalyzer.prompt.md). Copyright stays with the author.
Plan: Enable Automated Coverage Generation via Copilot
Enhance coverage_analyzer.py to intelligently run tests and generate fresh coverage reports using Copilot SDK. Currently, it only reads existing reports - this adds capability to execute test frameworks with coverage collection when reports are missing or stale, auto-install dependencies, and use Copilot to determine optimal test commands for each repository's specific setup.
Approach
Read-First Strategy:
- Attempt to read existing coverage reports (current behavior)
- Check if report exists and is fresh (modified within configurable threshold)
- If missing or stale, generate new report by running tests with coverage
- Fall back to existing stale report if generation fails
Steps
-
Add imports and constants to coverage_analyzer.py
- Import
asyncio,subprocess,shutilfor command execution - Import
copilot_servicetypes:CopilotService - Import
datetime,timefor staleness checking - Add constants:
TEST_TIMEOUT = 300COVERAGE_TIMEOUT = 60STALE_THRESHOLD_HOURS = 24(report older than this triggers regeneration)
- Import
-
Add constructor to
CoverageAnalyzerclass- Accept optional
copilot_service: CopilotServiceparameter - Store for use in command generation
- Matches pattern from
TestFileAnalyzer.__init__()
- Accept optional
-
Create
_is_coverage_stale()helper method- Takes
coverage_file: Pathparameter - Checks file modification time using
coverage_file.stat().st_mtime - Compares against
STALE_THRESHOLD_HOURS - Returns
bool(True if file doesn't exist OR older than threshold) - Logs staleness reason: "Coverage report not found" vs "Coverage report is X hours old"
- Takes
-
Create
generate_coverage_if_needed()async method- Main orchestration method called by
analyzer.py - Takes
repo_path: Path,metrics: TestMetrics - Step 1: Check for existing coverage files in common locations:
coverage.xml,coverage/coverage-final.json,htmlcov/index.html
- Step 2: If found, call
_is_coverage_stale()to check freshness - Step 3: If missing OR stale:
- Check if test frameworks detected (from
metrics.test_frameworks) - Call
_determine_coverage_command()using Copilot - Call
_install_dependencies()if needed - Execute command via
_execute_coverage_command()
- Check if test frameworks detected (from
- Step 4: Set metric flag
metrics.coverage_generated = True/False - Returns
boolindicating if new coverage was generated
- Main orchestration method called by
-
Create
_determine_coverage_command()async method- Uses
copilot_service.send_prompt()with repo context:- Detected frameworks from
metrics.test_frameworks - Languages from
metrics.languages - Repo structure (presence of
package.json,requirements.txt, etc.)
- Detected frameworks from
- Prompt asks Copilot: "What command generates coverage for [frameworks] in this repo?"
- Parses response to extract command string
- Falls back to predefined commands if Copilot unavailable:
- Python + pytest →
coverage run -m pytest && coverage xml - JavaScript + jest →
npm test -- --coverage - Matches pattern map from research findings
- Python + pytest →
- Uses
-
Create
_install_dependencies()async method- Detects package manager:
requirements.txt→ pip,package.json→ npm - For Python: Run
pip install coverage pytest(if pytest in frameworks) - For JavaScript: Run
npm install(dependencies already in package.json) - Uses same async subprocess pattern from
git_analyzer._run_git_command() - Timeout: 120 seconds for install operations
- Logs installation status via
get_logger()
- Detects package manager:
-
Create
_execute_coverage_command()async method- Accepts
command: strandrepo_path: Path - Splits command into parts for
asyncio.create_subprocess_exec() - Configure:
cwd=repo_path, capture stdout/stderr - Wait with 300 second timeout (constant
TEST_TIMEOUT) - Returns
(success: bool, output: str, error: str) - Uses pattern from git_analyzer.py
- Accepts
-
Create
_validate_tools_installed()helper method- Checks if required tools exist:
pytest,coverage,npm,jest - Uses
shutil.which(tool_name)to verify PATH availability - Called before
_install_dependencies()to determine what's needed - Returns
List[str]of missing tools
- Checks if required tools exist:
-
Update
extract_coverage()method- Keep existing parsing logic unchanged
- Add logging when coverage files found vs not found
- Add parameter to record coverage source in metrics
- Log: "Using existing coverage report" vs "Using generated coverage report"
-
Update analyzer.py to call new flow
- Pass
copilot_servicetoCoverageAnalyzer.__init__() - Call sequence:
coverage_analyzer = CoverageAnalyzer(copilot_service) # Try to generate if needed (checks staleness internally) await coverage_analyzer.generate_coverage_if_needed(repo_path, metrics) # Then extract (will use newly generated or existing report) await coverage_analyzer.extract_coverage(repo_path, metrics) - Wrap in try/except to handle execution failures gracefully
- Matches async patterns in
analyze_repository()method
- Pass
-
Add error handling and logging
- Use
get_logger()consistently for all operations log_metric()for:- Coverage staleness status ("Fresh", "Stale", "Missing")
- Tool installation status
- Test execution time
- Coverage generation success/failure
- Coverage source ("Existing", "Generated", "Fallback")
- Store error details in
metricsif new error field added, or log warnings - Non-fatal failures → fall back to existing report reading (if stale report exists)
- Log decision points: "Coverage report is fresh, skipping generation" vs "Generating new coverage report"
- Use
Verification
- Unit test: Mock Copilot service, verify command generation logic and staleness checking
- Test
_is_coverage_stale()with various file timestamps - Test
generate_coverage_if_needed()decision tree
- Test
- Integration test: Run against test repository with pytest setup
- Scenario 1: No coverage file exists → should generate new
- Scenario 2: Fresh coverage file exists (< 24h) → should skip generation
- Scenario 3: Stale coverage file exists (> 24h) → should regenerate
- Verify coverage.xml generated/reused correctly
- Verify metrics populated with correct source
- Manual test: Execute
repo_analyzer.py --config repo_list_test.json- Observer logs for:
- Staleness check decisions
- Copilot command suggestion (when triggered)
- Dependency installation (when triggered)
- Test execution (when triggered)
- Coverage parsing (always)
- Check generated reports in analyzed repos
- Verify timestamp-based logic by manually aging coverage files
- Observer logs for:
Decisions
- Read-first approach: Preserves current behavior as default path, only generates when necessary
- 24-hour staleness threshold: Balances freshness with avoiding unnecessary test runs (configurable via constant)
- Copilot-first command generation: Leverages existing SDK integration for intelligent commands, more adaptive than pattern matching
- 300s test timeout: Matches
CLONE_TIMEOUTfor consistency, handles comprehensive test suites - Auto-install dependencies: Ensures tests can run even in fresh repo clones
- Graceful fallback chain:
- Try existing fresh report (preferred)
- Try generating new report if stale/missing
- Fall back to stale report if generation fails
- Report no coverage if all fail (current behavior)
- Non-blocking: Coverage generation failures don't fail entire analysis