import os LCOV_FILE = "coverage.out" OUTPUT_DIR = "coverage" os.makedirs(OUTPUT_DIR, exist_ok=True) def parse_lcov(content): """Parses LCOV data from a single string.""" files = {} current_file = None for line in content.splitlines(): if line.startswith("SF:"): current_file = line[3:].strip() files[current_file] = {"coverage": {}, "functions": []} elif line.startswith("DA:") and current_file: parts = line[3:].split(",") line_num = int(parts[0]) execution_count = int(parts[1]) files[current_file]["coverage"][line_num] = execution_count elif line.startswith("FN:") and current_file: parts = line[3:].split(",") line_num = int(parts[0]) function_name = parts[1].strip() files[current_file]["functions"].append({"name": function_name, "line": line_num, "hits": 0}) elif line.startswith("FNDA:") and current_file: parts = line[5:].split(",") hit_count = int(parts[0]) function_name = parts[1].strip() for func in files[current_file]["functions"]: if func["name"] == function_name: func["hits"] = hit_count break return files def read_source_file(filepath): """Reads source file content if available.""" if not os.path.exists(filepath): return [] with open(filepath, "r", encoding="utf-8") as f: return f.readlines() def generate_file_html(filepath, coverage_data, functions_data): """Generates an HTML file for a specific source file.""" filename = os.path.basename(filepath) source_code = read_source_file(filepath) html_path = os.path.join(OUTPUT_DIR, f"{filename}.html") total_hits = sum(func["hits"] for func in functions_data) max_hits = max((func["hits"] for func in functions_data), default=0) total_functions = len(functions_data) covered_functions = sum(1 for func in functions_data if func["hits"] > 0) function_coverage_percent = (covered_functions / total_functions * 100) if total_functions > 0 else 0 lines = [ "", '', '', "", f'

{filename} Coverage

', f'

Total Execution Hits: {total_hits}

', f'

Function Coverage Overview: {function_coverage_percent:.2f}%

', '', '
', '

Function Coverage:

' ] longest_name = max((len(func["name"]) for func in functions_data), default=0) for func in functions_data: hit_color = "red" if func["hits"] == 0 else "green" lines.append( f'' f'' ) lines.append('
FunctionHits
{func["name"]}{func["hits"]}
') # Close collapsible div lines.append('

Source Code:

') for i, line in enumerate(source_code, start=1): stripped_line = line.strip() class_name = "text-muted" if not stripped_line or stripped_line.startswith("end") or stripped_line.startswith("--"): count_display = "N/A" lines.append(f'>') else: count = coverage_data.get(i, 0) class_name = "zero-hits" if count == 0 else "low-hits" if count < max_hits * 0.3 else "high-hits" count_display = f'{count}' marked_text = f'{line.strip()}' lines.append(f'') lines.append("
LineHitsCode
{i}{count_display}{line.strip()}
{i}{count_display}{marked_text}
") with open(html_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) def generate_index(files): """Generates an index.html summarizing the coverage.""" index_html = [ "", '', "", '

Coverage Report

', '' ] for filepath, data in files.items(): filename = os.path.basename(filepath) total_hits = sum(func["hits"] for func in data["functions"]) total_functions = len(data["functions"]) index_html.append(f'') index_html.append("
FileTotal HitsFunctions
{filename}{total_hits}{total_functions}
") with open(os.path.join(OUTPUT_DIR, "index.html"), "w", encoding="utf-8") as f: f.write("\n".join(index_html)) with open(LCOV_FILE, "r", encoding="utf-8") as f: lcov_content = f.read() files_data = parse_lcov(lcov_content) for file_path, data in files_data.items(): generate_file_html(file_path, data["coverage"], data["functions"]) generate_index(files_data) print(f"Coverage report generated in {OUTPUT_DIR}/index.html")