piglit-summary-html.py 9.87 KB
Newer Older
1
#!/usr/bin/env python
Nicolai Hähnle's avatar
Nicolai Hähnle committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#
# 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
18
# PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHOR(S) BE
Nicolai Hähnle's avatar
Nicolai Hähnle committed
19 20 21 22 23 24
# 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
Nicolai Hähnle's avatar
Nicolai Hähnle committed
25
import cgi
26
import os, os.path
Nicolai Hähnle's avatar
Nicolai Hähnle committed
27 28
import sys

29
sys.path.append(os.path.dirname(os.path.realpath(sys.argv[0])))
Nicolai Hähnle's avatar
Nicolai Hähnle committed
30
import framework.core as core
31
import framework.summary
Nicolai Hähnle's avatar
Nicolai Hähnle committed
32

33

Nicolai Hähnle's avatar
Nicolai Hähnle committed
34 35 36 37 38
#############################################################################
##### Auxiliary functions
#############################################################################

def testPathToHtmlFilename(path):
39
	return 'test_' + filter(lambda s: s.isalnum() or s == '_', path.replace('/', '__')) + '.html'
Nicolai Hähnle's avatar
Nicolai Hähnle committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53


#############################################################################
##### HTML output
#############################################################################

def readfile(filename):
	f = open(filename, "r")
	s = f.read()
	f.close()
	return s

def writefile(filename, text):
	f = open(filename, "w")
54
	f.write(text.encode('utf-8'))
Nicolai Hähnle's avatar
Nicolai Hähnle committed
55 56
	f.close()

57
templatedir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates')
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
Result = readfile(os.path.join(templatedir, 'result.html'))
ResultDetail = readfile(os.path.join(templatedir, 'result_detail.html'))
ResultList = readfile(os.path.join(templatedir, 'result_list.html'))
ResultListItem = readfile(os.path.join(templatedir, 'result_listitem.html'))
ResultMString = readfile(os.path.join(templatedir, 'result_mstring.html'))

Index = readfile(os.path.join(templatedir, 'index.html'))
IndexTestrun = readfile(os.path.join(templatedir, 'index_testrun.html'))
IndexTestrunB = readfile(os.path.join(templatedir, 'index_testrunb.html'))
IndexTestrunBHref = readfile(os.path.join(templatedir, 'index_testrunb_href.html'))
IndexGroup = readfile(os.path.join(templatedir, 'index_group.html'))
IndexGroupTestrun = readfile(os.path.join(templatedir, 'index_group_testrun.html'))
IndexGroupGroup = readfile(os.path.join(templatedir, 'index_groupgroup.html'))
IndexTest = readfile(os.path.join(templatedir, 'index_test.html'))
IndexTestTestrun = readfile(os.path.join(templatedir, 'index_test_testrun.html'))

Testrun = readfile(os.path.join(templatedir, 'testrun.html'))
75

Nicolai Hähnle's avatar
Nicolai Hähnle committed
76 77 78
SummaryPages = {
	'all': 'index.html',
	'changes': 'changes.html',
79
	'problems': 'problems.html',
80 81
	'regressions': 'regressions.html',
	'fixes': 'fixes.html'
Nicolai Hähnle's avatar
Nicolai Hähnle committed
82 83
}

84 85 86
def buildResultListItem(detail):
	return ResultListItem % { 'detail': buildDetailValue(detail) }

Nicolai Hähnle's avatar
Nicolai Hähnle committed
87 88
def buildDetailValue(detail):
	if type(detail) == list:
89 90
		items = map(buildResultListItem, detail)
		return ResultList % { 'items': "".join(items) }
Nicolai Hähnle's avatar
Nicolai Hähnle committed
91

92
	elif isinstance(detail, basestring):
93
		return ResultMString % { 'detail': cgi.escape(detail) }
Nicolai Hähnle's avatar
Nicolai Hähnle committed
94

Nicolai Hähnle's avatar
Nicolai Hähnle committed
95
	return cgi.escape(str(detail))
Nicolai Hähnle's avatar
Nicolai Hähnle committed
96 97 98 99 100


def buildDetails(testResult):
	details = []
	for name in testResult:
101 102 103
		assert(isinstance(name, basestring))

		if name == 'result':
Nicolai Hähnle's avatar
Nicolai Hähnle committed
104 105 106
			continue

		value = buildDetailValue(testResult[name])
107
		details += [(name, value)]
Nicolai Hähnle's avatar
Nicolai Hähnle committed
108

109
	details.sort(lambda a, b: len(a[1])-len(b[1]))
Nicolai Hähnle's avatar
Nicolai Hähnle committed
110 111 112

	text = ''
	alternate = 'a'
113
	for name, value in details:
Nicolai Hähnle's avatar
Nicolai Hähnle committed
114 115 116 117 118 119 120 121 122 123
		text += ResultDetail % locals()

		if alternate == 'a':
			alternate = 'b'
		else:
			alternate = 'a'

	return text


124 125 126
def writeResultHtml(test, testResult, filename):
	path = test.path
	name = test.name
Nicolai Hähnle's avatar
Nicolai Hähnle committed
127 128 129 130 131 132 133 134 135 136 137
	status = testResult.status

	if 'result' in testResult:
		result = testResult['result']
	else:
		result = '?'

	details = buildDetails(testResult)

	writefile(filename, Result % locals())

138
def writeTestrunHtml(testrun, filename):
139 140 141 142 143 144 145
	detail_keys = [
		key
		for key in testrun.__dict__.keys()
		if key in testrun.serialized_keys
		if key != 'tests'
		]
	detaildict = dict([(k, testrun.__dict__[k]) for k in detail_keys])
146 147 148 149 150
	details = buildDetails(detaildict)
	name = testrun.name
	codename = testrun.codename

	writefile(filename, Testrun % locals())
Nicolai Hähnle's avatar
Nicolai Hähnle committed
151

152 153 154 155 156 157
def hrefFromParts(codename, path):
	outStr = codename + '/' + testPathToHtmlFilename(path)
	if outStr[0] == '/':
		outStr = outStr[1:]
	return outStr

158
def buildTestSummary(indent, alternate, testsummary):
Nicolai Hähnle's avatar
Nicolai Hähnle committed
159
	tenindent = 10 - indent
160 161
	path = testsummary.path
	name = testsummary.name
Nicolai Hähnle's avatar
Nicolai Hähnle committed
162 163
	testruns = "".join([IndexTestTestrun % {
		'alternate': alternate,
164
		'status': result.status,
165
		'link': hrefFromParts(result.testrun.codename, path)
166
	} for result in testsummary.results])
Nicolai Hähnle's avatar
Nicolai Hähnle committed
167 168 169 170

	return IndexTest % locals()


171 172 173 174 175
def buildGroupSummaryTestrun(groupresult):
	passnr = groupresult.passvector.passnr
	warnnr = groupresult.passvector.warnnr
	failnr = groupresult.passvector.failnr
	skipnr = groupresult.passvector.skipnr
176
	crashnr = groupresult.passvector.crashnr
177
	totalnr = passnr + warnnr + failnr + crashnr # do not count skips
178

179
	if crashnr > 0:
180 181
		status = 'crash'
	elif failnr > 0:
Nicolai Hähnle's avatar
Nicolai Hähnle committed
182 183 184 185 186 187 188 189 190 191 192
		status = 'fail'
	elif warnnr > 0:
		status = 'warn'
	elif passnr > 0:
		status = 'pass'
	else:
		status = 'skip'

	return IndexGroupTestrun % locals()


193
def buildGroupSummary(indent, groupsummary, showcurrent):
Nicolai Hähnle's avatar
Nicolai Hähnle committed
194 195 196 197
	tenindent = 10 - indent

	items = ''
	alternate = 'a'
198 199 200
	path = groupsummary.path
	name = groupsummary.name
	names = groupsummary.children.keys()
Nicolai Hähnle's avatar
Nicolai Hähnle committed
201 202

	if showcurrent == 'changes':
203
		names = filter(lambda n: groupsummary.children[n].changes, names)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
204
	elif showcurrent == 'problems':
205
		names = filter(lambda n: groupsummary.children[n].problems, names)
206 207
	elif showcurrent == 'regressions':
		names = filter(lambda n: groupsummary.children[n].regressions, names)
208 209
	elif showcurrent == 'fixes':
		names = filter(lambda n: groupsummary.children[n].fixes, names)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
210 211 212

	names.sort()
	for n in names:
213 214
		child = groupsummary.children[n]
		if isinstance(child, framework.summary.GroupSummary):
Nicolai Hähnle's avatar
Nicolai Hähnle committed
215
			items = items + IndexGroupGroup % {
216
				'group': buildGroupSummary(indent+1, child, showcurrent)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
217 218
			}
		else:
219
			items = items + buildTestSummary(indent+1, alternate, child)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
220 221 222 223 224 225

		if alternate == 'a':
			alternate = 'b'
		else:
			alternate = 'a'

226 227
	testruns = "".join([buildGroupSummaryTestrun(result)
			for result in groupsummary.results])
Nicolai Hähnle's avatar
Nicolai Hähnle committed
228 229 230 231

	return IndexGroup % locals()


232
def writeSummaryHtml(summary, summaryDir, showcurrent):
Nicolai Hähnle's avatar
Nicolai Hähnle committed
233 234 235 236 237 238 239 240 241 242
	"""\
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()

243 244 245 246 247 248
	def testrunb(tr):
		if 'href' in tr.__dict__:
			return IndexTestrunBHref % tr.__dict__
		else:
			return IndexTestrunB % tr.__dict__

249 250
	group = buildGroupSummary(1, summary.root, showcurrent)
	testruns = "".join([IndexTestrun % tr.__dict__ for tr in summary.testruns])
251
	testrunsb = "".join([testrunb(tr) for tr in summary.testruns])
Nicolai Hähnle's avatar
Nicolai Hähnle committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

	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
270
  -l, --list=listfile   Use test results from a list file
Nicolai Hähnle's avatar
Nicolai Hähnle committed
271 272 273

Example:
  %(progName)s summary/mysum results/all.results
274 275 276 277 278 279

Example list file:
[
	[ 'test.result', { name: 'override-name' } ],
	[ 'other.result' ]
]
Nicolai Hähnle's avatar
Nicolai Hähnle committed
280 281
"""
	print USAGE % {'progName': sys.argv[0]}
282
	sys.exit(1)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
283 284


285 286 287 288 289 290
def parse_listfile(filename):
	file = open(filename, "r")
	code = "".join([s for s in file])
	file.close()
	return eval(code)

291
def loadresult(descr):
292
	result = core.loadTestResults(descr[0])
293 294 295 296
	if len(descr) > 1:
		result.__dict__.update(descr[1])
	return result

Nicolai Hähnle's avatar
Nicolai Hähnle committed
297 298
def main():
	try:
299
		options, args = getopt(sys.argv[1:], "hofl:", [ "help", "overwrite", "list" ])
Nicolai Hähnle's avatar
Nicolai Hähnle committed
300 301 302 303
	except GetoptError:
		usage()

	OptionOverwrite = False
304
	OptionList = []
305
	for name, value in options:
Nicolai Hähnle's avatar
Nicolai Hähnle committed
306 307 308 309
		if name == "-h" or name == "--help":
			usage()
		elif name == "-o" or name == "--overwrite":
			OptionOverwrite = True
310 311 312 313
		elif name == "-l" or name == "--list":
			OptionList += parse_listfile(value)

	OptionList += [[name] for name in args[1:]]
Nicolai Hähnle's avatar
Nicolai Hähnle committed
314

315
	if len(args) < 1 or len(OptionList) == 0:
Nicolai Hähnle's avatar
Nicolai Hähnle committed
316 317 318 319 320
		usage()

	summaryDir = args[0]
	core.checkDir(summaryDir, not OptionOverwrite)

321 322
	results = []
	for result_dir in OptionList:
323
		results.append(loadresult(result_dir))
Nicolai Hähnle's avatar
Nicolai Hähnle committed
324

325 326 327 328 329 330
	summary = framework.summary.Summary(results)
	for j in range(len(summary.testruns)):
		tr = summary.testruns[j]
		tr.codename = filter(lambda s: s.isalnum(), tr.name)
		dirname = summaryDir + '/' + tr.codename
		core.checkDir(dirname, False)
331
		writeTestrunHtml(tr, dirname + '/index.html')
332
		for test in summary.allTests():
333
			filename = dirname + '/' + testPathToHtmlFilename(test.path)
334
			writeResultHtml(test, test.results[j], filename)
Nicolai Hähnle's avatar
Nicolai Hähnle committed
335

336 337
	writefile(os.path.join(summaryDir, 'result.css'), readfile(os.path.join(templatedir, 'result.css')))
	writefile(os.path.join(summaryDir, 'index.css'), readfile(os.path.join(templatedir, 'index.css')))
338 339 340
	writeSummaryHtml(summary, summaryDir, 'all')
	writeSummaryHtml(summary, summaryDir, 'problems')
	writeSummaryHtml(summary, summaryDir, 'changes')
341
	writeSummaryHtml(summary, summaryDir, 'regressions')
342
	writeSummaryHtml(summary, summaryDir, 'fixes')
Nicolai Hähnle's avatar
Nicolai Hähnle committed
343 344 345 346


if __name__ == "__main__":
	main()