-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathcheck_coverage.py
More file actions
142 lines (103 loc) · 3.56 KB
/
check_coverage.py
File metadata and controls
142 lines (103 loc) · 3.56 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
"""
Usage
python3 check_coverage.py <path to spec html> <tested ids file>
"""
import sys
from html.parser import HTMLParser
def print_usage():
print("python3 check_coverage.py <path to spec html> <path to test html>")
class ImplementedTestFinder(HTMLParser):
"""Finds all implemented tests in the client test HTML."""
def __init__(self):
super().__init__()
self.conformance_ids = set()
def handle_starttag(self, tag, attrs):
if tag != "div":
return
attr_map = {a[0]: a[1] for a in attrs}
if "id" not in attr_map or "class" not in attr_map:
return
if "testCase" not in attr_map["class"]:
return
# In the test HTML all tests are prefixed with "client-" remove it
test_id = attr_map["id"].removeprefix("client-")
self.conformance_ids.add(test_id)
def handle_endtag(self, tag):
pass
def handle_data(self, data):
pass
def error(self, message):
pass
class ConformanceStatementFinder(HTMLParser):
"""Finds all conformance statements tagged in the spec."""
def __init__(self):
super().__init__()
self.algorithm_conformance_ids = set()
self.conformance_ids = set()
def handle_starttag(self, tag, attrs):
if tag != "span" and tag != "h3" and tag != "h4" and tag != "h5":
return
attr_map = {a[0]: a[1] for a in attrs}
if "id" not in attr_map or "class" not in attr_map:
return
if "conform" not in attr_map["class"]:
return
if "client" not in attr_map["class"]:
return
if"data-algorithm" in attr_map:
self.algorithm_conformance_ids.add(attr_map["id"])
else:
self.conformance_ids.add(attr_map["id"])
def handle_endtag(self, tag):
pass
def handle_data(self, data):
pass
def error(self, message):
pass
def run(spec_html_path, test_html_path):
"""Report which conformance statements are not being tested."""
with open(spec_html_path, "r", encoding='utf-8') as html_file:
spec_html_text = html_file.read()
with open(test_html_path, "r", encoding='utf-8') as html_file:
test_html_text = html_file.read()
finder = ConformanceStatementFinder()
finder.feed(spec_html_text)
spec_ids = finder.conformance_ids
spec_algorithm_ids = finder.algorithm_conformance_ids
finder = ImplementedTestFinder()
finder.feed(test_html_text)
tested_ids = finder.conformance_ids
algorithm_coverage = {id: 0 for id in spec_algorithm_ids}
normalized_tested_ids = set()
for id in tested_ids:
prefix = id.split('_')[0]
normalized_tested_ids.add(prefix)
if prefix not in spec_algorithm_ids:
continue
algorithm_coverage[prefix] += 1
tested_ids = normalized_tested_ids
# Section 1 tests that are missing coverage
untested_ids = (spec_ids | spec_algorithm_ids) - tested_ids
if len(untested_ids) > 0:
print("# Conformance statements in Spec that are not tested:")
for i in untested_ids:
print(f"UNTESTED {i}")
tested_spec_ids = (spec_ids | spec_algorithm_ids) & tested_ids
if len(tested_spec_ids) > 0:
print("# Conformance statements in Spec that are tested:")
for i in tested_spec_ids:
print(f"TESTED {i}")
print("# Algorithm test coverage:")
for id, count in algorithm_coverage.items():
if count > 0:
print(f"TEST_COUNT {id} {count}")
missing_from_spec = tested_ids - spec_ids - spec_algorithm_ids
if len(missing_from_spec) > 0:
print("# Tested IDs that are not in the spec:")
for i in missing_from_spec:
print(f"NOT_IN_SPEC {i}")
if __name__ == '__main__':
if len(sys.argv) != 3:
print_usage()
sys.exit()
run(sys.argv[1], sys.argv[2])