File: check_coverage.py

package info (click to toggle)
duckdb 1.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 299,196 kB
  • sloc: cpp: 865,414; ansic: 57,292; python: 18,871; sql: 12,663; lisp: 11,751; yacc: 7,412; lex: 1,682; sh: 747; makefile: 558
file content (129 lines) | stat: -rw-r--r-- 4,154 bytes parent folder | download | duplicates (3)
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
import argparse
import os
import math
import re

parser = argparse.ArgumentParser(description='Check code coverage results')

parser.add_argument(
    '--uncovered_files',
    action='store',
    help='Set of files that are not 100% covered',
    default=os.path.join(".github", "config", "uncovered_files.csv"),
)
parser.add_argument('--directory', help='Directory of generated HTML files', action='store', default='coverage_html')
parser.add_argument('--fix', help='Fill up the uncovered_files.csv with all files', action='store_true', default=False)


args = parser.parse_args()
if not os.path.exists(args.directory):
    print(f"The provided directory ({args.directory}) does not exist, please create it first")
    exit(1)

covered_regex = (
    r'<a name="(\d+)">[ \t\n]*<span class="lineNum">[ \t\n0-9]+</span><span class="{COVERED_CLASS}">[ \t\n0-9]+:([^<]+)'
)


def get_original_path(path):
    return (
        path.replace('.gcov.html', '')
        .replace(os.getcwd(), '')
        .replace('coverage_html' + os.path.sep, '')
        .replace('home/runner/work/duckdb/duckdb/', '')
    )


def cleanup_line(line):
    return line.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"')


partial_coverage_dict = {}
with open(args.uncovered_files, 'r') as f:
    for line in f.readlines():
        splits = line.split('\t')
        partial_coverage_dict[splits[0]] = int(splits[1].strip())

if args.fix:
    uncovered_file = open(args.uncovered_files, 'w+')

DASH_COUNT = 80
total_difference = 0
allowed_difference = 0


def check_file(path, partial_coverage_dict):
    global any_failed
    global total_difference
    if not '.cpp' in path and not '.hpp' in path:
        # files are named [path].[ch]pp
        return
    if not '.html' in path:
        return
    with open(path, 'r') as f:
        text = f.read()
    original_path = get_original_path(path)
    uncovered_lines = re.findall(covered_regex.replace('{COVERED_CLASS}', 'lineNoCov'), text)
    covered_lines = re.findall(covered_regex.replace('{COVERED_CLASS}', 'lineCov'), text)

    total_lines = len(uncovered_lines) + len(covered_lines)
    if total_lines == 0:
        # no lines to cover - skip
        return

    coverage_percentage = round(len(covered_lines) / (total_lines) * 100, 2)
    expected_uncovered_lines = 0
    if original_path in partial_coverage_dict:
        expected_uncovered_lines = partial_coverage_dict[original_path]
    if args.fix:
        if expected_uncovered_lines == 0 and len(uncovered_lines) == 0:
            return
        expected_uncovered = max(expected_uncovered_lines, len(uncovered_lines) + 1)
        uncovered_file.write(f'{original_path}\t{expected_uncovered}\n')
        return

    if len(uncovered_lines) > expected_uncovered_lines:
        total_difference += len(uncovered_lines) - expected_uncovered_lines

        print("-" * DASH_COUNT)
        print(f"Coverage failure in file {original_path}")
        print("-" * DASH_COUNT)
        print(f"Coverage percentage: {coverage_percentage}%")
        print(f"Uncovered lines: {len(uncovered_lines)}")
        print(f"Covered lines: {len(covered_lines)}")
        print("-" * DASH_COUNT)
        print(f"Expected uncovered lines: {expected_uncovered_lines}")
        print("-" * DASH_COUNT)
        print("Uncovered lines")
        print("-" * DASH_COUNT)
        for e in uncovered_lines:
            print(e[0] + ' ' * 8 + cleanup_line(e[1]))


def scan_directory(path):
    file_list = []
    if os.path.isfile(path):
        file_list.append(path)
    else:
        files = os.listdir(path)
        for file in files:
            file_list += scan_directory(os.path.join(path, file))
    return file_list


files = scan_directory(args.directory)
files.sort()

for file in files:
    check_file(file, partial_coverage_dict)

if args.fix:
    uncovered_file.close()

if total_difference > allowed_difference:
    exit(1)
elif total_difference > 0:
    print("-" * DASH_COUNT)
    print("SUCCESS-ish")
    print("-" * DASH_COUNT)
    print(f"{total_difference} lines were uncovered but this falls within the margin of {allowed_difference}")