13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry# -*- coding: utf-8 -*-
23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
33c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport os
43c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport sys
53c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport codecs
63c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.dom.minidom
73c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.sax
83c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.sax.handler
93c827367444ee418f129b2c238299f49d3264554Jarkko Poyryfrom log_parser import BatchResultParser, StatusCode
103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
113c827367444ee418f129b2c238299f49d3264554Jarkko PoyrySTYLESHEET_FILENAME = "testlog.xsl"
123c827367444ee418f129b2c238299f49d3264554Jarkko PoyryLOG_VERSION			= '0.3.2'
133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
143c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass BuildXMLLogHandler(xml.sax.handler.ContentHandler):
153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def __init__ (self, doc):
163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.doc			= doc
173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.elementStack	= []
183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.rootElements	= []
193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def getRootElements (self):
213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		return self.rootElements
223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def pushElement (self, elem):
243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if len(self.elementStack) == 0:
253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			self.rootElements.append(elem)
263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		else:
273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			self.getCurElement().appendChild(elem)
283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.elementStack.append(elem)
293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def popElement (self):
313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.elementStack.pop()
323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def getCurElement (self):
343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if len(self.elementStack) > 0:
353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			return self.elementStack[-1]
363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		else:
373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			return None
383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def startDocument (self):
403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def endDocument (self):
433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def startElement (self, name, attrs):
463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		elem = self.doc.createElement(name)
473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		for name in attrs.getNames():
483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			value = attrs.getValue(name)
493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			elem.setAttribute(name, value)
503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.pushElement(elem)
513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def endElement (self, name):
533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		self.popElement()
543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def characters (self, content):
563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		# Discard completely empty content
573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if len(content.strip()) == 0:
583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			return
593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		# Append as text node (not pushed to stack)
613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if self.getCurElement() != None:
623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			txt = self.doc.createTextNode(content)
633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			self.getCurElement().appendChild(txt)
643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
653c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass LogErrorHandler(xml.sax.handler.ErrorHandler):
663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def __init__ (self):
673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def error (self, err):
703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		#print "error(%s)" % str(err)
713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def fatalError (self, err):
743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		#print "fatalError(%s)" % str(err)
753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	def warning (self, warn):
783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		#print "warning(%s)" % str(warn)
793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		pass
803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
813c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef findFirstElementByName (nodes, name):
823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for node in nodes:
833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if node.nodeName == name:
843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			return node
853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		chFound = findFirstElementByName(node.childNodes, name)
863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		if chFound != None:
873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			return chFound
883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return None
893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry# Normalizes potentially broken (due to crash for example) log data to XML element tree
913c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef normalizeToXml (result, doc):
923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	handler		= BuildXMLLogHandler(doc)
933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	errHandler	= LogErrorHandler()
943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	xml.sax.parseString(result.log, handler, errHandler)
963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	rootNodes = handler.getRootElements()
983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	# Check if we have TestCaseResult
1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult')
1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if testCaseResult == None:
1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		# Create TestCaseResult element
1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		testCaseResult = doc.createElement('TestCaseResult')
1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		testCaseResult.setAttribute('CasePath', result.name)
1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable..
1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		testCaseResult.setAttribute('Version', LOG_VERSION)
1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		rootNodes.append(testCaseResult)
1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	# Check if we have Result element
1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	resultElem = findFirstElementByName(rootNodes, 'Result')
1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if resultElem == None:
1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		# Create result element
1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		resultElem = doc.createElement('Result')
1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		resultElem.setAttribute('StatusCode', result.statusCode)
1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		resultElem.appendChild(doc.createTextNode(result.statusDetails))
1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		testCaseResult.appendChild(resultElem)
1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	return rootNodes
1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef logToXml (inFile, outFile):
1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	parser	= BatchResultParser()
1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	results	= parser.parseFile(inFile)
1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	dstDoc			= xml.dom.minidom.Document()
1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	batchResultNode	= dstDoc.createElement('BatchResult')
1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	batchResultNode.setAttribute("FileName", os.path.basename(inFile))
1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	dstDoc.appendChild(batchResultNode)
1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for result in results:
1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		# Normalize log to valid XML
1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		rootNodes = normalizeToXml(result, dstDoc)
1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		for node in rootNodes:
1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry			batchResultNode.appendChild(node)
1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	# Summary
1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	countByStatusCode = {}
1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for code in StatusCode.STATUS_CODES:
1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		countByStatusCode[code] = 0
1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for result in results:
1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		countByStatusCode[result.statusCode] += 1
1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	summaryElem = dstDoc.createElement('ResultTotals')
1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for code in StatusCode.STATUS_CODES:
1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	summaryElem.setAttribute('All', "%d" % len(results))
1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	batchResultNode.appendChild(summaryElem)
1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	text = dstDoc.toprettyxml()
1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	out = codecs.open(outFile, "wb", encoding="utf-8")
1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	# Write custom headers
1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	out.write("<?xml version=\"1.0\"?>\n")
1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)
1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	for line in text.splitlines()[1:]:
1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		out.write(line)
1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		out.write("\n")
1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	out.close()
1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyryif __name__ == "__main__":
1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	if len(sys.argv) != 3:
1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		print "%s: [test log] [dst file]" % sys.argv[0]
1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry		sys.exit(-1)
1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry
1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry	logToXml(sys.argv[1], sys.argv[2])
170