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