annator-command-center / tests /scripts /analyze_coverage_gaps.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 9)
81e3673 verified
Raw
History Blame Contribute Delete
28.6 kB
#!/usr/bin/env python3
"""
Coverage Gap Analysis Script
Analyzes pytest coverage reports to identify high-impact testing opportunities.
Categorizes files by size, calculates coverage gaps, and prioritizes files for
maximum coverage improvement.
Usage:
python3 tests/scripts/analyze_coverage_gaps.py --format all --output-dir tests/coverage_reports/metrics/
python3 tests/scripts/analyze_coverage_gaps.py --format json --output-dir /path/to/output
python3 tests/scripts/analyze_coverage_gaps.py --format markdown --output-dir reports/
Output Files:
- coverage_summary.json: Module-level aggregations and file tiers
- priority_files_for_phases_12_13.json: Ranked file list for test planning
- zero_coverage_analysis.json: Files with 0% coverage >100 lines
- PHASE_11_COVERAGE_ANALYSIS_REPORT.md: Comprehensive markdown report
Requirements:
- Python 3.11+
- coverage.json from pytest-cov
Author: Phase 11 Coverage Analysis
Generated: 2026-02-15
"""
import argparse
import json
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Tuple
def load_coverage_data(coverage_path: str) -> Dict[str, Any]:
"""
Load coverage.json from pytest-cov.
Args:
coverage_path: Path to coverage.json file
Returns:
Coverage data dictionary
Raises:
FileNotFoundError: If coverage.json doesn't exist
json.JSONDecodeError: If coverage.json is invalid
"""
path = Path(coverage_path)
if not path.exists():
raise FileNotFoundError(
f"Coverage file not found: {coverage_path}\n"
f"Run tests with coverage first: pytest --cov=backend --cov-report=json"
)
with open(path, 'r') as f:
return json.load(f)
def categorize_file_by_size(lines: int) -> str:
"""
Categorize file by total lines of code.
Args:
lines: Total number of lines (statements)
Returns:
Tier category (Tier 1-5)
"""
if lines >= 500:
return "Tier 1" # Highest impact
elif lines >= 300:
return "Tier 2" # High impact
elif lines >= 200:
return "Tier 3" # Medium impact
elif lines >= 100:
return "Tier 4" # Low impact
else:
return "Tier 5" # Minimal impact
def calculate_metrics(filedata: Dict[str, Any]) -> Dict[str, float]:
"""
Calculate coverage metrics for a single file.
Args:
filedata: File coverage data from coverage.json
Returns:
Dictionary with calculated metrics
"""
total_lines = filedata['summary']['num_statements']
covered_lines = filedata['summary']['covered_lines']
coverage_pct = filedata['summary']['percent_covered']
# Coverage gap: lines that are NOT covered
coverage_gap = total_lines - covered_lines
# Potential gain: assuming 50% achievable coverage (Phase 8.6 proven target)
potential_gain = coverage_gap * 0.5
# Priority score: uncovered ratio (higher = more priority)
priority_score = coverage_gap / total_lines if total_lines > 0 else 0
return {
'total_lines': total_lines,
'covered_lines': covered_lines,
'coverage_pct': coverage_pct,
'coverage_gap': coverage_gap,
'potential_gain': potential_gain,
'priority_score': priority_score
}
def analyze_files(coverage_data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
Analyze all files in coverage report and calculate metrics.
Args:
coverage_data: Full coverage data dictionary
Returns:
List of file analysis results with metrics
"""
files_analysis = []
for filepath, filedata in coverage_data['files'].items():
metrics = calculate_metrics(filedata)
tier = categorize_file_by_size(metrics['total_lines'])
files_analysis.append({
'file': filepath,
'tier': tier,
**metrics
})
return files_analysis
def create_module_aggregation(files_analysis: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Aggregate metrics by module (core/, api/, tools/, etc.).
Args:
files_analysis: List of file analysis results
Returns:
Module-level aggregated metrics
"""
modules = {}
for file_data in files_analysis:
# Extract module from filepath (e.g., "core/workflow_engine.py" -> "core")
parts = file_data['file'].split('/')
module = parts[0] if len(parts) > 1 else 'other'
if module not in modules:
modules[module] = {
'total_lines': 0,
'covered_lines': 0,
'files': []
}
modules[module]['total_lines'] += file_data['total_lines']
modules[module]['covered_lines'] += file_data['covered_lines']
modules[module]['files'].append(file_data)
# Calculate module percentages
for module in modules.values():
module['coverage_pct'] = (
module['covered_lines'] / module['total_lines'] * 100
if module['total_lines'] > 0 else 0
)
module['coverage_gap'] = module['total_lines'] - module['covered_lines']
return modules
def get_high_priority_files(files_analysis: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Get high-priority files for testing (Tier 1-3, sorted by coverage gap).
Args:
files_analysis: List of file analysis results
Returns:
Sorted list of high-priority files
"""
# Filter to Tier 1-3 (files >= 200 lines)
high_priority = [f for f in files_analysis if f['tier'] in ['Tier 1', 'Tier 2', 'Tier 3']]
# Sort by coverage gap (largest gap first)
high_priority.sort(key=lambda x: x['coverage_gap'], reverse=True)
return high_priority
def get_zero_coverage_files(files_analysis: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Get files with 0% coverage and >100 lines.
Args:
files_analysis: List of file analysis results
Returns:
List of zero-coverage files
"""
zero_coverage = [
f for f in files_analysis
if f['coverage_pct'] == 0 and f['total_lines'] >= 100
]
# Sort by file size (largest first)
zero_coverage.sort(key=lambda x: x['total_lines'], reverse=True)
return zero_coverage
def estimate_test_complexity(file_data: Dict[str, Any]) -> str:
"""
Estimate test complexity based on file size and coverage.
Args:
file_data: File analysis data
Returns:
Complexity level (low, medium, high)
"""
lines = file_data['total_lines']
if lines < 150:
return "low" # <30 tests
elif lines < 400:
return "medium" # 30-60 tests
else:
return "high" # >60 tests
def recommend_test_type(filepath: str) -> str:
"""
Recommend test type based on file characteristics.
Args:
filepath: File path
Returns:
Recommended test type (unit, integration, property, e2e)
"""
# Property tests for stateful logic
if any(keyword in filepath for keyword in [
'workflow', 'engine', 'handler', 'coordinator', 'executor'
]):
return "property"
# Integration tests for API endpoints
if any(keyword in filepath for keyword in [
'endpoint', 'route', 'api'
]):
return "integration"
# Unit tests for isolated logic
if any(keyword in filepath for keyword in [
'service', 'util', 'helper', 'manager'
]):
return "unit"
# Default to unit tests
return "unit"
def create_coverage_summary(
files_analysis: List[Dict[str, Any]],
modules: Dict[str, Any]
) -> Dict[str, Any]:
"""
Create coverage summary JSON output.
Args:
files_analysis: List of file analysis results
modules: Module-level aggregated metrics
Returns:
Coverage summary dictionary
"""
# Calculate overall metrics
total_lines = sum(f['total_lines'] for f in files_analysis)
total_covered = sum(f['covered_lines'] for f in files_analysis)
overall_pct = (total_covered / total_lines * 100) if total_lines > 0 else 0
# Get high-priority files
high_priority = get_high_priority_files(files_analysis)
zero_coverage = get_zero_coverage_files(files_analysis)
# Organize by tier
files_by_tier = {
'Tier 1': [f for f in files_analysis if f['tier'] == 'Tier 1'],
'Tier 2': [f for f in files_analysis if f['tier'] == 'Tier 2'],
'Tier 3': [f for f in files_analysis if f['tier'] == 'Tier 3'],
'Tier 4': [f for f in files_analysis if f['tier'] == 'Tier 4'],
'Tier 5': [f for f in files_analysis if f['tier'] == 'Tier 5'],
}
return {
'generated_at': datetime.utcnow().isoformat() + 'Z',
'overall': {
'percent_covered': round(overall_pct, 2),
'covered_lines': total_covered,
'total_lines': total_lines,
'coverage_gap': total_lines - total_covered
},
'modules': {
module: {
'percent_covered': round(data['coverage_pct'], 2),
'covered_lines': data['covered_lines'],
'total_lines': data['total_lines'],
'coverage_gap': data['coverage_gap'],
'file_count': len(data['files'])
}
for module, data in modules.items()
},
'files_by_size': {
tier: {
'file_count': len(files),
'total_lines': sum(f['total_lines'] for f in files),
'covered_lines': sum(f['covered_lines'] for f in files),
'avg_coverage_pct': round(
sum(f['covered_lines'] for f in files) /
sum(f['total_lines'] for f in files) * 100
if sum(f['total_lines'] for f in files) > 0 else 0,
2
)
}
for tier, files in files_by_tier.items()
},
'high_priority_files': [
{
'file': f['file'],
'lines': f['total_lines'],
'coverage_pct': round(f['coverage_pct'], 2),
'uncovered_lines': f['coverage_gap'],
'potential_gain': round(f['potential_gain']),
'priority_score': round(f['priority_score'], 3)
}
for f in high_priority[:50] # Top 50
],
'zero_coverage_files': [
{
'file': f['file'],
'lines': f['total_lines'],
'estimated_gain_lines': round(f['total_lines'] * 0.5)
}
for f in zero_coverage
]
}
def create_priority_files_list(
high_priority: List[Dict[str, Any]],
zero_coverage: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Create prioritized file list for Phases 12-13.
Args:
high_priority: List of high-priority files
zero_coverage: List of zero-coverage files
Returns:
Priority files dictionary
"""
# Calculate overall metrics
total_lines = sum(f['total_lines'] for f in high_priority)
total_covered = sum(f['covered_lines'] for f in high_priority)
overall_pct = (total_covered / total_lines * 100) if total_lines > 0 else 0
# Split into Phase 12 (Tier 1, <20% coverage) and Phase 13 (Tier 2-3, <30%)
phase_12_files = [
f for f in high_priority
if f['tier'] == 'Tier 1' and f['coverage_pct'] < 20
][:20] # Top 20 files
phase_13_files = [
f for f in high_priority
if f not in phase_12_files and f['coverage_pct'] < 30
][:20] # Next 20 files
# Add zero-coverage files to Phase 13
phase_13_files.extend(zero_coverage[:10])
return {
'generated_at': datetime.utcnow().isoformat() + 'Z',
'current_coverage': {
'percent': round(overall_pct, 2),
'covered_lines': total_covered,
'total_lines': total_lines
},
'target_coverage': {
'percent': 80,
'required_lines': int(total_lines * 0.8),
'gap_lines': total_lines - int(total_lines * 0.8)
},
'phases': {
'12': {
'target_percent': 28,
'target_gain': 5.2,
'files': [
{
'rank': i + 1,
'file': f['file'],
'lines': f['total_lines'],
'current_percent': round(f['coverage_pct'], 2),
'uncovered_lines': f['coverage_gap'],
'estimated_tests_needed': max(30, int(f['total_lines'] / 20)),
'recommended_test_type': recommend_test_type(f['file']),
'coverage_complexity': estimate_test_complexity(f),
'tier': f['tier']
}
for i, f in enumerate(phase_12_files)
]
},
'13': {
'target_percent': 35,
'target_gain': 7.0,
'files': [
{
'rank': i + 1,
'file': f['file'],
'lines': f['total_lines'],
'current_percent': round(f['coverage_pct'], 2),
'uncovered_lines': f['coverage_gap'],
'estimated_tests_needed': max(30, int(f['total_lines'] / 20)),
'recommended_test_type': recommend_test_type(f['file']),
'coverage_complexity': estimate_test_complexity(f),
'tier': f['tier']
}
for i, f in enumerate(phase_13_files)
]
}
},
'zero_coverage_quick_wins': [
{
'file': f['file'],
'lines': f['total_lines'],
'estimated_gain_lines': round(f['total_lines'] * 0.5),
'recommended_test_type': recommend_test_type(f['file'])
}
for f in zero_coverage[:30] # Top 30
]
}
def create_zero_coverage_analysis(zero_coverage: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Create zero coverage analysis JSON.
Args:
zero_coverage: List of zero-coverage files
Returns:
Zero coverage analysis dictionary
"""
return {
'generated_at': datetime.utcnow().isoformat() + 'Z',
'total_zero_coverage_files': len(zero_coverage),
'total_lines_uncovered': sum(f['total_lines'] for f in zero_coverage),
'estimated_total_gain': sum(f['total_lines'] * 0.5 for f in zero_coverage),
'files': [
{
'file': f['file'],
'lines': f['total_lines'],
'estimated_gain_lines': round(f['total_lines'] * 0.5),
'recommended_test_type': recommend_test_type(f['file']),
'complexity': estimate_test_complexity(f)
}
for f in zero_coverage
]
}
def generate_markdown_report(
coverage_summary: Dict[str, Any],
priority_files: Dict[str, Any],
modules: Dict[str, Any]
) -> str:
"""
Generate comprehensive markdown report.
Args:
coverage_summary: Coverage summary data
priority_files: Priority files data
modules: Module aggregated data
Returns:
Markdown report content
"""
overall = coverage_summary['overall']
md = f"""# Phase 11 Coverage Analysis Report
**Generated:** {coverage_summary['generated_at']}
**Purpose:** Identify highest-impact testing opportunities for Phases 12-13
---
## Executive Summary
### Current Coverage Status
| Metric | Value |
|--------|-------|
| Overall Coverage | {overall['percent_covered']}% |
| Covered Lines | {overall['covered_lines']:,} |
| Total Lines | {overall['total_lines']:,} |
| Coverage Gap | {overall['coverage_gap']:,} lines |
| Target Coverage (80%) | {int(overall['total_lines'] * 0.8):,} lines |
| Remaining Gap | {int(overall['total_lines'] * 0.8) - overall['covered_lines']:,} lines |
### File Distribution by Size
| Tier | Size Range | File Count | Total Lines | Avg Coverage |
|------|------------|------------|-------------|--------------|
| Tier 1 | ≥500 lines | {coverage_summary['files_by_size']['Tier 1']['file_count']} | {coverage_summary['files_by_size']['Tier 1']['total_lines']:,} | {coverage_summary['files_by_size']['Tier 1']['avg_coverage_pct']}% |
| Tier 2 | 300-499 lines | {coverage_summary['files_by_size']['Tier 2']['file_count']} | {coverage_summary['files_by_size']['Tier 2']['total_lines']:,} | {coverage_summary['files_by_size']['Tier 2']['avg_coverage_pct']}% |
| Tier 3 | 200-299 lines | {coverage_summary['files_by_size']['Tier 3']['file_count']} | {coverage_summary['files_by_size']['Tier 3']['total_lines']:,} | {coverage_summary['files_by_size']['Tier 3']['avg_coverage_pct']}% |
| Tier 4 | 100-199 lines | {coverage_summary['files_by_size']['Tier 4']['file_count']} | {coverage_summary['files_by_size']['Tier 4']['total_lines']:,} | {coverage_summary['files_by_size']['Tier 4']['avg_coverage_pct']}% |
| Tier 5 | <100 lines | {coverage_summary['files_by_size']['Tier 5']['file_count']} | {coverage_summary['files_by_size']['Tier 5']['total_lines']:,} | {coverage_summary['files_by_size']['Tier 5']['avg_coverage_pct']}% |
---
## Top 20 High-Impact Files
Ranked by coverage gap (uncovered lines). **Target: 50% coverage per file** (Phase 8.6 proven sustainable).
| Rank | File | Lines | Current % | Uncovered | Priority | Tier | Test Type | Complexity |
|------|------|-------|-----------|-----------|----------|------|-----------|------------|
"""
for i, file_data in enumerate(coverage_summary['high_priority_files'][:20], 1):
filepath = file_data['file']
lines = file_data['lines']
pct = file_data['coverage_pct']
uncovered = file_data['uncovered_lines']
priority = file_data['priority_score']
# Determine tier and test type
tier = "Tier 1" if lines >= 500 else "Tier 2" if lines >= 300 else "Tier 3"
test_type = recommend_test_type(filepath)
complexity = "high" if lines >= 400 else "medium" if lines >= 150 else "low"
md += f"| {i} | {filepath} | {lines} | {pct}% | {uncovered} | {priority:.3f} | {tier} | {test_type} | {complexity} |\n"
md += f"""
---
## Zero Coverage Quick Wins
Files with **0% coverage** and >100 lines. Testing these to 50% coverage provides fast wins.
**Total zero-coverage files >100 lines:** {len(coverage_summary['zero_coverage_files'])}
| File | Lines | Est. Gain (50%) | Test Type | Complexity |
|------|-------|-----------------|-----------|------------|
"""
for file_data in coverage_summary['zero_coverage_files'][:25]:
filepath = file_data['file']
lines = file_data['lines']
gain = file_data['estimated_gain_lines']
test_type = recommend_test_type(filepath)
complexity = "high" if lines >= 400 else "medium" if lines >= 150 else "low"
md += f"| {filepath} | {lines} | {gain} | {test_type} | {complexity} |\n"
md += f"""
---
## Module Breakdown
### Core Module
**Files:** {len(modules.get('core', {}).get('files', []))}
| Metric | Value |
|--------|-------|
| Coverage | {modules.get('core', {}).get('coverage_pct', 0):.1f}% |
| Covered Lines | {modules.get('core', {}).get('covered_lines', 0):,} |
| Total Lines | {modules.get('core', {}).get('total_lines', 0):,} |
| Coverage Gap | {modules.get('core', {}).get('coverage_gap', 0):,} |
**Top 5 Gaps in core/:**
"""
core_files = sorted(
modules.get('core', {}).get('files', []),
key=lambda x: x['coverage_gap'],
reverse=True
)[:5]
for i, f in enumerate(core_files, 1):
md += f"{i}. {f['file']}: {f['coverage_gap']} lines uncovered ({f['coverage_pct']:.1f}%)\n"
md += f"""
### API Module
**Files:** {len(modules.get('api', {}).get('files', []))}
| Metric | Value |
|--------|-------|
| Coverage | {modules.get('api', {}).get('coverage_pct', 0):.1f}% |
| Covered Lines | {modules.get('api', {}).get('covered_lines', 0):,} |
| Total Lines | {modules.get('api', {}).get('total_lines', 0):,} |
| Coverage Gap | {modules.get('api', {}).get('coverage_gap', 0):,} |
**Top 5 Gaps in api/:**
"""
api_files = sorted(
modules.get('api', {}).get('files', []),
key=lambda x: x['coverage_gap'],
reverse=True
)[:5]
for i, f in enumerate(api_files, 1):
md += f"{i}. {f['file']}: {f['coverage_gap']} lines uncovered ({f['coverage_pct']:.1f}%)\n"
md += f"""
### Tools Module
**Files:** {len(modules.get('tools', {}).get('files', []))}
| Metric | Value |
|--------|-------|
| Coverage | {modules.get('tools', {}).get('coverage_pct', 0):.1f}% |
| Covered Lines | {modules.get('tools', {}).get('covered_lines', 0):,} |
| Total Lines | {modules.get('tools', {}).get('total_lines', 0):,} |
| Coverage Gap | {modules.get('tools', {}).get('coverage_gap', 0):,} |
**Top 5 Gaps in tools/:**
"""
tools_files = sorted(
modules.get('tools', {}).get('files', []),
key=lambda x: x['coverage_gap'],
reverse=True
)[:5]
for i, f in enumerate(tools_files, 1):
md += f"{i}. {f['file']}: {f['coverage_gap']} lines uncovered ({f['coverage_pct']:.1f}%)\n"
md += f"""
---
## Phase 12-13 Testing Strategy
### Strategy Overview
Based on Phase 8.6 validation (3.38x velocity acceleration), this analysis prioritizes **high-impact files** for maximum coverage gain.
**Key Principles:**
- Target 50% average coverage per file (proven sustainable)
- Prioritize largest files first (Tier 1 > Tier 2 > Tier 3)
- Use appropriate test types (property, integration, unit)
- 3-4 files per plan for focused execution
### Phase 12: Tier 1 Files
**Target:** {priority_files['phases']['12']['target_percent']}% coverage (+{priority_files['phases']['12']['target_gain']} percentage points)
**Focus:** Files ≥500 lines with <20% coverage
**Estimated Plans:** 4-5 plans
**Estimated Velocity:** +1.3-1.5% per plan
**Files:** {len(priority_files['phases']['12']['files'])} files
### Phase 13: Tier 2-3 + Zero Coverage
**Target:** {priority_files['phases']['13']['target_percent']}% coverage (+{priority_files['phases']['13']['target_gain']} percentage points)
**Focus:** Files 300-500 lines, <30% coverage + zero-coverage quick wins
**Estimated Plans:** 5-6 plans
**Estimated Velocity:** +1.2-1.4% per plan
**Files:** {len(priority_files['phases']['13']['files'])} files
### Test Type Recommendations
| Test Type | When to Use | Examples |
|-----------|-------------|----------|
| **Property Tests** | Stateful logic, workflows, handlers | workflow_engine, byok_handler, coordinators |
| **Integration Tests** | API endpoints, routes | atom_agent_endpoints, workflow_endpoints, API routes |
| **Unit Tests** | Isolated logic, services, utilities | lancedb_handler, auto_document_ingestion, services |
### Execution Plan
**Files Per Plan:** 3-4 high-impact files
**Target Coverage Per File:** 50% (Phase 8.6 proven sustainable)
**Estimated Velocity:** +1.5% per plan (maintaining Phase 8.6 acceleration)
**Estimated Duration:** 4-6 hours per plan
**Total Estimated Impact:**
- Phase 12: +{priority_files['phases']['12']['target_gain']} percentage points
- Phase 13: +{priority_files['phases']['13']['target_gain']} percentage points
- Combined: +{priority_files['phases']['12']['target_gain'] + priority_files['phases']['13']['target_gain']} percentage points
---
## Recommendations
1. **Start with Tier 1 files** (Phase 12) - Highest ROI with 3.38x velocity acceleration
2. **Target 50% coverage per file** - Proven sustainable from Phase 8.6
3. **Use appropriate test types** - Property tests for stateful logic, integration for APIs
4. **3-4 files per plan** - Focused execution without overwhelming complexity
5. **Leverage existing test infrastructure** - Property test patterns, AsyncMock, FastAPI TestClient
---
## Appendix: Data Sources
- **Coverage Data:** `tests/coverage_reports/metrics/coverage.json`
- **Analysis Script:** `tests/scripts/analyze_coverage_gaps.py`
- **Priority Files:** `tests/coverage_reports/priority_files_for_phases_12_13.json`
*Generated by Phase 11 Coverage Analysis - {datetime.utcnow().strftime('%Y-%m-%d')}*
"""
return md
def main():
"""Main entry point for coverage analysis script."""
parser = argparse.ArgumentParser(
description='Analyze coverage gaps and prioritize testing opportunities',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument(
'--coverage-file',
default='backend/tests/coverage_reports/metrics/coverage.json',
help='Path to coverage.json (default: backend/tests/coverage_reports/metrics/coverage.json)'
)
parser.add_argument(
'--format',
choices=['json', 'markdown', 'all'],
default='all',
help='Output format (default: all)'
)
parser.add_argument(
'--output-dir',
default='backend/tests/coverage_reports/metrics/',
help='Output directory for generated files (default: backend/tests/coverage_reports/metrics/)'
)
args = parser.parse_args()
try:
# Load coverage data
print(f"Loading coverage data from {args.coverage_file}...")
coverage_data = load_coverage_data(args.coverage_file)
print(f"Found {len(coverage_data['files'])} files")
# Analyze files
print("Analyzing coverage gaps...")
files_analysis = analyze_files(coverage_data)
modules = create_module_aggregation(files_analysis)
high_priority = get_high_priority_files(files_analysis)
zero_coverage = get_zero_coverage_files(files_analysis)
# Create output directory
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# Generate outputs based on format
if args.format in ['json', 'all']:
print("Generating JSON files...")
# Coverage summary
coverage_summary = create_coverage_summary(files_analysis, modules)
summary_path = output_dir / 'coverage_summary.json'
with open(summary_path, 'w') as f:
json.dump(coverage_summary, f, indent=2)
print(f" Created: {summary_path}")
# Priority files for Phases 12-13
priority_files = create_priority_files_list(high_priority, zero_coverage)
priority_path = output_dir / 'priority_files_for_phases_12_13.json'
with open(priority_path, 'w') as f:
json.dump(priority_files, f, indent=2)
print(f" Created: {priority_path}")
# Zero coverage analysis
zero_analysis = create_zero_coverage_analysis(zero_coverage)
zero_path = output_dir / 'zero_coverage_analysis.json'
with open(zero_path, 'w') as f:
json.dump(zero_analysis, f, indent=2)
print(f" Created: {zero_path}")
if args.format in ['markdown', 'all']:
print("Generating markdown report...")
# Generate markdown report
markdown_report = generate_markdown_report(
coverage_summary if args.format in ['json', 'all'] else create_coverage_summary(files_analysis, modules),
priority_files if args.format in ['json', 'all'] else create_priority_files_list(high_priority, zero_coverage),
modules
)
report_path = output_dir / 'PHASE_11_COVERAGE_ANALYSIS_REPORT.md'
with open(report_path, 'w') as f:
f.write(markdown_report)
print(f" Created: {report_path}")
print("\nAnalysis complete!")
print(f"High-priority files identified: {len(high_priority)}")
print(f"Zero-coverage files (>100 lines): {len(zero_coverage)}")
return 0
except FileNotFoundError as e:
print(f"Error: {e}", file=sys.stderr)
return 1
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in coverage file: {e}", file=sys.stderr)
return 1
except Exception as e:
print(f"Unexpected error: {e}", file=sys.stderr)
import traceback
traceback.print_exc()
return 1
if __name__ == '__main__':
sys.exit(main())