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
|
#! /usr/bin/python3
from optparse import OptionParser
import re
import sys
from xml.etree import ElementTree as ET
class CoverageTreeBuilder(ET.TreeBuilder):
def doctype(self, name, pubid, system):
self._doctype = (name, pubid, system)
class Coverage:
def __init__(self):
self._tree = None
self._builder = None
def _merge_children(self, left, right):
assert left.tag == right.tag
if left.tag == "coverage":
for right_child in right:
left_child = left.find(right_child.tag)
if left_child is not None:
self._merge_children(left_child, right_child)
else:
left.append(right_child)
# Re-compute rates from scratch.
lnum, lhits = 0, 0
bnum, bhits = 0, 0
condition_re = re.compile(r"\(([0-9]+)/([0-9]+)\)")
for package in left.find("packages").findall("package"):
for cclass in package.find("classes").findall("class"):
for line in cclass.find("lines").findall("line"):
lnum += 1
if line.get("hits", "0") != "0":
lhits += 1
if line.get("branch", "false") == "true":
match = condition_re.search(
line.get("condition-coverage"))
if match is not None:
bhits += int(match.group(1))
bnum += int(match.group(2))
left.set("branch-rate", "%.4g" % (float(bhits) / (bnum or 1.0)))
left.set("line-rate", "%.4g" % (float(lhits) / (lnum or 1.0)))
elif left.tag == "sources":
pass # just ignore this for now
elif left.tag == "packages":
left_names = []
for left_child in left:
assert left_child.tag == "package"
left_names.append(left_child.get("name"))
for right_child in right:
assert right_child.tag == "package"
assert right_child.get("name") not in left_names
left.append(right_child)
def merge(self, arg):
builder = CoverageTreeBuilder()
tree = ET.parse(arg, parser=ET.XMLParser(target=builder))
if self._tree is None:
self._tree = tree
self._builder = builder
else:
self._merge_children(self._tree.getroot(), tree.getroot())
def write(self, outfilename):
if outfilename is None:
outfile = sys.stdout
else:
outfile = open(outfilename, "w")
outfile.write('<?xml version="1.0" ?>\n')
doctype = self._builder._doctype
outfile.write("<!DOCTYPE %s%s%s>\n" % (
doctype[0],
"" if doctype[1] is None else "\n PUBLIC '%s'" % doctype[1],
"" if doctype[2] is None else "\n SYSTEM '%s'" % doctype[2]))
try:
self._tree.write(outfile, encoding="unicode")
finally:
outfile.close()
def main():
parser = OptionParser(usage="%prog FILE [...]")
parser.add_option(
"-o", "--output", help="output file name (default: stdout)")
options, args = parser.parse_args()
if not args:
parser.error("need at least one input file")
coverage = Coverage()
for arg in args:
coverage.merge(arg)
coverage.write(options.output)
if __name__ == "__main__":
main()
|