1# -*- coding: utf-8 -*-
2
3import os
4import sys
5import codecs
6import xml.dom.minidom
7import xml.sax
8import xml.sax.handler
9from log_parser import BatchResultParser, StatusCode
10
11STYLESHEET_FILENAME = "testlog.xsl"
12LOG_VERSION			= '0.3.2'
13
14class BuildXMLLogHandler(xml.sax.handler.ContentHandler):
15	def __init__ (self, doc):
16		self.doc			= doc
17		self.elementStack	= []
18		self.rootElements	= []
19
20	def getRootElements (self):
21		return self.rootElements
22
23	def pushElement (self, elem):
24		if len(self.elementStack) == 0:
25			self.rootElements.append(elem)
26		else:
27			self.getCurElement().appendChild(elem)
28		self.elementStack.append(elem)
29
30	def popElement (self):
31		self.elementStack.pop()
32
33	def getCurElement (self):
34		if len(self.elementStack) > 0:
35			return self.elementStack[-1]
36		else:
37			return None
38
39	def startDocument (self):
40		pass
41
42	def endDocument (self):
43		pass
44
45	def startElement (self, name, attrs):
46		elem = self.doc.createElement(name)
47		for name in attrs.getNames():
48			value = attrs.getValue(name)
49			elem.setAttribute(name, value)
50		self.pushElement(elem)
51
52	def endElement (self, name):
53		self.popElement()
54
55	def characters (self, content):
56		# Discard completely empty content
57		if len(content.strip()) == 0:
58			return
59
60		# Append as text node (not pushed to stack)
61		if self.getCurElement() != None:
62			txt = self.doc.createTextNode(content)
63			self.getCurElement().appendChild(txt)
64
65class LogErrorHandler(xml.sax.handler.ErrorHandler):
66	def __init__ (self):
67		pass
68
69	def error (self, err):
70		#print "error(%s)" % str(err)
71		pass
72
73	def fatalError (self, err):
74		#print "fatalError(%s)" % str(err)
75		pass
76
77	def warning (self, warn):
78		#print "warning(%s)" % str(warn)
79		pass
80
81def findFirstElementByName (nodes, name):
82	for node in nodes:
83		if node.nodeName == name:
84			return node
85		chFound = findFirstElementByName(node.childNodes, name)
86		if chFound != None:
87			return chFound
88	return None
89
90# Normalizes potentially broken (due to crash for example) log data to XML element tree
91def normalizeToXml (result, doc):
92	handler		= BuildXMLLogHandler(doc)
93	errHandler	= LogErrorHandler()
94
95	xml.sax.parseString(result.log, handler, errHandler)
96
97	rootNodes = handler.getRootElements()
98
99	# Check if we have TestCaseResult
100	testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult')
101	if testCaseResult == None:
102		# Create TestCaseResult element
103		testCaseResult = doc.createElement('TestCaseResult')
104		testCaseResult.setAttribute('CasePath', result.name)
105		testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable..
106		testCaseResult.setAttribute('Version', LOG_VERSION)
107		rootNodes.append(testCaseResult)
108
109	# Check if we have Result element
110	resultElem = findFirstElementByName(rootNodes, 'Result')
111	if resultElem == None:
112		# Create result element
113		resultElem = doc.createElement('Result')
114		resultElem.setAttribute('StatusCode', result.statusCode)
115		resultElem.appendChild(doc.createTextNode(result.statusDetails))
116		testCaseResult.appendChild(resultElem)
117
118	return rootNodes
119
120def logToXml (inFile, outFile):
121	parser	= BatchResultParser()
122	results	= parser.parseFile(inFile)
123
124	dstDoc			= xml.dom.minidom.Document()
125	batchResultNode	= dstDoc.createElement('BatchResult')
126	batchResultNode.setAttribute("FileName", os.path.basename(inFile))
127
128	dstDoc.appendChild(batchResultNode)
129
130	for result in results:
131		# Normalize log to valid XML
132		rootNodes = normalizeToXml(result, dstDoc)
133		for node in rootNodes:
134			batchResultNode.appendChild(node)
135
136	# Summary
137	countByStatusCode = {}
138	for code in StatusCode.STATUS_CODES:
139		countByStatusCode[code] = 0
140
141	for result in results:
142		countByStatusCode[result.statusCode] += 1
143
144	summaryElem = dstDoc.createElement('ResultTotals')
145	for code in StatusCode.STATUS_CODES:
146		summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
147	summaryElem.setAttribute('All', "%d" % len(results))
148	batchResultNode.appendChild(summaryElem)
149
150	text = dstDoc.toprettyxml()
151
152	out = codecs.open(outFile, "wb", encoding="utf-8")
153
154	# Write custom headers
155	out.write("<?xml version=\"1.0\"?>\n")
156	out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)
157
158	for line in text.splitlines()[1:]:
159		out.write(line)
160		out.write("\n")
161
162	out.close()
163
164if __name__ == "__main__":
165	if len(sys.argv) != 3:
166		print "%s: [test log] [dst file]" % sys.argv[0]
167		sys.exit(-1)
168
169	logToXml(sys.argv[1], sys.argv[2])
170