DebugClients/Python3/coverage/xmlreport.py

branch
Py2 comp.
changeset 3495
fac17a82b431
parent 29
391dc0bc4ae5
child 4489
d0d6e4ad31bd
equal deleted inserted replaced
3485:f1cbc18f88b2 3495:fac17a82b431
2 2
3 import os, sys, time 3 import os, sys, time
4 import xml.dom.minidom 4 import xml.dom.minidom
5 5
6 from . import __url__, __version__ 6 from . import __url__, __version__
7 from .backward import sorted # pylint: disable-msg=W0622 7 from .backward import sorted, rpartition # pylint: disable=W0622
8 from .report import Reporter 8 from .report import Reporter
9 9
10 def rate(hit, num): 10 def rate(hit, num):
11 """Return the fraction of `hit`/`num`.""" 11 """Return the fraction of `hit`/`num`, as a string."""
12 return hit / (num or 1.0) 12 return "%.4g" % (float(hit) / (num or 1.0))
13 13
14 14
15 class XmlReporter(Reporter): 15 class XmlReporter(Reporter):
16 """A reporter for writing Cobertura-style XML coverage results.""" 16 """A reporter for writing Cobertura-style XML coverage results."""
17 17
18 def __init__(self, coverage, ignore_errors=False): 18 def __init__(self, coverage, config):
19 super(XmlReporter, self).__init__(coverage, ignore_errors) 19 super(XmlReporter, self).__init__(coverage, config)
20 20
21 self.packages = None 21 self.packages = None
22 self.xml_out = None 22 self.xml_out = None
23 self.arcs = coverage.data.has_arcs() 23 self.arcs = coverage.data.has_arcs()
24 24
25 def report(self, morfs, omit_prefixes=None, outfile=None): 25 def report(self, morfs, outfile=None):
26 """Generate a Cobertura-compatible XML report for `morfs`. 26 """Generate a Cobertura-compatible XML report for `morfs`.
27 27
28 `morfs` is a list of modules or filenames. `omit_prefixes` is a list 28 `morfs` is a list of modules or filenames.
29 of strings, prefixes of modules to omit from the report. 29
30 `outfile` is a file object to write the XML to.
30 31
31 """ 32 """
32 # Initial setup. 33 # Initial setup.
33 outfile = outfile or sys.stdout 34 outfile = outfile or sys.stdout
34 35
50 xpackages = self.xml_out.createElement("packages") 51 xpackages = self.xml_out.createElement("packages")
51 xcoverage.appendChild(xpackages) 52 xcoverage.appendChild(xpackages)
52 53
53 # Call xml_file for each file in the data. 54 # Call xml_file for each file in the data.
54 self.packages = {} 55 self.packages = {}
55 self.report_files(self.xml_file, morfs, omit_prefixes=omit_prefixes) 56 self.report_files(self.xml_file, morfs)
56 57
57 lnum_tot, lhits_tot = 0, 0 58 lnum_tot, lhits_tot = 0, 0
58 bnum_tot, bhits_tot = 0, 0 59 bnum_tot, bhits_tot = 0, 0
59 60
60 # Populate the XML DOM with the package info. 61 # Populate the XML DOM with the package info.
61 for pkg_name, pkg_data in self.packages.items(): 62 for pkg_name in sorted(self.packages.keys()):
63 pkg_data = self.packages[pkg_name]
62 class_elts, lhits, lnum, bhits, bnum = pkg_data 64 class_elts, lhits, lnum, bhits, bnum = pkg_data
63 xpackage = self.xml_out.createElement("package") 65 xpackage = self.xml_out.createElement("package")
64 xpackages.appendChild(xpackage) 66 xpackages.appendChild(xpackage)
65 xclasses = self.xml_out.createElement("classes") 67 xclasses = self.xml_out.createElement("classes")
66 xpackage.appendChild(xclasses) 68 xpackage.appendChild(xclasses)
67 for className in sorted(class_elts.keys()): 69 for class_name in sorted(class_elts.keys()):
68 xclasses.appendChild(class_elts[className]) 70 xclasses.appendChild(class_elts[class_name])
69 xpackage.setAttribute("name", pkg_name.replace(os.sep, '.')) 71 xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
70 xpackage.setAttribute("line-rate", str(rate(lhits, lnum))) 72 xpackage.setAttribute("line-rate", rate(lhits, lnum))
71 xpackage.setAttribute("branch-rate", str(rate(bhits, bnum))) 73 xpackage.setAttribute("branch-rate", rate(bhits, bnum))
72 xpackage.setAttribute("complexity", "0.0") 74 xpackage.setAttribute("complexity", "0")
73 75
74 lnum_tot += lnum 76 lnum_tot += lnum
75 lhits_tot += lhits 77 lhits_tot += lhits
76 bnum_tot += bnum 78 bnum_tot += bnum
77 bhits_tot += bhits 79 bhits_tot += bhits
78 80
79 xcoverage.setAttribute("line-rate", str(rate(lhits_tot, lnum_tot))) 81 xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot))
80 xcoverage.setAttribute("branch-rate", str(rate(bhits_tot, bnum_tot))) 82 xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot))
81 83
82 # Use the DOM to write the output file. 84 # Use the DOM to write the output file.
83 outfile.write(self.xml_out.toprettyxml()) 85 outfile.write(self.xml_out.toprettyxml())
86
87 # Return the total percentage.
88 denom = lnum_tot + bnum_tot
89 if denom == 0:
90 pct = 0.0
91 else:
92 pct = 100.0 * (lhits_tot + bhits_tot) / denom
93 return pct
84 94
85 def xml_file(self, cu, analysis): 95 def xml_file(self, cu, analysis):
86 """Add to the XML report for a single file.""" 96 """Add to the XML report for a single file."""
87 97
88 # Create the 'lines' and 'package' XML elements, which 98 # Create the 'lines' and 'package' XML elements, which
89 # are populated later. Note that a package == a directory. 99 # are populated later. Note that a package == a directory.
90 dirname, fname = os.path.split(cu.name) 100 package_name = rpartition(cu.name, ".")[0]
91 dirname = dirname or '.' 101 className = cu.name
92 package = self.packages.setdefault(dirname, [ {}, 0, 0, 0, 0 ]) 102
103 package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
93 104
94 xclass = self.xml_out.createElement("class") 105 xclass = self.xml_out.createElement("class")
95 106
96 xclass.appendChild(self.xml_out.createElement("methods")) 107 xclass.appendChild(self.xml_out.createElement("methods"))
97 108
98 xlines = self.xml_out.createElement("lines") 109 xlines = self.xml_out.createElement("lines")
99 xclass.appendChild(xlines) 110 xclass.appendChild(xlines)
100 className = fname.replace('.', '_') 111
101 xclass.setAttribute("name", className) 112 xclass.setAttribute("name", className)
102 ext = os.path.splitext(cu.filename)[1] 113 filename = cu.file_locator.relative_filename(cu.filename)
103 xclass.setAttribute("filename", cu.name + ext) 114 xclass.setAttribute("filename", filename.replace("\\", "/"))
104 xclass.setAttribute("complexity", "0.0") 115 xclass.setAttribute("complexity", "0")
105 116
106 branch_lines = analysis.branch_lines() 117 branch_stats = analysis.branch_stats()
107 118
108 # For each statement, create an XML 'line' element. 119 # For each statement, create an XML 'line' element.
109 for line in analysis.statements: 120 for line in sorted(analysis.statements):
110 xline = self.xml_out.createElement("line") 121 xline = self.xml_out.createElement("line")
111 xline.setAttribute("number", str(line)) 122 xline.setAttribute("number", str(line))
112 123
113 # Q: can we get info about the number of times a statement is 124 # Q: can we get info about the number of times a statement is
114 # executed? If so, that should be recorded here. 125 # executed? If so, that should be recorded here.
115 xline.setAttribute("hits", str(int(not line in analysis.missing))) 126 xline.setAttribute("hits", str(int(line not in analysis.missing)))
116 127
117 if self.arcs: 128 if self.arcs:
118 if line in branch_lines: 129 if line in branch_stats:
130 total, taken = branch_stats[line]
119 xline.setAttribute("branch", "true") 131 xline.setAttribute("branch", "true")
132 xline.setAttribute("condition-coverage",
133 "%d%% (%d/%d)" % (100*taken/total, taken, total)
134 )
120 xlines.appendChild(xline) 135 xlines.appendChild(xline)
121 136
122 class_lines = 1.0 * len(analysis.statements) 137 class_lines = len(analysis.statements)
123 class_hits = class_lines - len(analysis.missing) 138 class_hits = class_lines - len(analysis.missing)
124 139
125 if self.arcs: 140 if self.arcs:
126 # We assume here that every branch line has 2 exits, which is 141 class_branches = sum([t for t,k in branch_stats.values()])
127 # usually true. In theory, though, we could have a branch line 142 missing_branches = sum([t-k for t,k in branch_stats.values()])
128 # with more exits.. 143 class_br_hits = class_branches - missing_branches
129 class_branches = 2.0 * len(branch_lines)
130 missed_branch_targets = analysis.missing_branch_arcs().values()
131 missing_branches = sum([len(b) for b in missed_branch_targets])
132 class_branch_hits = class_branches - missing_branches
133 else: 144 else:
134 class_branches = 0.0 145 class_branches = 0.0
135 class_branch_hits = 0.0 146 class_br_hits = 0.0
136 147
137 # Finalize the statistics that are collected in the XML DOM. 148 # Finalize the statistics that are collected in the XML DOM.
138 line_rate = rate(class_hits, class_lines) 149 xclass.setAttribute("line-rate", rate(class_hits, class_lines))
139 branch_rate = rate(class_branch_hits, class_branches) 150 xclass.setAttribute("branch-rate", rate(class_br_hits, class_branches))
140 xclass.setAttribute("line-rate", str(line_rate))
141 xclass.setAttribute("branch-rate", str(branch_rate))
142 package[0][className] = xclass 151 package[0][className] = xclass
143 package[1] += class_hits 152 package[1] += class_hits
144 package[2] += class_lines 153 package[2] += class_lines
145 package[3] += class_branch_hits 154 package[3] += class_br_hits
146 package[4] += class_branches 155 package[4] += class_branches

eric ide

mercurial