1563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#!/usr/bin/env python
2563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
3563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Copyright (C) 2004, 2005, 2006 Nathaniel Smith
4563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Copyright (C) 2007 Holger Hans Peter Freyther
5563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
6563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Redistribution and use in source and binary forms, with or without
7563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# modification, are permitted provided that the following conditions
8563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# are met:
9563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
10563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 1.  Redistributions of source code must retain the above copyright
11563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#     notice, this list of conditions and the following disclaimer. 
12563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 2.  Redistributions in binary form must reproduce the above copyright
13563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#     notice, this list of conditions and the following disclaimer in the
14563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#     documentation and/or other materials provided with the distribution. 
15563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#     its contributors may be used to endorse or promote products derived
17563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#     from this software without specific prior written permission. 
18563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
19563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
30563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
31563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# HTML output inspired by the output of lcov as found on the GStreamer
32563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# site. I assume this is not copyrightable.
33563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
34563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
35563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
36563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
37563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark# Read all CSV files and
38563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#  Create an overview file
39563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
40563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark#
41563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
42563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
43563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport sys
44563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport csv
45563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport glob
46563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport time
47563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport os
48563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport os.path
49563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport datetime
50563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkimport shutil
51563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
52563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkos.environ["TTFPATH"] = ":".join(["/usr/share/fonts/truetype/" + d
53563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                                  for d in "ttf-bitstream-vera",
54563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                                           "freefont",
55563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                                           "msttcorefonts"])
56563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
57563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarklevel_LOW    = 10
58563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarklevel_MEDIUM = 70
59563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
60563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef copy_files(dest_dir):
61563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
62563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    Copy the CSS and the png's to the destination directory
63563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
64563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    images = ["amber.png", "emerald.png", "glass.png", "ruby.png", "snow.png"]
65563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    css    = "gcov.css"
66563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    (base_path, name) = os.path.split(__file__)
67563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    base_path = os.path.abspath(base_path)
68563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
69563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    shutil.copyfile(os.path.join(base_path,css), os.path.join(dest_dir,css))
70563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    map(lambda x: shutil.copyfile(os.path.join(base_path,x), os.path.join(dest_dir,x)), images)
71563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
72563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef sumcov(cov):
73563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        return "%.2f%% (%s/%s)" % (cov[1] * 100.0 / (cov[0] or 1), cov[1], cov[0])
74563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
75563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef create_page(dest_dir, name):
76563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    index = open(os.path.join(dest_dir, name), "w")
77563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    index.write("""<HTML>
78563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <HEAD>
79563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <TITLE>WebKit test coverage information</TITLE>
80563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <link rel="stylesheet" type="text/css" href="gcov.css">
81563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </HEAD>
82563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <BODY>
83563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """)
84563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    return index
85563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
86563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef generate_header(file, last_time, total_lines, total_executed, path, image):
87563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    product = "WebKit"
88563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    date = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(last_time))
89563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    covered_lines = sumcov((total_lines, total_executed))
90563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
91563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    file.write("""<table width="100%%" border=0 cellspacing=0 cellpadding=0>
92563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr><td class="title">GCOV code coverage report</td></tr>
93563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
94563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
95563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr>
96563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td width="100%%">
97563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <table cellpadding=1 border=0 width="100%%">
98563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <tr>
99563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Current&nbsp;view:</td>
100563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="80%%" colspan=4>%(path)s</td>
101563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        </tr>
102563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <tr>
103563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Test:</td>
104563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="80%%" colspan=4>%(product)s</td>
105563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        </tr>
106563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <tr>
107563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Date:</td>
108563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="20%%">%(date)s</td>
109563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td width="20%%"></td>
110563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Instrumented&nbsp;lines:</td>
111563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="20%%">%(total_lines)s</td>
112563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        </tr>
113563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <tr>
114563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Code&nbsp;covered:</td>
115563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="20%%">%(covered_lines)s</td>
116563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td width="20%%"></td>
117563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerItem" width="20%%">Executed&nbsp;lines:</td>
118563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark          <td class="headerValue" width="20%%">%(total_executed)s</td>
119563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        </tr>
120563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        </table>
121563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      </td>
122563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </tr>
123563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr><td class="ruler"><img src="glass.png" width=3 height=3 alt=""></td></tr>
124563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark  </table>""" % vars())
125563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # disabled for now <tr><td><img src="%(image)s"></td></tr>
126563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
127563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef generate_table_item(file, name, total_lines, covered_lines):
128563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    covered_precise = (covered_lines*100.0)/(total_lines or 1.0)
129563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    covered = int(round(covered_precise))
130563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    remainder = 100-covered
131563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    (image,perClass,numClass) = coverage_icon(covered_precise)
132563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site = "%s.html" % name.replace(os.path.sep,'__')
133563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    file.write("""
134563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <tr>
135563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="coverFile"><a href="%(site)s">%(name)s</a></td>
136563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="coverBar" align="center">
137563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        <table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="%(image)s" width=%(covered)s height=10 alt="%(covered_precise).2f"><img src="snow.png" width=%(remainder)s height=10 alt="%(covered_precise).2f"></td></tr></table>
138563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      </td>
139563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="%(perClass)s">%(covered_precise).2f&nbsp;%%</td>
140563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="%(numClass)s">%(covered_lines)s&nbsp;/&nbsp;%(total_lines)s&nbsp;lines</td>
141563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </tr>
142563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """ % vars())
143563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
144563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef generate_table_header_start(file):
145563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark  file.write("""<center>
146563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark  <table width="80%%" cellpadding=2 cellspacing=1 border=0>
147563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
148563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr>
149563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td width="50%%"><br></td>
150563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td width="15%%"></td>
151563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td width="15%%"></td>
152563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td width="20%%"></td>
153563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </tr>
154563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
155563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr>
156563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="tableHead">Directory&nbsp;name</td>
157563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td class="tableHead" colspan=3>Coverage</td>
158563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </tr>
159563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """)
160563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
161563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef coverage_icon(percent):
162563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    if percent < level_LOW:
163563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        return ("ruby.png", "coverPerLo", "coverNumLo")
164563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    elif percent < level_MEDIUM:
165563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        return ("amber.png", "coverPerMed", "coverNumMed")
166563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    else:
167563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        return ("emerald.png", "coverPerHi", "coverNumHi")
168563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
169563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef replace(text, *pairs):
170563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
171563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    From pydoc... almost identical at least
172563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
173563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    from string import split, join
174563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    while pairs:
175563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        (a,b) = pairs[0]
176563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        text = join(split(text, a), b)
177563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        pairs = pairs[1:]
178563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    return text
179563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
180563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef escape(text):
181563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
182563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    Escape string to be conform HTML
183563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
184563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    return replace(text,
185563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                        ('&', '&amp;'),
186563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                        ('<', '&lt;' ),
187563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                        ('>', '&gt;' ) )
188563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
189563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef generate_table_header_end(file):
190563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    file.write("""</table>
191563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </center>""")
192563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
193563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef write_title_page(dest_dir, last_time, last_tot_lines, last_tot_covered, dir_series):
194563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
195563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    Write the index.html with a overview of each directory
196563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """
197563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    index= create_page(dest_dir, "index.html")
198563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_header(index, last_time, last_tot_lines, last_tot_covered, "directory", "images/Total.png")
199563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Create the directory overview
200563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_table_header_start(index)
201563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    dirs = dir_series.keys()
202563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    dirs.sort()
203563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for dir in dirs:
204563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        (dir_files, total_lines, covered_lines,_) = dir_series[dir][-1]
205563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        generate_table_item(index, dir, total_lines, covered_lines)
206563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_table_header_end(index)
207563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
208563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    index.write("""</BODY></HTML>""")
209563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    index.close()
210563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
211563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef write_directory_site(dest_dir, dir_name, last_time, dir_series, file_series):
212563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    escaped_dir = dir_name.replace(os.path.sep,'__')
213563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site = create_page(dest_dir, "%s.html" % escaped_dir)
214563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    (_,tot_lines,tot_covered,files) = dir_series[dir_name][-1]
215563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_header(site, last_time, tot_lines, tot_covered, "directory - %s" % dir_name, "images/%s.png" % escaped_dir)
216563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
217563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    files.sort()
218563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
219563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_table_header_start(site)
220563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for file in files:
221563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        (lines,covered) = file_series[file][-1]
222563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        generate_table_item(site, file, lines, covered)
223563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
224563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_table_header_end(site)
225563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site.write("""</BODY></HTML>""")
226563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site.close()
227563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
228563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef write_file_site(dest_dir, file_name, last_time, data_dir, last_id, file_series):
229563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    escaped_name = file_name.replace(os.path.sep,'__')
230563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site = create_page(dest_dir, "%s.html" % escaped_name)
231563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    (tot_lines,tot_covered) = file_series[file_name][-1]
232563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    generate_header(site, last_time, tot_lines, tot_covered, "file - %s" % file_name, "images/%s.png" % escaped_name)
233563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
234563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    path = "%s/%s.annotated%s" % (data_dir,last_id,file_name)
235563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
236563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # In contrast to the lcov we want to show files that have been compiled
237563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # but have not been tested at all. This means we have sourcefiles with 0
238563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # lines covered in the path but they are not lcov files.
239563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # To identify them we check the first line now. If we see that we can
240563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # continue
241563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    #         -:    0:Source:
242563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    try:
243563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        file = open(path, "r")
244563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    except:
245563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        return
246563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    all_lines = file.read().split("\n")
247563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
248563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Convert the gcov file to HTML if we have a chanche to do so
249563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Scan each line and see if it was covered or not and escape the
250563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # text
251563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    if len(all_lines) == 0 or not "-:    0:Source:" in all_lines[0]:
252563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        site.write("<p>The file was not excercised</p>")
253563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    else:
254563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        site.write("""</br><table cellpadding=0 cellspacing=0 border=0>
255563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr>
256563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td><br></td>
257563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    </tr>
258563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    <tr>
259563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark      <td><pre class="source">
260563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    """)
261563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        for line in all_lines:
262563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            split_line = line.split(':',2)
263563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            # e.g. at the EOF
264563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            if len(split_line) == 1:
265563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                continue
266563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            line_number = split_line[1].strip()
267563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            if line_number == "0":
268563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                continue
269563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            covered = 15*" "
270563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            end = ""
271563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            if "#####" in split_line[0]:
272563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                covered = '<span class="lineNoCov">%15s' % "0"
273563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                end = "</span>"
274563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            elif split_line[0].strip() != "-":
275563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                covered = '<span class="lineCov">%15s' % split_line[0].strip()
276563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark                end = "</span>"
277563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
278563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            escaped_line = escape(split_line[2])
279563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            str = '<span class="lineNum">%(line_number)10s </span>%(covered)s: %(escaped_line)s%(end)s\n' % vars()
280563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            site.write(str)
281563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        site.write("</pre></td></tr></table>")
282563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site.write("</BODY></HTML>")
283563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    site.close()
284563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
285563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef main(progname, args):
286563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    if len(args) != 2:
287563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        sys.exit("Usage: %s DATADIR OUTDIR" % progname)
288563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
289563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    branch = "WebKit from trunk"
290563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    datadir, outdir = args
291563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
292563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # First, load in all data from the data directory.
293563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    data = []
294563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for datapath in glob.glob(os.path.join(datadir, "*.csv")):
295563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        data.append(read_csv(datapath))
296563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Sort by time
297563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    data.sort()
298563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
299563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Calculate time series for each file.
300563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    times = [sample[0] for sample in data]
301563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    times = [datetime.datetime.utcfromtimestamp(t) for t in times]
302563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
303563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    all_files = {}
304563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    all_dirs  = {}
305563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for sample in data:
306563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        t, i, tot_line, tot_cover, per_file, per_dir = sample
307563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        all_files.update(per_file)
308563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        all_dirs.update(per_dir)
309563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    total_series = []
310563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    file_serieses = dict([[k, [(0, 0)] * len(times)] for k in all_files.keys()])
311563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    dir_serieses  = dict([[k, [(0, 0, 0, [])] * len(times)] for k in all_dirs.keys()])
312563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    data_idx = 0
313563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for sample in data:
314563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        t, i, tot_line, tot_cover, per_file, per_dir = sample
315563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        total_series.append([tot_line, tot_cover])
316563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        for f, covinfo in per_file.items():
317563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            file_serieses[f][data_idx] = covinfo
318563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        for f, covinfo in per_dir.items():
319563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            dir_serieses[f][data_idx] = covinfo
320563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        data_idx += 1
321563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
322563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
323563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Okay, ready to start outputting.  First make sure our directories
324563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # exist.
325563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    if not os.path.exists(outdir):
326563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        os.makedirs(outdir)
327563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    rel_imgdir = "images"
328563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    imgdir = os.path.join(outdir, rel_imgdir)
329563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    if not os.path.exists(imgdir):
330563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        os.makedirs(imgdir)
331563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
332563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
333563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # And look up the latest revision id, and coverage information
334563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    last_time, last_id, last_tot_lines, last_tot_covered = data[-1][:4]
335563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
336563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Now start generating our html file
337563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    copy_files(outdir)
338563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    write_title_page(outdir, last_time, last_tot_lines, last_tot_covered, dir_serieses)
339563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
340563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    dir_keys = dir_serieses.keys()
341563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    dir_keys.sort()
342563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for dir_name in dir_keys:
343563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        write_directory_site(outdir, dir_name, last_time, dir_serieses, file_serieses)
344563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
345563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    file_keys = file_serieses.keys()
346563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for file_name in file_keys:
347563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        write_file_site(outdir, file_name, last_time, datadir, last_id, file_serieses)
348563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
349563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkdef read_csv(path):
350563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    r = csv.reader(open(path, "r"))
351563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # First line is id, time
352563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for row in r:
353563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        id, time_str = row
354563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        break
355563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    time = int(float(time_str))
356563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    # Rest of lines are path, total_lines, covered_lines
357563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    per_file = {}
358563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    per_dir  = {}
359563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    grand_total_lines, grand_covered_lines = 0, 0
360563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    for row in r:
361563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        path, total_lines_str, covered_lines_str = row
362563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        total_lines = int(total_lines_str)
363563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        covered_lines = int(covered_lines_str)
364563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        grand_total_lines += total_lines
365563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        grand_covered_lines += covered_lines
366563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        per_file[path] = [total_lines, covered_lines]
367563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
368563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        # Update dir statistics
369563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        dirname = os.path.dirname(path)
370563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        if not dirname in per_dir:
371563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark            per_dir[dirname] = (0,0,0,[])
372563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        (dir_files,dir_total_lines,dir_covered_lines, files) = per_dir[dirname]
373563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        dir_files += 1
374563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        dir_total_lines += total_lines
375563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        dir_covered_lines += covered_lines
376563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        files.append(path)
377563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        per_dir[dirname] = (dir_files,dir_total_lines,dir_covered_lines,files)
378563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    return [time, id, grand_total_lines, grand_covered_lines, per_file, per_dir]
379563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark
380563af33bc48281d19dce701398dbb88cb54fd7ecCary Clarkif __name__ == "__main__":
381563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    import sys
382563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark    main(sys.argv[0], sys.argv[1:])
383