diff options
Diffstat (limited to 'test/unittest/cover_to_html.py')
-rwxr-xr-x | test/unittest/cover_to_html.py | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/test/unittest/cover_to_html.py b/test/unittest/cover_to_html.py new file mode 100755 index 0000000..2d69242 --- /dev/null +++ b/test/unittest/cover_to_html.py @@ -0,0 +1,355 @@ +#!/usr/bin/python + +#Copyright (c) 2005 Drew Smathers + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + + +#From http://svn.xix.python-hosting.com/trunk/xix/utils/cover.py + +"""More things for working with coverage.py +""" + +from UserList import UserList +import sys, os +import glob +from StringIO import StringIO +import lxml.etree as ET + +__author__ = 'Drew Smathers' +__version__ = '$Revision$'[11:-2] + +class AnnotationLine: + def __init__(self, text, isexec=False, covered=False): + self.text = text + self.isexec = isexec + self.covered = covered + # hack + if self.text and self.text[-1] == '\n': + self.text = self.text[:-1] + + def __repr__(self): + buf = "" + buf = buf + self.text + "\n" + return buf + + +class Annotation(UserList): + pass + +class AnnotationParser: + """Parser for annotation files generated by coverage. + """ + def parse(self, input): + fd = input + if not hasattr(input, 'read'): + fd = open(input) + annotation = Annotation() + for line in input: +# print line + if not line or line[0] not in ('>', '!'): + annotation.append(AnnotationLine(line[2:], isexec=False)) + elif line[0] == '>': + annotation.append(AnnotationLine(line[2:], isexec=True, covered=True)) + elif line[0] == '!': + annotation.append(AnnotationLine(line[2:], isexec=True, covered=False)) + else: + print >> sys.stderr, 'invalid annotation line: ' + line + + return annotation + +def annotationToXML(annotation, tree=False): + """Transform annotation object to XML string or ElementTree instance + if tree is True. + + @param annotation: Annotation instance + @param tree: set to true to return ElementTree instance. + """ + etree = _annotationToXML(annotation) + if tree: + return etree + buffer = StringIO() + etree.write(buffer) + return buffer.getvalue() + +def _annotationToXML(annotation): + root = ET.Element('coverageAnnotation') + for line in annotation: + aline = ET.SubElement(root, 'line') + aline.attrib['executable'] = str(line.isexec).lower() + aline.attrib['covered'] = str(line.covered).lower() + aline.text = line.text + return ET.ElementTree(root) + + +def annotationToHTML(annotation, xml=None, xslt=None, tree=False): + """Transform annotation object to HTML string or ElementTree instance + if tree is True. + + @param annotation: Annotation instance + @param xslt: xslt source for transformation + @param tree: set to true to return ElementTree instance. + """ + style = xslt or _XSLT +# style = _XSLT + if not hasattr(style, 'read'): + style = StringIO(style) + style = ET.parse(style) + etree = _annotationToXML(annotation) + html = etree.xslt(style) + if tree: + return html + buffer = StringIO() + html.write(buffer) + return buffer.getvalue() + +########################################### +# Coverage reports +########################################### + +class CoverageReport(UserList): + package_name = None + summary = None + +class CoverageReportEntry: + + def __init__(self, modname, statements, executed, coverage, missing): + self.modname = modname + self.statements = statements + self.executed = executed + self.coverage = coverage + self.missing = missing + +class CoverageReportParser: + + def parse(self, input): + fd = input + if not hasattr(input, 'read'): + fd = open(input) + report = CoverageReport() + for line in fd.readlines()[2:]: + tokens = line.split() + try: + modname, stmts, execd, coverage = tokens[:4] + perc = coverage[:-1] + _stmts, _execd = int(stmts), int(execd) + except Exception, e: + continue + if modname == 'TOTAL': + report.summary = CoverageReportEntry(modname, stmts, execd, coverage, None) + break + modname = modname.replace(os.path.sep, '.') + missed = [ tk.replace(',','') for tk in tokens[4:] ] + report.append(CoverageReportEntry(modname, stmts, execd, coverage, missed)) + return report + + +def reportToXML(report, tree=False): + """Transform report object to XML representation as string or ElementTree + instance if tree arg is set to True. + + @param report: report instance + @param tree: set to true to return ElementTree instance + """ + etree = _reportToXML(report) + if tree: + return etree + buffer = StringIO() + etree.write(buffer) + return buffer.getvalue() + +def _reportToXML(report): + root = ET.Element('coverage-report') + elm = ET.SubElement(root, 'summary') + attr = elm.attrib + attr['statements'] = report.summary.statements + attr['executed'] = report.summary.executed + attr['coverage'] = report.summary.coverage + for entry in report: + elm = ET.SubElement(root, 'module') + attr = elm.attrib + attr['name'] = entry.modname + attr['statements'] = entry.statements + attr['coverage'] = entry.coverage + attr['executed'] = entry.executed + melm = ET.SubElement(elm, 'missing-ranges') + for missed in entry.missing: + start_end = missed.split('-') + if len(start_end) == 2: + start, end = start_end + else: + start = end = start_end[0] + relm = ET.SubElement(melm, 'range') + relm.attrib['start'] = start + relm.attrib['end'] = end + return ET.ElementTree(root) + + +def gen_html(path_to_cover, path_for_html): + cover_files = glob.glob("%s/*,cover" % path_to_cover) + + # write out the css file + f = open("coverage.css", "w") + f.write(_CSS) + f.close() + + for cf in cover_files: + fd = open(cf, "r") + ann = AnnotationParser().parse(fd) + html = annotationToHTML(ann, xslt=_XSLT) + base_name = os.path.basename(cf) + source_name = base_name.split(',')[0] + html_name = "%s/%s.html" % (path_for_html, source_name) + f = open(html_name, "w") + f.write(html) + f.close() + +# fd = open("%s/cover.report" % path_to_cover) +# crp = CoverageReportParser().parse(fd) + + +_CSS = """ +body { + margin: 0px; +} + +h2 { + padding: 3px; + margin: 0px; + background-color: #ddd; + border-bottom: 2px solid; +} + +th { + text-align: left; + padding-right: 28px; +} + +.report-column { + border-bottom: 1px dashed; + padding-right: 28px; +} + +.summary { + background-color: #eed; + border-bottom: 0px; +} + +a { + text-decoration: none; +} + +.annotation-line { + font-style: italic; + font-weight: 700; + font-family: mono,arial; + border-left: 8px solid #aaa; + padding-left: 5px; +} + +.covered { + border-left: 8px solid #3e3; + background-color: #cfc; +} + +.uncovered { + border-left: 8px solid red; +} + +pre { + margin: 1px; + display: inline; +} + +.uncovered { + background: #ebb; +} + +.non-exec { + border-left: 8px solid #aaa; + background-color: #eee; +} + +.lineno { + margin-right: 5px; + background-color: #ef5; +} + +#colophon { + border-top: 1px solid; + background-color: #eee; + display: block; + position: relative; + bottom: 0px; + padding: 8px; + float: bottom; + width: 100%; + margin-top: 20px; + font-size: 75%; + text-align: center; +} + +""" + + +_XSLT = '''<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> +<xsl:output method="html"/> +<xsl:param name="modname"/> +<xsl:template match="/"> + <html><head><title>Coverage Results for <xsl:value-of select="$modname"/></title> + <link rel="stylesheet" type="text/css" href="coverage.css"/></head> + <body> + <h2>Coverage Results for <xsl:value-of select="$modname"/></h2> + <div class="annotated-source-code"> + <table width="100%"> + <xsl:for-each select="/coverageAnnotation/line"> + <xsl:variable name="lineno" select="position()"/> + <xsl:choose> + <xsl:when test="@executable='false'"> + <!--<div class="annotation-line non-exec">--> + <tr class="annotation-line non-exec"> + <td class="non-exec"><xsl:value-of select="$lineno"/></td> + <td><pre><xsl:value-of select="."/></pre></td></tr> + </xsl:when> + <xsl:when test="@executable='true' and @covered='true'"> + <tr class="annotation-line executable covered"> + <td class="covered"><xsl:value-of select="$lineno"/></td> + <td><pre><xsl:value-of select="."/></pre></td></tr> + </xsl:when> + <xsl:otherwise> + <tr class="annotation-line executable uncovered"> + <td class="uncovered"><xsl:value-of select="$lineno"/></td> + <td><pre><xsl:value-of select="."/></pre></td></tr> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </table> + </div></body></html> +</xsl:template> +</xsl:stylesheet>''' + + + + +if __name__ == "__main__": + gen_html(sys.argv[1], sys.argv[2]) + + |