diff options
author | Kenneth Graunke <kenneth@whitecape.org> | 2012-11-29 23:20:40 -0800 |
---|---|---|
committer | Kenneth Graunke <kenneth@whitecape.org> | 2012-11-29 23:20:40 -0800 |
commit | 7f3801306d91849839f25a15b6dc49b826a924af (patch) | |
tree | e2a5c4e053d53eeba1e742db023707633c2e5932 | |
parent | 5dbc923793ec757afff3b9aebb4cf5b02359baed (diff) |
Reporting actually almost works
-rwxr-xr-x | programs/report.py | 179 | ||||
-rw-r--r-- | programs/templates/index.css | 85 | ||||
-rw-r--r-- | programs/templates/index.html | 25 |
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> |