#!/usr/bin/python2.4 # # Copyright 2009, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Crocodile HTML output.""" import os import shutil import time import xml.dom class CrocHtmlError(Exception): """Coverage HTML error.""" class HtmlElement(object): """Node in a HTML file.""" def __init__(self, doc, element): """Constructor. Args: doc: XML document object. element: XML element. """ self.doc = doc self.element = element def E(self, name, **kwargs): """Adds a child element. Args: name: Name of element. kwargs: Attributes for element. To use an attribute which is a python reserved word (i.e. 'class'), prefix the attribute name with 'e_'. Returns: The child element. """ he = HtmlElement(self.doc, self.doc.createElement(name)) element = he.element self.element.appendChild(element) for k, v in kwargs.iteritems(): if k.startswith('e_'): # Remove prefix element.setAttribute(k[2:], str(v)) else: element.setAttribute(k, str(v)) return he def Text(self, text): """Adds a text node. Args: text: Text to add. Returns: self. """ t = self.doc.createTextNode(str(text)) self.element.appendChild(t) return self class HtmlFile(object): """HTML file.""" def __init__(self, xml_impl, filename): """Constructor. Args: xml_impl: DOMImplementation to use to create document. filename: Path to file. """ self.xml_impl = xml_impl doctype = xml_impl.createDocumentType( 'HTML', '-//W3C//DTD HTML 4.01//EN', 'http://www.w3.org/TR/html4/strict.dtd') self.doc = xml_impl.createDocument(None, 'html', doctype) self.filename = filename # Create head and body elements root = HtmlElement(self.doc, self.doc.documentElement) self.head = root.E('head') self.body = root.E('body') def Write(self, cleanup=True): """Writes the file. Args: cleanup: If True, calls unlink() on the internal xml document. This frees up memory, but means that you can't use this file for anything else. """ f = open(self.filename, 'wt') self.doc.writexml(f, encoding='UTF-8') f.close() if cleanup: self.doc.unlink() # Prevent future uses of the doc now that we've unlinked it self.doc = None #------------------------------------------------------------------------------ COV_TYPE_STRING = {None: 'm', 0: 'i', 1: 'E', 2: ' '} COV_TYPE_CLASS = {None: 'missing', 0: 'instr', 1: 'covered', 2: ''} class CrocHtml(object): """Crocodile HTML output class.""" def __init__(self, cov, output_root): """Constructor.""" self.cov = cov self.output_root = output_root self.xml_impl = xml.dom.getDOMImplementation() self.time_string = 'Coverage information generated %s.' % time.asctime() def CreateHtmlDoc(self, filename, title): """Creates a new HTML document. Args: filename: Filename to write to, relative to self.output_root. title: Title of page Returns: The document. """ f = HtmlFile(self.xml_impl, self.output_root + '/' + filename) f.head.E('title').Text(title) f.head.E( 'link', rel='stylesheet', type='text/css', href='../' * (len(filename.split('/')) - 1) + 'croc.css') return f def AddCaptionForFile(self, body, path): """Adds a caption for the file, with links to each parent dir. Args: body: Body elemement. path: Path to file. """ # This is slightly different that for subdir, because it needs to have a # link to the current directory's index.html. hdr = body.E('h2') hdr.Text('Coverage for ') dirs = [''] + path.split('/') num_dirs = len(dirs) for i in range(num_dirs - 1): hdr.E('a', href=( '../' * (num_dirs - i - 2) + 'index.html')).Text(dirs[i] + '/') hdr.Text(dirs[-1]) def AddCaptionForSubdir(self, body, path): """Adds a caption for the subdir, with links to each parent dir. Args: body: Body elemement. path: Path to subdir. """ # Link to parent dirs hdr = body.E('h2') hdr.Text('Coverage for ') dirs = [''] + path.split('/') num_dirs = len(dirs) for i in range(num_dirs - 1): hdr.E('a', href=( '../' * (num_dirs - i - 1) + 'index.html')).Text(dirs[i] + '/') hdr.Text(dirs[-1] + '/') def AddSectionHeader(self, table, caption, itemtype, is_file=False): """Adds a section header to the coverage table. Args: table: Table to add rows to. caption: Caption for section, if not None. itemtype: Type of items in this section, if not None. is_file: Are items in this section files? """ if caption is not None: table.E('tr').E('td', e_class='secdesc', colspan=8).Text(caption) sec_hdr = table.E('tr') if itemtype is not None: sec_hdr.E('td', e_class='section').Text(itemtype) sec_hdr.E('td', e_class='section').Text('Coverage') sec_hdr.E('td', e_class='section', colspan=3).Text( 'Lines executed / instrumented / missing') graph = sec_hdr.E('td', e_class='section') graph.E('span', style='color:#00FF00').Text('exe') graph.Text(' / ') graph.E('span', style='color:#FFFF00').Text('inst') graph.Text(' / ') graph.E('span', style='color:#FF0000').Text('miss') if is_file: sec_hdr.E('td', e_class='section').Text('Language') sec_hdr.E('td', e_class='section').Text('Group') else: sec_hdr.E('td', e_class='section', colspan=2) def AddItem(self, table, itemname, stats, attrs, link=None): """Adds a bar graph to the element. This is a series of