2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
3 |
3 |
4 """XML reporting for coverage.py""" |
4 """XML reporting for coverage.py""" |
5 |
5 |
6 import os |
6 import os |
|
7 import os.path |
7 import sys |
8 import sys |
8 import time |
9 import time |
9 import xml.dom.minidom |
10 import xml.dom.minidom |
10 |
11 |
|
12 from coverage import env |
11 from coverage import __url__, __version__, files |
13 from coverage import __url__, __version__, files |
|
14 from coverage.backward import iitems |
|
15 from coverage.misc import isolate_module |
12 from coverage.report import Reporter |
16 from coverage.report import Reporter |
|
17 |
|
18 os = isolate_module(os) |
|
19 |
13 |
20 |
14 DTD_URL = ( |
21 DTD_URL = ( |
15 'https://raw.githubusercontent.com/cobertura/web/' |
22 'https://raw.githubusercontent.com/cobertura/web/' |
16 'f0366e5e2cf18f111cbd61fc34ef720a6584ba02' |
23 'f0366e5e2cf18f111cbd61fc34ef720a6584ba02' |
17 '/htdocs/xml/coverage-03.dtd' |
24 '/htdocs/xml/coverage-03.dtd' |
31 |
38 |
32 def __init__(self, coverage, config): |
39 def __init__(self, coverage, config): |
33 super(XmlReporter, self).__init__(coverage, config) |
40 super(XmlReporter, self).__init__(coverage, config) |
34 |
41 |
35 self.source_paths = set() |
42 self.source_paths = set() |
|
43 if config.source: |
|
44 for src in config.source: |
|
45 if os.path.exists(src): |
|
46 self.source_paths.add(files.canonical_filename(src)) |
36 self.packages = {} |
47 self.packages = {} |
37 self.xml_out = None |
48 self.xml_out = None |
38 self.has_arcs = coverage.data.has_arcs() |
49 self.has_arcs = coverage.data.has_arcs() |
39 |
50 |
40 def report(self, morfs, outfile=None): |
51 def report(self, morfs, outfile=None): |
79 |
90 |
80 xpackages = self.xml_out.createElement("packages") |
91 xpackages = self.xml_out.createElement("packages") |
81 xcoverage.appendChild(xpackages) |
92 xcoverage.appendChild(xpackages) |
82 |
93 |
83 # Populate the XML DOM with the package info. |
94 # Populate the XML DOM with the package info. |
84 for pkg_name in sorted(self.packages.keys()): |
95 for pkg_name, pkg_data in sorted(iitems(self.packages)): |
85 pkg_data = self.packages[pkg_name] |
|
86 class_elts, lhits, lnum, bhits, bnum = pkg_data |
96 class_elts, lhits, lnum, bhits, bnum = pkg_data |
87 xpackage = self.xml_out.createElement("package") |
97 xpackage = self.xml_out.createElement("package") |
88 xpackages.appendChild(xpackage) |
98 xpackages.appendChild(xpackage) |
89 xclasses = self.xml_out.createElement("classes") |
99 xclasses = self.xml_out.createElement("classes") |
90 xpackage.appendChild(xclasses) |
100 xpackage.appendChild(xclasses) |
91 for class_name in sorted(class_elts.keys()): |
101 for _, class_elt in sorted(iitems(class_elts)): |
92 xclasses.appendChild(class_elts[class_name]) |
102 xclasses.appendChild(class_elt) |
93 xpackage.setAttribute("name", pkg_name.replace(os.sep, '.')) |
103 xpackage.setAttribute("name", pkg_name.replace(os.sep, '.')) |
94 xpackage.setAttribute("line-rate", rate(lhits, lnum)) |
104 xpackage.setAttribute("line-rate", rate(lhits, lnum)) |
95 if self.has_arcs: |
105 if self.has_arcs: |
96 branch_rate = rate(bhits, bnum) |
106 branch_rate = rate(bhits, bnum) |
97 else: |
107 else: |
125 def xml_file(self, fr, analysis): |
138 def xml_file(self, fr, analysis): |
126 """Add to the XML report for a single file.""" |
139 """Add to the XML report for a single file.""" |
127 |
140 |
128 # Create the 'lines' and 'package' XML elements, which |
141 # Create the 'lines' and 'package' XML elements, which |
129 # are populated later. Note that a package == a directory. |
142 # are populated later. Note that a package == a directory. |
130 filename = fr.relative_filename() |
143 filename = fr.filename.replace("\\", "/") |
131 filename = filename.replace("\\", "/") |
144 for source_path in self.source_paths: |
132 dirname = os.path.dirname(filename) or "." |
145 if filename.startswith(source_path.replace("\\", "/") + "/"): |
133 parts = dirname.split("/") |
146 rel_name = filename[len(source_path)+1:] |
134 dirname = "/".join(parts[:self.config.xml_package_depth]) |
147 break |
|
148 else: |
|
149 rel_name = fr.relative_filename() |
|
150 |
|
151 dirname = os.path.dirname(rel_name) or "." |
|
152 dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth]) |
135 package_name = dirname.replace("/", ".") |
153 package_name = dirname.replace("/", ".") |
136 className = fr.relative_filename() |
154 |
137 |
155 if rel_name != fr.filename: |
138 self.source_paths.add(files.relative_directory().rstrip('/')) |
156 self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/")) |
139 package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) |
157 package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) |
140 |
158 |
141 xclass = self.xml_out.createElement("class") |
159 xclass = self.xml_out.createElement("class") |
142 |
160 |
143 xclass.appendChild(self.xml_out.createElement("methods")) |
161 xclass.appendChild(self.xml_out.createElement("methods")) |
144 |
162 |
145 xlines = self.xml_out.createElement("lines") |
163 xlines = self.xml_out.createElement("lines") |
146 xclass.appendChild(xlines) |
164 xclass.appendChild(xlines) |
147 |
165 |
148 xclass.setAttribute("name", os.path.relpath(filename, dirname)) |
166 xclass.setAttribute("name", os.path.relpath(rel_name, dirname)) |
149 xclass.setAttribute("filename", filename) |
167 xclass.setAttribute("filename", fr.relative_filename().replace("\\", "/")) |
150 xclass.setAttribute("complexity", "0") |
168 xclass.setAttribute("complexity", "0") |
151 |
169 |
152 branch_stats = analysis.branch_stats() |
170 branch_stats = analysis.branch_stats() |
153 missing_branch_arcs = analysis.missing_branch_arcs() |
171 missing_branch_arcs = analysis.missing_branch_arcs() |
154 |
172 |
191 branch_rate = rate(class_br_hits, class_branches) |
209 branch_rate = rate(class_br_hits, class_branches) |
192 else: |
210 else: |
193 branch_rate = "0" |
211 branch_rate = "0" |
194 xclass.setAttribute("branch-rate", branch_rate) |
212 xclass.setAttribute("branch-rate", branch_rate) |
195 |
213 |
196 package[0][className] = xclass |
214 package[0][rel_name] = xclass |
197 package[1] += class_hits |
215 package[1] += class_hits |
198 package[2] += class_lines |
216 package[2] += class_lines |
199 package[3] += class_br_hits |
217 package[3] += class_br_hits |
200 package[4] += class_branches |
218 package[4] += class_branches |
201 |
219 |