This repository was archived by the owner on Apr 23, 2025. It is now read-only.
forked from raizamartin/gemini-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfind_low_coverage.py
More file actions
executable file
·174 lines (137 loc) · 5.72 KB
/
find_low_coverage.py
File metadata and controls
executable file
·174 lines (137 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env python3
"""
Script to analyze coverage data and identify modules with low coverage.
"""
import os
import sys
import xml.etree.ElementTree as ET
# Set the minimum acceptable coverage percentage
MIN_COVERAGE = 60.0
# Check for rich library and provide fallback if not available
try:
from rich import box
from rich.console import Console
from rich.table import Table
RICH_AVAILABLE = True
except ImportError:
RICH_AVAILABLE = False
print("Note: Install 'rich' package for better formatted output: pip install rich")
def parse_coverage_xml(file_path="coverage.xml"):
"""Parse the coverage XML file and extract coverage data."""
try:
tree = ET.parse(file_path)
root = tree.getroot()
return root
except FileNotFoundError:
print(f"Error: Coverage file '{file_path}' not found. Run coverage first.")
sys.exit(1)
except Exception as e:
print(f"Error parsing coverage XML: {e}")
sys.exit(1)
def calculate_module_coverage(root):
"""Calculate coverage percentage for each module."""
modules = []
# Process packages and classes
for package in root.findall(".//package"):
package_name = package.attrib.get("name", "")
for class_elem in package.findall(".//class"):
filename = class_elem.attrib.get("filename", "")
line_rate = float(class_elem.attrib.get("line-rate", 0)) * 100
# Count lines covered/valid
lines = class_elem.find("lines")
if lines is not None:
line_count = len(lines.findall("line"))
covered_count = len([line for line in lines.findall("line") if int(line.attrib.get("hits", 0)) > 0])
else:
line_count = 0
covered_count = 0
modules.append(
{
"package": package_name,
"filename": filename,
"coverage": line_rate,
"line_count": line_count,
"covered_count": covered_count,
}
)
return modules
def display_coverage_table_rich(modules, min_coverage=MIN_COVERAGE):
"""Display a table of module coverage using rich library."""
console = Console()
# Create a table
table = Table(title="Module Coverage Report", box=box.ROUNDED)
table.add_column("Module", style="cyan")
table.add_column("Coverage", justify="right", style="green")
table.add_column("Lines", justify="right", style="blue")
table.add_column("Covered", justify="right", style="green")
table.add_column("Missing", justify="right", style="red")
# Sort modules by coverage (ascending)
modules.sort(key=lambda x: x["coverage"])
# Add modules to table
for module in modules:
table.add_row(
module["filename"],
f"{module['coverage']:.2f}%",
str(module["line_count"]),
str(module["covered_count"]),
str(module["line_count"] - module["covered_count"]),
style="red" if module["coverage"] < min_coverage else None,
)
console.print(table)
# Print summary
below_threshold = [m for m in modules if m["coverage"] < min_coverage]
console.print(f"\n[bold cyan]Summary:[/]")
console.print(f"Total modules: [bold]{len(modules)}[/]")
console.print(f"Modules below {min_coverage}% coverage: [bold red]{len(below_threshold)}[/]")
if below_threshold:
console.print("\n[bold red]Modules needing improvement:[/]")
for module in below_threshold:
console.print(f" • [red]{module['filename']}[/] ([yellow]{module['coverage']:.2f}%[/])")
def display_coverage_table_plain(modules, min_coverage=MIN_COVERAGE):
"""Display a table of module coverage using plain text."""
# Calculate column widths
module_width = max(len(m["filename"]) for m in modules) + 2
# Print header
print("\nModule Coverage Report")
print("=" * 80)
print(f"{'Module':<{module_width}} {'Coverage':>10} {'Lines':>8} {'Covered':>8} {'Missing':>8}")
print("-" * 80)
# Sort modules by coverage (ascending)
modules.sort(key=lambda x: x["coverage"])
# Print modules
for module in modules:
print(
f"{module['filename']:<{module_width}} {module['coverage']:>9.2f}% {module['line_count']:>8} {module['covered_count']:>8} {module['line_count'] - module['covered_count']:>8}"
)
print("=" * 80)
# Print summary
below_threshold = [m for m in modules if m["coverage"] < min_coverage]
print(f"\nSummary:")
print(f"Total modules: {len(modules)}")
print(f"Modules below {min_coverage}% coverage: {len(below_threshold)}")
if below_threshold:
print(f"\nModules needing improvement:")
for module in below_threshold:
print(f" • {module['filename']} ({module['coverage']:.2f}%)")
def main():
"""Main function to analyze coverage data."""
# Check if coverage.xml exists
if not os.path.exists("coverage.xml"):
print("Error: coverage.xml not found. Run coverage tests first.")
print("Run: ./run_coverage.sh")
sys.exit(1)
root = parse_coverage_xml()
overall_coverage = float(root.attrib.get("line-rate", 0)) * 100
if RICH_AVAILABLE:
console = Console()
console.print(
f"\n[bold cyan]Overall Coverage:[/] [{'green' if overall_coverage >= MIN_COVERAGE else 'red'}]{overall_coverage:.2f}%[/]"
)
modules = calculate_module_coverage(root)
display_coverage_table_rich(modules)
else:
print(f"\nOverall Coverage: {overall_coverage:.2f}%")
modules = calculate_module_coverage(root)
display_coverage_table_plain(modules)
if __name__ == "__main__":
main()