1e5c31af7Sopenharmony_ci# -*- coding: utf-8 -*-
2e5c31af7Sopenharmony_ci
3e5c31af7Sopenharmony_ci#-------------------------------------------------------------------------
4e5c31af7Sopenharmony_ci# drawElements Quality Program utilities
5e5c31af7Sopenharmony_ci# --------------------------------------
6e5c31af7Sopenharmony_ci#
7e5c31af7Sopenharmony_ci# Copyright 2015 The Android Open Source Project
8e5c31af7Sopenharmony_ci#
9e5c31af7Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
10e5c31af7Sopenharmony_ci# you may not use this file except in compliance with the License.
11e5c31af7Sopenharmony_ci# You may obtain a copy of the License at
12e5c31af7Sopenharmony_ci#
13e5c31af7Sopenharmony_ci#      http://www.apache.org/licenses/LICENSE-2.0
14e5c31af7Sopenharmony_ci#
15e5c31af7Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
16e5c31af7Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
17e5c31af7Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18e5c31af7Sopenharmony_ci# See the License for the specific language governing permissions and
19e5c31af7Sopenharmony_ci# limitations under the License.
20e5c31af7Sopenharmony_ci#
21e5c31af7Sopenharmony_ci#-------------------------------------------------------------------------
22e5c31af7Sopenharmony_ci
23e5c31af7Sopenharmony_ciimport os
24e5c31af7Sopenharmony_ciimport sys
25e5c31af7Sopenharmony_ciimport codecs
26e5c31af7Sopenharmony_ciimport xml.dom.minidom
27e5c31af7Sopenharmony_ciimport xml.sax
28e5c31af7Sopenharmony_ciimport xml.sax.handler
29e5c31af7Sopenharmony_cifrom log_parser import BatchResultParser, StatusCode
30e5c31af7Sopenharmony_ci
31e5c31af7Sopenharmony_ciSTYLESHEET_FILENAME = "testlog.xsl"
32e5c31af7Sopenharmony_ciLOG_VERSION			= '0.3.2'
33e5c31af7Sopenharmony_ci
34e5c31af7Sopenharmony_ciclass BuildXMLLogHandler(xml.sax.handler.ContentHandler):
35e5c31af7Sopenharmony_ci	def __init__ (self, doc):
36e5c31af7Sopenharmony_ci		self.doc			= doc
37e5c31af7Sopenharmony_ci		self.elementStack	= []
38e5c31af7Sopenharmony_ci		self.rootElements	= []
39e5c31af7Sopenharmony_ci
40e5c31af7Sopenharmony_ci	def getRootElements (self):
41e5c31af7Sopenharmony_ci		return self.rootElements
42e5c31af7Sopenharmony_ci
43e5c31af7Sopenharmony_ci	def pushElement (self, elem):
44e5c31af7Sopenharmony_ci		if len(self.elementStack) == 0:
45e5c31af7Sopenharmony_ci			self.rootElements.append(elem)
46e5c31af7Sopenharmony_ci		else:
47e5c31af7Sopenharmony_ci			self.getCurElement().appendChild(elem)
48e5c31af7Sopenharmony_ci		self.elementStack.append(elem)
49e5c31af7Sopenharmony_ci
50e5c31af7Sopenharmony_ci	def popElement (self):
51e5c31af7Sopenharmony_ci		self.elementStack.pop()
52e5c31af7Sopenharmony_ci
53e5c31af7Sopenharmony_ci	def getCurElement (self):
54e5c31af7Sopenharmony_ci		if len(self.elementStack) > 0:
55e5c31af7Sopenharmony_ci			return self.elementStack[-1]
56e5c31af7Sopenharmony_ci		else:
57e5c31af7Sopenharmony_ci			return None
58e5c31af7Sopenharmony_ci
59e5c31af7Sopenharmony_ci	def startDocument (self):
60e5c31af7Sopenharmony_ci		pass
61e5c31af7Sopenharmony_ci
62e5c31af7Sopenharmony_ci	def endDocument (self):
63e5c31af7Sopenharmony_ci		pass
64e5c31af7Sopenharmony_ci
65e5c31af7Sopenharmony_ci	def startElement (self, name, attrs):
66e5c31af7Sopenharmony_ci		elem = self.doc.createElement(name)
67e5c31af7Sopenharmony_ci		for name in attrs.getNames():
68e5c31af7Sopenharmony_ci			value = attrs.getValue(name)
69e5c31af7Sopenharmony_ci			elem.setAttribute(name, value)
70e5c31af7Sopenharmony_ci		self.pushElement(elem)
71e5c31af7Sopenharmony_ci
72e5c31af7Sopenharmony_ci	def endElement (self, name):
73e5c31af7Sopenharmony_ci		self.popElement()
74e5c31af7Sopenharmony_ci
75e5c31af7Sopenharmony_ci	def characters (self, content):
76e5c31af7Sopenharmony_ci		# Discard completely empty content
77e5c31af7Sopenharmony_ci		if len(content.strip()) == 0:
78e5c31af7Sopenharmony_ci			return
79e5c31af7Sopenharmony_ci
80e5c31af7Sopenharmony_ci		# Append as text node (not pushed to stack)
81e5c31af7Sopenharmony_ci		if self.getCurElement() != None:
82e5c31af7Sopenharmony_ci			txt = self.doc.createTextNode(content)
83e5c31af7Sopenharmony_ci			self.getCurElement().appendChild(txt)
84e5c31af7Sopenharmony_ci
85e5c31af7Sopenharmony_ciclass LogErrorHandler(xml.sax.handler.ErrorHandler):
86e5c31af7Sopenharmony_ci	def __init__ (self):
87e5c31af7Sopenharmony_ci		pass
88e5c31af7Sopenharmony_ci
89e5c31af7Sopenharmony_ci	def error (self, err):
90e5c31af7Sopenharmony_ci		#print("error(%s)" % str(err))
91e5c31af7Sopenharmony_ci		pass
92e5c31af7Sopenharmony_ci
93e5c31af7Sopenharmony_ci	def fatalError (self, err):
94e5c31af7Sopenharmony_ci		#print("fatalError(%s)" % str(err))
95e5c31af7Sopenharmony_ci		pass
96e5c31af7Sopenharmony_ci
97e5c31af7Sopenharmony_ci	def warning (self, warn):
98e5c31af7Sopenharmony_ci		#print("warning(%s)" % str(warn))
99e5c31af7Sopenharmony_ci		pass
100e5c31af7Sopenharmony_ci
101e5c31af7Sopenharmony_cidef findFirstElementByName (nodes, name):
102e5c31af7Sopenharmony_ci	for node in nodes:
103e5c31af7Sopenharmony_ci		if node.nodeName == name:
104e5c31af7Sopenharmony_ci			return node
105e5c31af7Sopenharmony_ci		chFound = findFirstElementByName(node.childNodes, name)
106e5c31af7Sopenharmony_ci		if chFound != None:
107e5c31af7Sopenharmony_ci			return chFound
108e5c31af7Sopenharmony_ci	return None
109e5c31af7Sopenharmony_ci
110e5c31af7Sopenharmony_ci# Normalizes potentially broken (due to crash for example) log data to XML element tree
111e5c31af7Sopenharmony_cidef normalizeToXml (result, doc):
112e5c31af7Sopenharmony_ci	handler		= BuildXMLLogHandler(doc)
113e5c31af7Sopenharmony_ci	errHandler	= LogErrorHandler()
114e5c31af7Sopenharmony_ci
115e5c31af7Sopenharmony_ci	xml.sax.parseString(result.log, handler, errHandler)
116e5c31af7Sopenharmony_ci
117e5c31af7Sopenharmony_ci	rootNodes = handler.getRootElements()
118e5c31af7Sopenharmony_ci
119e5c31af7Sopenharmony_ci	# Check if we have TestCaseResult
120e5c31af7Sopenharmony_ci	testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult')
121e5c31af7Sopenharmony_ci	if testCaseResult == None:
122e5c31af7Sopenharmony_ci		# Create TestCaseResult element
123e5c31af7Sopenharmony_ci		testCaseResult = doc.createElement('TestCaseResult')
124e5c31af7Sopenharmony_ci		testCaseResult.setAttribute('CasePath', result.name)
125e5c31af7Sopenharmony_ci		testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable..
126e5c31af7Sopenharmony_ci		testCaseResult.setAttribute('Version', LOG_VERSION)
127e5c31af7Sopenharmony_ci		rootNodes.append(testCaseResult)
128e5c31af7Sopenharmony_ci
129e5c31af7Sopenharmony_ci	# Check if we have Result element
130e5c31af7Sopenharmony_ci	resultElem = findFirstElementByName(rootNodes, 'Result')
131e5c31af7Sopenharmony_ci	if resultElem == None:
132e5c31af7Sopenharmony_ci		# Create result element
133e5c31af7Sopenharmony_ci		resultElem = doc.createElement('Result')
134e5c31af7Sopenharmony_ci		resultElem.setAttribute('StatusCode', result.statusCode)
135e5c31af7Sopenharmony_ci		resultElem.appendChild(doc.createTextNode(result.statusDetails))
136e5c31af7Sopenharmony_ci		testCaseResult.appendChild(resultElem)
137e5c31af7Sopenharmony_ci
138e5c31af7Sopenharmony_ci	return rootNodes
139e5c31af7Sopenharmony_ci
140e5c31af7Sopenharmony_cidef logToXml (logFilePath, outFilePath):
141e5c31af7Sopenharmony_ci	# Initialize Xml Document
142e5c31af7Sopenharmony_ci	dstDoc = xml.dom.minidom.Document()
143e5c31af7Sopenharmony_ci	batchResultNode	= dstDoc.createElement('BatchResult')
144e5c31af7Sopenharmony_ci	batchResultNode.setAttribute("FileName", os.path.basename(logFilePath))
145e5c31af7Sopenharmony_ci	dstDoc.appendChild(batchResultNode)
146e5c31af7Sopenharmony_ci
147e5c31af7Sopenharmony_ci	# Initialize dictionary for counting status codes
148e5c31af7Sopenharmony_ci	countByStatusCode = {}
149e5c31af7Sopenharmony_ci	for code in StatusCode.STATUS_CODES:
150e5c31af7Sopenharmony_ci		countByStatusCode[code] = 0
151e5c31af7Sopenharmony_ci
152e5c31af7Sopenharmony_ci	# Write custom headers
153e5c31af7Sopenharmony_ci	out = codecs.open(outFilePath, "wb", encoding="utf-8")
154e5c31af7Sopenharmony_ci	out.write("<?xml version=\"1.0\"?>\n")
155e5c31af7Sopenharmony_ci	out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)
156e5c31af7Sopenharmony_ci
157e5c31af7Sopenharmony_ci	summaryElem = dstDoc.createElement('ResultTotals')
158e5c31af7Sopenharmony_ci	batchResultNode.appendChild(summaryElem)
159e5c31af7Sopenharmony_ci
160e5c31af7Sopenharmony_ci	# Print the first line manually <BatchResult FileName=something.xml>
161e5c31af7Sopenharmony_ci	out.write(dstDoc.toprettyxml().splitlines()[1])
162e5c31af7Sopenharmony_ci	out.write("\n")
163e5c31af7Sopenharmony_ci
164e5c31af7Sopenharmony_ci	parser = BatchResultParser()
165e5c31af7Sopenharmony_ci	parser.init(logFilePath)
166e5c31af7Sopenharmony_ci	logFile = open(logFilePath, 'rb')
167e5c31af7Sopenharmony_ci
168e5c31af7Sopenharmony_ci	result = parser.getNextTestCaseResult(logFile)
169e5c31af7Sopenharmony_ci	while result is not None:
170e5c31af7Sopenharmony_ci
171e5c31af7Sopenharmony_ci		countByStatusCode[result.statusCode] += 1
172e5c31af7Sopenharmony_ci		rootNodes = normalizeToXml(result, dstDoc)
173e5c31af7Sopenharmony_ci
174e5c31af7Sopenharmony_ci		for node in rootNodes:
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_ci			# Do not append TestResults to dstDoc to save memory.
177e5c31af7Sopenharmony_ci			# Instead print them directly to the file and add tabs manually.
178e5c31af7Sopenharmony_ci			for line in node.toprettyxml().splitlines():
179e5c31af7Sopenharmony_ci				out.write("\t" + line + "\n")
180e5c31af7Sopenharmony_ci
181e5c31af7Sopenharmony_ci		result = parser.getNextTestCaseResult(logFile)
182e5c31af7Sopenharmony_ci
183e5c31af7Sopenharmony_ci	# Calculate the totals to add at the end of the Xml file
184e5c31af7Sopenharmony_ci	for code in StatusCode.STATUS_CODES:
185e5c31af7Sopenharmony_ci		summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
186e5c31af7Sopenharmony_ci	summaryElem.setAttribute('All', "%d" % sum(countByStatusCode.values()))
187e5c31af7Sopenharmony_ci
188e5c31af7Sopenharmony_ci	# Print the test totals and finish the Xml Document"
189e5c31af7Sopenharmony_ci	for line in dstDoc.toprettyxml().splitlines()[2:]:
190e5c31af7Sopenharmony_ci		out.write(line + "\n")
191e5c31af7Sopenharmony_ci
192e5c31af7Sopenharmony_ci	out.close()
193e5c31af7Sopenharmony_ci	logFile.close()
194e5c31af7Sopenharmony_ci
195e5c31af7Sopenharmony_ciif __name__ == "__main__":
196e5c31af7Sopenharmony_ci	if len(sys.argv) != 3:
197e5c31af7Sopenharmony_ci		print("%s: [test log] [dst file]" % sys.argv[0])
198e5c31af7Sopenharmony_ci		sys.exit(-1)
199e5c31af7Sopenharmony_ci
200e5c31af7Sopenharmony_ci	logToXml(sys.argv[1], sys.argv[2])
201