summaryrefslogtreecommitdiff
path: root/piglit-summary-html.py
diff options
context:
space:
mode:
authorNicolai Hähnle <nh@annarchy.freedesktop.org>2007-03-24 17:20:31 -0700
committerNicolai Hähnle <nh@annarchy.freedesktop.org>2007-03-24 17:20:31 -0700
commit22fbbb5e8679ddf91532cb10eaa31be533383b48 (patch)
treecb43f83fe997f166f297dad671ddf04e2f601be4 /piglit-summary-html.py
Initial commit
Diffstat (limited to 'piglit-summary-html.py')
-rwxr-xr-xpiglit-summary-html.py414
1 files changed, 414 insertions, 0 deletions
diff --git a/piglit-summary-html.py b/piglit-summary-html.py
new file mode 100755
index 000000000..edad4f673
--- /dev/null
+++ b/piglit-summary-html.py
@@ -0,0 +1,414 @@
+#!/usr/bin/python
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# This permission notice shall be included in all copies or
+# substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from getopt import getopt, GetoptError
+import errno
+import os
+import sys
+
+import framework.core as core
+
+#############################################################################
+##### Auxiliary functions
+#############################################################################
+
+def testPathToHtmlFilename(path):
+ return filter(lambda s: s.isalnum() or s == '_', path.replace('/', '__')) + '.html'
+
+
+#############################################################################
+##### Annotation preprocessing
+#############################################################################
+
+class PassVector:
+ def __init__(self,p,w,f,s):
+ self.passnr = p
+ self.warnnr = w
+ self.failnr = f
+ self.skipnr = s
+
+ def add(self,o):
+ self.passnr += o.passnr
+ self.warnnr += o.warnnr
+ self.failnr += o.failnr
+ self.skipnr += o.skipnr
+
+def annotateOneTest(path, results):
+ """\
+Result is an array containing one test result.
+"""
+ result = ''
+ if 'result' in results:
+ result = results['result']
+
+ vectormap = {
+ 'pass': PassVector(1,0,0,0),
+ 'warn': PassVector(0,1,0,0),
+ 'fail': PassVector(0,0,1,0),
+ 'skip': PassVector(0,0,0,1)
+ }
+
+ if result not in vectormap:
+ result = 'warn'
+
+ results.status = result
+ results.passvector = vectormap[result]
+ results.path = path
+
+ return results
+
+
+def annotateTest(path, results):
+ """\
+Result is an array containing corresponding test results, one per test run
+"""
+ for j in range(len(results)):
+ results[j] = annotateOneTest(path, results[j])
+
+ stati = set([r.status for r in results])
+ changes = len(stati) > 1
+ problems = len(stati - set(['pass', 'skip'])) > 0
+ for r in results:
+ r.changes = changes
+ r.problems = problems
+
+
+def annotateGroup(path, results):
+ """\
+Result is an array containing corresponding GroupResults, one per test run
+"""
+ groupnames = set()
+ testnames = set()
+
+ changes = False
+ problems = False
+
+ for r in results:
+ r.passvector = PassVector(0,0,0,0)
+ r.path = path
+
+ for name in r:
+ if type(name) != str:
+ continue
+
+ if isinstance(r[name], core.GroupResult):
+ groupnames.add(name)
+ else:
+ testnames.add(name)
+
+ for name in groupnames:
+ children = []
+
+ for r in results:
+ if name not in r:
+ r[name] = core.GroupResult()
+
+ children.append(r[name])
+
+ spath = name
+ if len(path) > 0:
+ spath = path + '/' + spath
+
+ annotateGroup(spath, children)
+
+ changes = changes or results[0][name].changes
+ problems = problems or results[0][name].problems
+
+ for r in results:
+ r.passvector.add(r[name].passvector)
+
+
+ for name in testnames:
+ children = []
+
+ for r in results:
+ if name not in r:
+ r[name] = core.TestResult({}, { 'result': 'skip' })
+
+ # BACKWARDS COMPATIBILITY
+ if not isinstance(r[name], core.TestResult):
+ r[name] = core.TestResult({}, r[name])
+ # END BACKWARDS COMPATIBILITY
+
+ children.append(r[name])
+
+ spath = name
+ if len(path) > 0:
+ spath = path + '/' + spath
+
+ annotateTest(spath, children)
+
+ changes = changes or results[0][name].changes
+ problems = problems or results[0][name].problems
+
+ for r in results:
+ r.passvector.add(r[name].passvector)
+
+ for r in results:
+ r.changes = changes
+ r.problems = problems
+
+
+#############################################################################
+##### HTML output
+#############################################################################
+
+def readfile(filename):
+ f = open(filename, "r")
+ s = f.read()
+ f.close()
+ return s
+
+def writefile(filename, text):
+ f = open(filename, "w")
+ f.write(text)
+ f.close()
+
+Result = readfile('templates/result.html')
+ResultDetail = readfile('templates/result_detail.html')
+ResultList = readfile('templates/result_list.html')
+ResultListItem = readfile('templates/result_listitem.html')
+ResultMString = readfile('templates/result_mstring.html')
+
+Index = readfile('templates/index.html')
+IndexTestrun = readfile('templates/index_testrun.html')
+IndexTestrunB = readfile('templates/index_testrunb.html')
+IndexGroup = readfile('templates/index_group.html')
+IndexGroupTestrun = readfile('templates/index_group_testrun.html')
+IndexGroupGroup = readfile('templates/index_groupgroup.html')
+IndexTest = readfile('templates/index_test.html')
+IndexTestTestrun = readfile('templates/index_test_testrun.html')
+
+SummaryPages = {
+ 'all': 'index.html',
+ 'changes': 'changes.html',
+ 'problems': 'problems.html'
+}
+
+def buildDetailValue(detail):
+ if type(detail) == list:
+ items = ''
+
+ for d in detail:
+ items = items + ResultListItem % { 'detail': buildDetailValue(d) }
+
+ return ResultList % { 'items': items }
+ elif type(detail) == str and detail[0:3] == '@@@':
+ return ResultMString % { 'detail': detail[3:] }
+
+ return str(detail)
+
+
+def buildDetails(testResult):
+ details = []
+ for name in testResult:
+ if type(name) != str or name == 'result':
+ continue
+
+ value = buildDetailValue(testResult[name])
+ details += [(name,value)]
+
+ details.sort(lambda a,b: len(a[1])-len(b[1]))
+
+ text = ''
+ alternate = 'a'
+ for name,value in details:
+ text += ResultDetail % locals()
+
+ if alternate == 'a':
+ alternate = 'b'
+ else:
+ alternate = 'a'
+
+ return text
+
+
+def writeResultHtml(testResult, filename):
+ path = testResult.path
+ name = testResult.path.split('/')[-1]
+ status = testResult.status
+
+ if 'result' in testResult:
+ result = testResult['result']
+ else:
+ result = '?'
+
+ details = buildDetails(testResult)
+
+ writefile(filename, Result % locals())
+
+
+def recursiveWriteResultHtml(results, summaryDir):
+ for n in results:
+ if type(n) != str:
+ continue
+
+ if isinstance(results[n], core.GroupResult):
+ recursiveWriteResultHtml(results[n], summaryDir)
+ else:
+ writeResultHtml(results[n], summaryDir + '/' + testPathToHtmlFilename(results[n].path))
+
+
+def buildTestSummary(indent, alternate, name, test):
+ tenindent = 10 - indent
+ testruns = "".join([IndexTestTestrun % {
+ 'alternate': alternate,
+ 'status': t.status,
+ 'link': r.codename + '/' + testPathToHtmlFilename(t.path)
+ } for r,t in test])
+
+ return IndexTest % locals()
+
+
+def buildGroupSummaryTestrun(results, group):
+ passnr = group.passvector.passnr
+ warnnr = group.passvector.warnnr
+ failnr = group.passvector.failnr
+ skipnr = group.passvector.skipnr
+ totalnr = passnr + warnnr + failnr # do not count skips
+
+ if failnr > 0:
+ status = 'fail'
+ elif warnnr > 0:
+ status = 'warn'
+ elif passnr > 0:
+ status = 'pass'
+ else:
+ status = 'skip'
+
+ return IndexGroupTestrun % locals()
+
+
+def buildGroupSummary(indent, name, results, showcurrent):
+ """\
+testruns is an array of pairs (results,group), where results is the
+entire testrun record and group is the group we're currently printing.
+"""
+ tenindent = 10 - indent
+
+ items = ''
+ alternate = 'a'
+ names = filter(lambda k: type(k) == str, results[0][1])
+
+ if showcurrent == 'changes':
+ names = filter(lambda n: results[0][1][n].changes, names)
+ elif showcurrent == 'problems':
+ names = filter(lambda n: results[0][1][n].problems, names)
+
+ names.sort()
+ for n in names:
+ if isinstance(results[0][1][n], core.GroupResult):
+ items = items + IndexGroupGroup % {
+ 'group': buildGroupSummary(indent+1, n, [(r,g[n]) for r,g in results], showcurrent)
+ }
+ else:
+ items = items + buildTestSummary(indent+1, alternate, n, [(r,g[n]) for r,g in results])
+
+ if alternate == 'a':
+ alternate = 'b'
+ else:
+ alternate = 'a'
+
+ testruns = "".join([buildGroupSummaryTestrun(r,g) for r,g in results])
+
+ return IndexGroup % locals()
+
+
+def writeSummaryHtml(results, summaryDir, showcurrent):
+ """\
+results is an array containing the top-level results dictionarys.
+"""
+ def link(to):
+ if to == showcurrent:
+ return to
+ else:
+ page = SummaryPages[to]
+ return '<a href="%(page)s">%(to)s</a>' % locals()
+
+ group = buildGroupSummary(1, 'Total', [(r,r.results) for r in results], showcurrent)
+ testruns = "".join([IndexTestrun % r.__dict__ for r in results])
+ testrunsb = "".join([IndexTestrunB % r.__dict__ for r in results])
+
+ tolist = SummaryPages.keys()
+ tolist.sort()
+ showlinks = " | ".join([link(to) for to in tolist])
+
+ writefile(summaryDir + '/' + SummaryPages[showcurrent], Index % locals())
+
+
+#############################################################################
+##### Main program
+#############################################################################
+def usage():
+ USAGE = """\
+Usage: %(progName)s [options] [summary-dir] [test.results]...
+
+Options:
+ -h, --help Show this message
+ -o, --overwrite Overwrite existing directories
+
+Example:
+ %(progName)s summary/mysum results/all.results
+"""
+ print USAGE % {'progName': sys.argv[0]}
+ exit(1)
+
+
+def main():
+ try:
+ options, args = getopt(sys.argv[1:], "ho", [ "help", "overwrite" ])
+ except GetoptError:
+ usage()
+
+ OptionOverwrite = False
+ for name,value in options:
+ if name == "-h" or name == "--help":
+ usage()
+ elif name == "-o" or name == "--overwrite":
+ OptionOverwrite = True
+
+ if len(args) < 2:
+ usage()
+
+ summaryDir = args[0]
+ resultFilenames = args[1:]
+
+ core.checkDir(summaryDir, not OptionOverwrite)
+
+ results = [core.loadTestResults(name) for name in resultFilenames]
+
+ annotateGroup('', [r.results for r in results])
+ for r in results:
+ r.codename = filter(lambda s: s.isalnum(), r.name)
+ core.checkDir(summaryDir + '/' + r.codename, False)
+ recursiveWriteResultHtml(r.results, summaryDir + '/' + r.codename)
+
+ writefile(summaryDir + '/result.css', readfile('templates/result.css'))
+ writefile(summaryDir + '/index.css', readfile('templates/index.css'))
+ writeSummaryHtml(results, summaryDir, 'all')
+ writeSummaryHtml(results, summaryDir, 'problems')
+ writeSummaryHtml(results, summaryDir, 'changes')
+
+
+if __name__ == "__main__":
+ main()