Commits

Ned Batchelder  committed b3d4959

XML report: now validates against the Cobertura dtd.

  • Participants
  • Parent commits 9a3fba0

Comments (0)

Files changed (3)

File coverage/xmlreport.py

 """XML reporting for coverage.py"""
 
-import os, sys
+import os, sys, time
 import xml.dom.minidom
 
 from coverage import __url__, __version__
 from coverage.backward import sorted            # pylint: disable-msg=W0622
 from coverage.report import Reporter
 
+def rate(hit, num):
+    """Return the fraction of `hit`/`num`."""
+    return hit / (num or 1.0)
+
 
 class XmlReporter(Reporter):
     """A reporter for writing Cobertura-style XML coverage results."""
             "http://cobertura.sourceforge.net/xml/coverage-03.dtd"
             )
         self.xml_out = impl.createDocument(None, "coverage", docType)
-        root = self.xml_out.documentElement
+        xcoverage = self.xml_out.documentElement
 
-        root.appendChild(self.xml_out.createComment(
-            " Generated by coverage.py %s: %s " % (__version__, __url__)
+        xcoverage.setAttribute("version", __version__)
+        xcoverage.setAttribute("timestamp", str(int(time.time()*1000)))
+        xcoverage.appendChild(self.xml_out.createComment(
+            " Generated by coverage.py: %s " % __url__
             ))
-        packageXml = self.xml_out.createElement("packages")
-        root.appendChild(packageXml)
+        xpackages = self.xml_out.createElement("packages")
+        xcoverage.appendChild(xpackages)
         self.packages = {}
 
-        errors = False
-        
         self.report_files(self.xml_file, morfs, omit_prefixes=omit_prefixes)
 
-        # Don't write the XML data if we've encountered errors.
-        if errors:
-            return
+        lnum_tot, lhits_tot = 0, 0
+        bnum_tot, bhits_tot = 0, 0
+        
+        # Populate the XML DOM with the package info.
+        for pkg_name, pkg_data in self.packages.items():
+            class_elts, lhits, lnum, bhits, bnum = pkg_data
+            xpackage = self.xml_out.createElement("package")
+            xpackages.appendChild(xpackage)
+            xclasses = self.xml_out.createElement("classes")
+            xpackage.appendChild(xclasses)
+            for className in sorted(class_elts.keys()):
+                xclasses.appendChild(class_elts[className])
+            xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
+            xpackage.setAttribute("line-rate", str(rate(lhits, lnum)))
+            xpackage.setAttribute("branch-rate", str(rate(bhits, bnum)))
+            xpackage.setAttribute("complexity", "0.0")
 
-        # Populate the XML DOM with the package info.
-        for packageName, packageData in self.packages.items():
-            package = self.xml_out.createElement("package")
-            packageXml.appendChild(package)
-            classes = self.xml_out.createElement("classes")
-            package.appendChild(classes)
-            for className in sorted(packageData[0].keys()):
-                classes.appendChild(packageData[0][className])
-            package.setAttribute("name", packageName.replace(os.sep, '.'))
-            package.setAttribute("line-rate", str(packageData[1]/(packageData[2] or 1.0)))
-            package.setAttribute("branch-rate", str(packageData[3]/(packageData[4] or 1.0)))
-            package.setAttribute("complexity", "0.0")
-
+            lnum_tot += lnum
+            lhits_tot += lhits
+            bnum_tot += bnum
+            bhits_tot += bhits
+            
+        xcoverage.setAttribute("line-rate", str(rate(lhits_tot, lnum_tot)))
+        xcoverage.setAttribute("branch-rate", str(rate(bhits_tot, bnum_tot)))
+        
         # Use the DOM to write the output file.
         outfile.write(self.xml_out.toprettyxml())
 
         dirname, fname = os.path.split(cu.name)
         dirname = dirname or '.'
         package = self.packages.setdefault(dirname, [ {}, 0, 0, 0, 0 ])
-        c = self.xml_out.createElement("class")
-        lines = self.xml_out.createElement("lines")
-        c.appendChild(lines)
+
+        xclass = self.xml_out.createElement("class")
+
+        xclass.appendChild(self.xml_out.createElement("methods"))
+
+        xlines = self.xml_out.createElement("lines")
+        xclass.appendChild(xlines)
         className = fname.replace('.', '_')
-        c.setAttribute("name", className)
-        c.setAttribute("filename", cu.filename)
-        c.setAttribute("complexity", "0.0")
+        xclass.setAttribute("name", className)
+        xclass.setAttribute("filename", os.path.split(cu.filename)[1])
+        xclass.setAttribute("complexity", "0.0")
 
         # For each statement, create an XML 'line' element.
         for line in statements:
             # Q: can we get info about whether this statement
             # is a branch?  If so, that data should be
             # used here.
-            l.setAttribute("branch", "false")
-            lines.appendChild(l)
+            #l.setAttribute("branch", "false")
+            xlines.appendChild(l)
 
         class_lines = 1.0 * len(statements)
         class_hits = class_lines - len(missing)
         class_branch_hits = 0.0
 
         # Finalize the statistics that are collected in the XML DOM.
-        line_rate = class_hits / (class_lines or 1.0)
-        branch_rate = class_branch_hits / (class_branches or 1.0)
-        c.setAttribute("line-rate", str(line_rate))
-        c.setAttribute("branch-rate", str(branch_rate))
-        package[0][className] = c
+        line_rate = rate(class_hits, class_lines)
+        branch_rate = rate(class_branch_hits, class_branches)
+        xclass.setAttribute("line-rate", str(line_rate))
+        xclass.setAttribute("branch-rate", str(branch_rate))
+        package[0][className] = xclass
         package[1] += class_hits
         package[2] += class_lines
         package[3] += class_branch_hits

File test/farm/html/gold_x_xml/coverage.xml

 <?xml version="1.0" ?>
 <!DOCTYPE coverage
   SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
-<coverage>
-	<!-- Generated by coverage.py 3.1b1: http://nedbatchelder.com/code/coverage -->
+<coverage branch-rate="0.0" line-rate="0.666666666667" timestamp="1253972570431" version="3.1b1">
+	<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage -->
 	<packages>
 		<package branch-rate="0.0" complexity="0.0" line-rate="0.666666666667" name=".">
 			<classes>
-				<class branch-rate="0.0" complexity="0.0" filename="c:\ned\coverage\trunk\test\farm\html\src\x.py" line-rate="0.666666666667" name="x">
+				<class branch-rate="0.0" complexity="0.0" filename="x.py" line-rate="0.666666666667" name="x">
+					<methods/>
 					<lines>
-						<line branch="false" hits="1" number="3"/>
-						<line branch="false" hits="1" number="5"/>
-						<line branch="false" hits="0" number="7"/>
+						<line hits="1" number="3"/>
+						<line hits="1" number="5"/>
+						<line hits="0" number="7"/>
 					</lines>
 				</class>
 			</classes>

File test/farm/html/run_x_xml.py

 
 runfunc(html_it, rundir="src")
 
-compare("gold_x_xml", "xml")
+compare("gold_x_xml", "xml", scrubs=[
+    (r' timestamp="\d+"', ' timestamp="TIMESTAMP"'),
+    (r' version="[-.\w]+"', ' version="VERSION"'),
+    ])
 clean("xml")