summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenneth Graunke <kenneth@whitecape.org>2012-11-29 23:20:40 -0800
committerKenneth Graunke <kenneth@whitecape.org>2012-11-29 23:20:40 -0800
commit7f3801306d91849839f25a15b6dc49b826a924af (patch)
treee2a5c4e053d53eeba1e742db023707633c2e5932
parent5dbc923793ec757afff3b9aebb4cf5b02359baed (diff)
Reporting actually almost works
-rwxr-xr-xprograms/report.py179
-rw-r--r--programs/templates/index.css85
-rw-r--r--programs/templates/index.html25
3 files changed, 241 insertions, 48 deletions
diff --git a/programs/report.py b/programs/report.py
index 7ded68a..d5e4425 100755
--- a/programs/report.py
+++ b/programs/report.py
@@ -30,12 +30,59 @@ import sys
from framework.database import ResultDatabase
+def readfile(filename):
+ with open(filename) as f:
+ return f.read()
+
+def writefile(filename, text):
+ with open(filename, "w") as f:
+ f.write(text)
+
+templateDir = path.join(path.dirname(path.realpath(__file__)), 'templates')
+templates = {
+ 'index': readfile(path.join(templateDir, 'index.html'))
+}
+
+#############################################################################
+##### Vector indicating the number of subtests that have passed/failed/etc.
+#############################################################################
+class PassVector:
+ def __init__(self, p, f, s, c, t, h):
+ self.passnr = p
+ self.failnr = f
+ self.skipnr = s
+ self.crashnr = c
+ self.timeoutnr = t
+ self.hangnr = h
+
+ def add(self, o):
+ self.passnr += o.passnr
+ self.failnr += o.failnr
+ self.skipnr += o.skipnr
+ self.crashnr += o.crashnr
+ self.timeoutnr += o.timeoutnr
+ self.hangnr += o.hangnr
+
+ # Do not count skips
+ def totalRun(self):
+ return self.passnr + self.failnr + self.crashnr + self.timeoutnr + self.hangnr
+
+def toPassVector(status):
+ vectormap = {
+ 'pass': PassVector(1,0,0,0,0,0),
+ 'fail': PassVector(0,1,0,0,0,0),
+ 'skip': PassVector(0,0,1,0,0,0),
+ 'crash': PassVector(0,0,0,1,0,0),
+ 'timeout': PassVector(0,0,0,0,1,0),
+ 'hang': PassVector(0,0,0,0,0,1)
+ }
+ return vectormap[status]
#############################################################################
##### Helper functions
#############################################################################
-filename_char_re = re.compile(r'[^a-zA-Z_]+')
+filename_char_re = re.compile(r'[^a-zA-Z0-9_]+')
def escape(s):
return filename_char_re.sub('', s.replace('/', '__'))
@@ -43,74 +90,109 @@ def escape(s):
##### Summary page generation
#############################################################################
+def testResult(run_name, full_name, status):
+ html = '<a class="%(status)s" href="%(link)s">%(status)s</a>' % {
+ 'status': status,
+ 'link': path.join(run_name, escape(full_name) + '.html')
+ }
+ return html
+
class StackEntry:
def __init__(self, num_runs, group_name):
self.name = group_name
- #self.results = [PassVector(0,0,0,0,0,0,0) for i in range(num_runs)]
+ self.results = [PassVector(0,0,0,0,0,0) for i in range(num_runs)]
self.name_html = ''
self.column_html = ['' for i in range(num_runs)]
+def buildGroupResultHeader(p):
+ if p.hangnr > 0:
+ status = 'hang'
+ elif p.timeoutnr > 0:
+ status = 'timeout'
+ elif p.crashnr > 0:
+ status = 'crash'
+ elif p.failnr > 0:
+ status = 'fail'
+ elif p.passnr > 0:
+ status = 'pass'
+ else:
+ status = 'skip'
+
+ totalnr = p.totalRun()
+ passnr = p.passnr
+
+ return '<div class="head %(status)s">%(pass)d/%(total)d</div>' % {
+ 'status': status, 'total': p.totalRun(), 'pass': p.passnr }
+
def buildTable(run_names, results):
# If the test list is empty, just return now.
- if not results:
- return ('', [''])
+ if not results:
+ return ('', [''])
num_runs = len(run_names)
- last_group = ''
- stack = []
+ last_group = ''
+ stack = []
- def openGroup(name):
- stack.append(StackEntry(num_runs, name))
+ def openGroup(name):
+ stack.append(StackEntry(num_runs, name))
- def closeGroup():
- group = stack.pop()
+ def closeGroup():
+ group = stack.pop()
- stack[-1].name_html += ''.join(['<div class="group"><div class="head">', group.name, '</div><div class="groupbody">', group.name_html, '</div></div>'])
+ stack[-1].name_html += ''.join(['<div class="group"><div class="head">', group.name, '</div><div class="groupbody">', group.name_html, '</div></div>'])
- for i in range(num_runs):
- stack[-1].results[i].add(group.results[i])
- stack[-1].column_html[i] += ''.join(['<div class="group">', buildGroupResultHeader(group.results[i]), group.column_html[i], '</div>'])
+ for i in range(num_runs):
+ stack[-1].results[i].add(group.results[i])
+ stack[-1].column_html[i] += ''.join(['<div class="group">', buildGroupResultHeader(group.results[i]), group.column_html[i], '</div>'])
- openGroup('fake')
- openGroup('All')
+ openGroup('fake')
+ openGroup('All')
- for full_test in results.keys():
- group, test = path.split(full_test) # or full_test.rpartition('/')
+ for full_test in sorted(results.keys()):
+ group, test = path.split(full_test) # or full_test.rpartition('/')
- if group != last_group:
- # We're in a different group now. Close the old ones
- # and open the new ones.
- for x in path.relpath(group, last_group).split('/'):
- if x == '..':
- closeGroup()
- else:
- openGroup(x)
+ if group != last_group:
+ # We're in a different group now. Close the old ones
+ # and open the new ones.
+ for x in path.relpath(group, last_group).split('/'):
+ if x == '..':
+ closeGroup()
+ else:
+ openGroup(x)
- last_group = group
+ last_group = group
- # Add the current test
- stack[-1].name_html += '<div>' + test + '</div>\n';
- for i in range(num_runs):
- passv, html = testResult(summary, summary.testruns[i], full_test, summary.results[full_test][i])
- stack[-1].results[i].add(passv)
- stack[-1].column_html[i] += html
+ # Add the current test
+ stack[-1].name_html += '<div>' + test + '</div>\n';
+ for i in range(num_runs):
+ passv = toPassVector(results[full_test][i])
+ html = testResult(run_names[i], full_test, results[full_test][i])
+ stack[-1].results[i].add(passv)
+ stack[-1].column_html[i] += html
- # Close any remaining groups
- while len(stack) > 1:
- closeGroup()
+ # Close any remaining groups
+ while len(stack) > 1:
+ closeGroup()
- assert(len(stack) == 1)
+ assert(len(stack) == 1)
- return (stack[0].name_html, stack[0].column_html)
+ return (stack[0].name_html, stack[0].column_html)
-def writeSummaryHtml():
- names, columns = buildTable(...)
+def writeSummaryHtml(run_names, results, reportDir):
+ names, columns = buildTable(run_names, results)
- #def makeColumn(name, contents):
- #return ''.join(['<div class="resultColumn"><div class="title"><b>%s</b><br/>(<a href="%s/index.html">info</a>)</div>' % (name, escape(name)), contents, '</div>'])
+ def makeColumn(name, contents):
+ return ''.join(['<div class="resultColumn"><a class="title" href="%s/index.html">%s</a>' % (escape(name), name), contents, '</div>'])
- #column_html = ''.join([makeColumn(name, contents) for name, contents in zip(run_names, columns)])
+ column_html = ''.join([makeColumn(name, contents) for name, contents in zip(run_names, columns)])
+ group = '<div class="nameColumn"><a class="title" href="%s/index.html">%(name)s</a>' + names + '</div>'
+ writefile(path.join(reportDir, 'index.html'), templates['index'] % {
+ 'page': 'Your face',
+ 'showlinks': 'Navbar',
+ 'group': group,
+ 'columns': column_html
+ })
#############################################################################
##### Main program
@@ -137,13 +219,14 @@ def main(argv, config):
run_names = list(args.runs)
results = db.getResults(run_names)
+ #print(results)
+
# XXX: write detail pages
- print(results)
- #os.link(path.join(templateDir, 'index.css'),
- #path.join(reportDir, 'index.css'))
- writeSummaryHtml()
+ os.link(path.join(templateDir, 'index.css'),
+ path.join(reportDir, 'index.css'))
+ writeSummaryHtml(run_names, results, reportDir)
if __name__ == "__main__":
- main()
+ main()
diff --git a/programs/templates/index.css b/programs/templates/index.css
new file mode 100644
index 0000000..4133d33
--- /dev/null
+++ b/programs/templates/index.css
@@ -0,0 +1,85 @@
+div#table {
+ margin-left: 1.75em;
+ margin-right: 1.75em;
+}
+
+div#tablebody {
+ display: box;
+ box-orient: horizontal;
+ display: -moz-box;
+ -moz-box-orient: horizontal;
+ width: 100%;
+}
+
+div.nameColumn {
+ box-flex: 1;
+ -moz-box-flex: 1;
+}
+
+div.nameColumn a.title {
+ visibility: hidden;
+}
+
+div.groupbody {
+ margin-left: 1.5em;
+ padding: 0;
+}
+
+/* column header: test run name */
+div.resultColumn {
+ text-align: right;
+}
+
+div.resultColumn a {
+ display: block;
+ padding: 4pt;
+}
+
+a.title {
+ display: block;
+ font-weight: bold;
+ color: black;
+ background-color: #c8c838;
+ padding: 4pt;
+}
+
+div.head {
+ font-weight: bold;
+ background-color: #c8c838;
+ padding: 4pt;
+}
+
+div.group {
+ white-space: nowrap;
+}
+
+/* Normal test names (as opposed to subgroups) */
+div.groupbody > div:not([class]) {
+ padding: 4pt;
+}
+
+.skip, .timeout, .fail, .pass {
+}
+
+.crash {
+ color: #ffffff;
+}
+
+div.groupbody > div:not([class]):nth-child(odd) { background-color: #ffff95 }
+div.groupbody > div:not([class]):nth-child(even) { background-color: #e1e183 }
+
+div.group .pass:nth-child(odd) { background-color: #20ff20; }
+div.group .pass:nth-child(even) { background-color: #15e015; }
+
+div.group .skip:nth-child(odd) { background-color: #b0b0b0; }
+div.group .skip:nth-child(even) { background-color: #a0a0a0; }
+
+div.group .timeout:nth-child(odd) { background-color: #ff9020; }
+div.group .timeout:nth-child(even) { background-color: #ef8010; }
+
+div.group .fail:nth-child(odd) { background-color: #ff2020; }
+div.group .fail:nth-child(even) { background-color: #e00505; }
+
+div.group .crash:nth-child(odd) { background-color: #111111; }
+div.group .crash:nth-child(even) { background-color: #000000; }
+
diff --git a/programs/templates/index.html b/programs/templates/index.html
new file mode 100644
index 0000000..a79e9b6
--- /dev/null
+++ b/programs/templates/index.html
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Result summary </title>
+ <link rel="stylesheet" href="index.css"/>
+ </head>
+ <body>
+ <h1>Result summary</h1>
+ <p>
+ Currently showing: %(page)s
+ </p>
+ <p>
+ Show: %(showlinks)s
+ </p>
+ <div id="table">
+ <div id="tablebody">
+ %(group)s
+ %(columns)s
+ </diV>
+ </div>
+ </body>
+</html>