15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Crocodile HTML output.""" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import xml.dom 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CrocHtmlError(Exception): 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Coverage HTML error.""" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HtmlElement(object): 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Node in a HTML file.""" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, doc, element): 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Constructor. 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doc: XML document object. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) element: XML element. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doc = doc 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.element = element 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def E(self, name, **kwargs): 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a child element. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name: Name of element. 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kwargs: Attributes for element. To use an attribute which is a python 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reserved word (i.e. 'class'), prefix the attribute name with 'e_'. 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The child element. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) he = HtmlElement(self.doc, self.doc.createElement(name)) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) element = he.element 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.element.appendChild(element) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for k, v in kwargs.iteritems(): 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if k.startswith('e_'): 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Remove prefix 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) element.setAttribute(k[2:], str(v)) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) element.setAttribute(k, str(v)) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return he 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Text(self, text): 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a text node. 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) text: Text to add. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self. 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) t = self.doc.createTextNode(str(text)) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.element.appendChild(t) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HtmlFile(object): 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """HTML file.""" 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, xml_impl, filename): 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Constructor. 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xml_impl: DOMImplementation to use to create document. 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename: Path to file. 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.xml_impl = xml_impl 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doctype = xml_impl.createDocumentType( 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'HTML', '-//W3C//DTD HTML 4.01//EN', 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'http://www.w3.org/TR/html4/strict.dtd') 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doc = xml_impl.createDocument(None, 'html', doctype) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.filename = filename 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Create head and body elements 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) root = HtmlElement(self.doc, self.doc.documentElement) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.head = root.E('head') 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.body = root.E('body') 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Write(self, cleanup=True): 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Writes the file. 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cleanup: If True, calls unlink() on the internal xml document. This 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) frees up memory, but means that you can't use this file for anything 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else. 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = open(self.filename, 'wt') 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doc.writexml(f, encoding='UTF-8') 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.close() 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cleanup: 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doc.unlink() 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Prevent future uses of the doc now that we've unlinked it 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.doc = None 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#------------------------------------------------------------------------------ 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)COV_TYPE_STRING = {None: 'm', 0: 'i', 1: 'E', 2: ' '} 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)COV_TYPE_CLASS = {None: 'missing', 0: 'instr', 1: 'covered', 2: ''} 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CrocHtml(object): 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Crocodile HTML output class.""" 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, cov, output_root, base_url=None): 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Constructor.""" 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.cov = cov 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.output_root = output_root 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.base_url = base_url 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.xml_impl = xml.dom.getDOMImplementation() 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.time_string = 'Coverage information generated %s.' % time.asctime() 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def CreateHtmlDoc(self, filename, title): 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Creates a new HTML document. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) filename: Filename to write to, relative to self.output_root. 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) title: Title of page 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The document. 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = HtmlFile(self.xml_impl, self.output_root + '/' + filename) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.head.E('title').Text(title) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.base_url: 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) css_href = self.base_url + 'croc.css' 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_href = self.base_url + os.path.dirname(filename) 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not base_href.endswith('/'): 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_href += '/' 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.head.E('base', href=base_href) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) css_href = '../' * (len(filename.split('/')) - 1) + 'croc.css' 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.head.E('link', rel='stylesheet', type='text/css', href=css_href) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return f 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def AddCaptionForFile(self, body, path): 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a caption for the file, with links to each parent dir. 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body: Body elemement. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path: Path to file. 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # This is slightly different that for subdir, because it needs to have a 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # link to the current directory's index.html. 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr = body.E('h2') 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.Text('Coverage for ') 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirs = [''] + path.split('/') 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_dirs = len(dirs) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(num_dirs - 1): 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.E('a', href=( 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '../' * (num_dirs - i - 2) + 'index.html')).Text(dirs[i] + '/') 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.Text(dirs[-1]) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def AddCaptionForSubdir(self, body, path): 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a caption for the subdir, with links to each parent dir. 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body: Body elemement. 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path: Path to subdir. 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Link to parent dirs 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr = body.E('h2') 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.Text('Coverage for ') 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirs = [''] + path.split('/') 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_dirs = len(dirs) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(num_dirs - 1): 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.E('a', href=( 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '../' * (num_dirs - i - 1) + 'index.html')).Text(dirs[i] + '/') 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hdr.Text(dirs[-1] + '/') 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def AddSectionHeader(self, table, caption, itemtype, is_file=False): 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a section header to the coverage table. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table: Table to add rows to. 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) caption: Caption for section, if not None. 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) itemtype: Type of items in this section, if not None. 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_file: Are items in this section files? 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if caption is not None: 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table.E('tr').E('th', e_class='secdesc', colspan=8).Text(caption) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr = table.E('tr') 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if itemtype is not None: 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section').Text(itemtype) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section').Text('Coverage') 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section', colspan=3).Text( 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Lines executed / instrumented / missing') 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph = sec_hdr.E('th', e_class='section') 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='color:#00FF00').Text('exe') 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.Text(' / ') 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='color:#FFFF00').Text('inst') 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.Text(' / ') 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='color:#FF0000').Text('miss') 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if is_file: 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section').Text('Language') 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section').Text('Group') 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sec_hdr.E('th', e_class='section', colspan=2) 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def AddItem(self, table, itemname, stats, attrs, link=None): 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Adds a bar graph to the element. This is a series of <td> elements. 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table: Table to add item to. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) itemname: Name of item. 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stats: Stats object. 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) attrs: Attributes dictionary; if None, no attributes will be printed. 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) link: Destination for itemname hyperlink, if not None. 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row = table.E('tr') 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Add item name 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if itemname is not None: 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_elem = row.E('td') 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if link is not None: 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_elem = item_elem.E('a', href=link) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_elem.Text(itemname) 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Get stats 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stat_exe = stats.get('lines_executable', 0) 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stat_ins = stats.get('lines_instrumented', 0) 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stat_cov = stats.get('lines_covered', 0) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent = row.E('td') 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Add text 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', e_class='number').Text(stat_cov) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', e_class='number').Text(stat_ins) 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', e_class='number').Text(stat_exe - stat_ins) 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Add percent and graph; only fill in if there's something in there 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph = row.E('td', e_class='graph', width=100) 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if stat_exe: 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent_cov = 100.0 * stat_cov / stat_exe 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent_ins = 100.0 * stat_ins / stat_exe 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Color percent based on thresholds 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent.Text('%.1f%%' % percent_cov) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if percent_cov >= 80: 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent.element.setAttribute('class', 'high_pct') 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif percent_cov >= 60: 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent.element.setAttribute('class', 'mid_pct') 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent.element.setAttribute('class', 'low_pct') 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Graphs use integer values 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent_cov = int(percent_cov) 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) percent_ins = int(percent_ins) 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.Text('.') 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='padding-left:%dpx' % percent_cov, 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e_class='g_covered') 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='padding-left:%dpx' % (percent_ins - percent_cov), 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e_class='g_instr') 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graph.E('span', style='padding-left:%dpx' % (100 - percent_ins), 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e_class='g_missing') 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if attrs: 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', e_class='stat').Text(attrs.get('language')) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', e_class='stat').Text(attrs.get('group')) 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.E('td', colspan=2) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def WriteFile(self, cov_file): 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Writes the HTML for a file. 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov_file: croc.CoveredFile to write. 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ' ' + cov_file.filename 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) title = 'Coverage for ' + cov_file.filename 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = self.CreateHtmlDoc(cov_file.filename + '.html', title) 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body = f.body 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Write header section 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddCaptionForFile(body, cov_file.filename) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Summary for this file 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table = body.E('table') 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddSectionHeader(table, None, None, is_file=True) 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddItem(table, None, cov_file.stats, cov_file.attrs) 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('h2').Text('Line-by-line coverage:') 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Print line-by-line coverage 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cov_file.local_path: 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) code_table = body.E('table').E('tr').E('td').E('pre') 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flines = open(cov_file.local_path, 'rt') 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineno = 0 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for line in flines: 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineno += 1 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line_cov = cov_file.lines.get(lineno, 2) 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e_class = COV_TYPE_CLASS.get(line_cov) 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) code_table.E('span', e_class=e_class).Text('%4d %s : %s\n' % ( 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineno, 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) COV_TYPE_STRING.get(line_cov), 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) line.rstrip() 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) )) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.Text('Line-by-line coverage not available. Make sure the directory' 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ' containing this file has been scanned via ') 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('B').Text('add_files') 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.Text(' in a configuration file, or the ') 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('B').Text('--addfiles') 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.Text(' command line option.') 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO: if file doesn't have a local path, try to find it by 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # reverse-mapping roots and searching for the file. 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('p', e_class='time').Text(self.time_string) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.Write() 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def WriteSubdir(self, cov_dir): 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Writes the index.html for a subdirectory. 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov_dir: croc.CoveredDir to write. 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ' ' + cov_dir.dirpath + '/' 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Create the subdir if it doesn't already exist 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) subdir = self.output_root + '/' + cov_dir.dirpath 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not os.path.exists(subdir): 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.mkdir(subdir) 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cov_dir.dirpath: 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) title = 'Coverage for ' + cov_dir.dirpath + '/' 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = self.CreateHtmlDoc(cov_dir.dirpath + '/index.html', title) 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) title = 'Coverage summary' 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f = self.CreateHtmlDoc('index.html', title) 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body = f.body 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dirs = [''] + cov_dir.dirpath.split('/') 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_dirs = len(dirs) 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sort_jsfile = '../' * (num_dirs - 1) + 'sorttable.js' 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) script = body.E('script', src=sort_jsfile) 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('/script') 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Write header section 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cov_dir.dirpath: 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddCaptionForSubdir(body, cov_dir.dirpath) 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('h2').Text(title) 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table = body.E('table', e_class='sortable') 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table.E('h3').Text('Coverage by Group') 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Coverage by group 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddSectionHeader(table, None, 'Group') 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for group in sorted(cov_dir.stats_by_group): 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddItem(table, group, cov_dir.stats_by_group[group], None) 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # List subdirs 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cov_dir.subdirs: 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table = body.E('table', e_class='sortable') 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table.E('h3').Text('Subdirectories') 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddSectionHeader(table, None, 'Subdirectory') 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for d in sorted(cov_dir.subdirs): 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddItem(table, d + '/', cov_dir.subdirs[d].stats_by_group['all'], 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) None, link=d + '/index.html') 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # List files 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if cov_dir.files: 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table = body.E('table', e_class='sortable') 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table.E('h3').Text('Files in This Directory') 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddSectionHeader(table, None, 'Filename', 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_file=True) 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for filename in sorted(cov_dir.files): 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov_file = cov_dir.files[filename] 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.AddItem(table, filename, cov_file.stats, cov_file.attrs, 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) link=filename + '.html') 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) body.E('p', e_class='time').Text(self.time_string) 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) f.Write() 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def WriteRoot(self): 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Writes the files in the output root.""" 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find ourselves 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) src_dir = os.path.split(self.WriteRoot.func_code.co_filename)[0] 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Files to copy into output root 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy_files = ['croc.css'] 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Third_party files to copy into output root 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) third_party_files = ['sorttable.js'] 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Copy files from our directory into the output directory 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for copy_file in copy_files: 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ' Copying %s' % copy_file 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) shutil.copyfile(os.path.join(src_dir, copy_file), 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.path.join(self.output_root, copy_file)) 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Copy third party files from third_party directory into 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # the output directory 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) src_dir = os.path.join(src_dir, 'third_party') 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for third_party_file in third_party_files: 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print ' Copying %s' % third_party_file 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) shutil.copyfile(os.path.join(src_dir, third_party_file), 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) os.path.join(self.output_root, third_party_file)) 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Write(self): 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Writes HTML output.""" 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'Writing HTML to %s...' % self.output_root 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Loop through the tree and write subdirs, breadth-first 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO: switch to depth-first and sort values - makes nicer output? 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) todo = [self.cov.tree] 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while todo: 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov_dir = todo.pop(0) 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Append subdirs to todo list 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) todo += cov_dir.subdirs.values() 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Write this subdir 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.WriteSubdir(cov_dir) 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Write files in this subdir 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for cov_file in cov_dir.files.itervalues(): 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.WriteFile(cov_file) 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Write files in root directory 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.WriteRoot() 452