1 # -*- coding: utf-8 -*-
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
7 # Copyright 2015 The Android Open Source Project
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 #-------------------------------------------------------------------------
26 import xml.dom.minidom
28 import xml.sax.handler
29 from log_parser import BatchResultParser, StatusCode
31 STYLESHEET_FILENAME = "testlog.xsl"
34 class BuildXMLLogHandler(xml.sax.handler.ContentHandler):
35 def __init__ (self, doc):
37 self.elementStack = []
38 self.rootElements = []
40 def getRootElements (self):
41 return self.rootElements
43 def pushElement (self, elem):
44 if len(self.elementStack) == 0:
45 self.rootElements.append(elem)
47 self.getCurElement().appendChild(elem)
48 self.elementStack.append(elem)
50 def popElement (self):
51 self.elementStack.pop()
53 def getCurElement (self):
54 if len(self.elementStack) > 0:
55 return self.elementStack[-1]
59 def startDocument (self):
62 def endDocument (self):
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)
72 def endElement (self, name):
75 def characters (self, content):
76 # Discard completely empty content
77 if len(content.strip()) == 0:
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)
85 class LogErrorHandler(xml.sax.handler.ErrorHandler):
89 def error (self, err):
90 #print "error(%s)" % str(err)
93 def fatalError (self, err):
94 #print "fatalError(%s)" % str(err)
97 def warning (self, warn):
98 #print "warning(%s)" % str(warn)
101 def findFirstElementByName (nodes, name):
103 if node.nodeName == name:
105 chFound = findFirstElementByName(node.childNodes, name)
110 # Normalizes potentially broken (due to crash for example) log data to XML element tree
111 def normalizeToXml (result, doc):
112 handler = BuildXMLLogHandler(doc)
113 errHandler = LogErrorHandler()
115 xml.sax.parseString(result.log, handler, errHandler)
117 rootNodes = handler.getRootElements()
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)
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)
140 def logToXml (inFile, outFile):
141 parser = BatchResultParser()
142 results = parser.parseFile(inFile)
144 dstDoc = xml.dom.minidom.Document()
145 batchResultNode = dstDoc.createElement('BatchResult')
146 batchResultNode.setAttribute("FileName", os.path.basename(inFile))
148 dstDoc.appendChild(batchResultNode)
150 for result in results:
151 # Normalize log to valid XML
152 rootNodes = normalizeToXml(result, dstDoc)
153 for node in rootNodes:
154 batchResultNode.appendChild(node)
157 countByStatusCode = {}
158 for code in StatusCode.STATUS_CODES:
159 countByStatusCode[code] = 0
161 for result in results:
162 countByStatusCode[result.statusCode] += 1
164 summaryElem = dstDoc.createElement('ResultTotals')
165 for code in StatusCode.STATUS_CODES:
166 summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
167 summaryElem.setAttribute('All', "%d" % len(results))
168 batchResultNode.appendChild(summaryElem)
170 text = dstDoc.toprettyxml()
172 out = codecs.open(outFile, "wb", encoding="utf-8")
174 # Write custom headers
175 out.write("<?xml version=\"1.0\"?>\n")
176 out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)
178 for line in text.splitlines()[1:]:
184 if __name__ == "__main__":
185 if len(sys.argv) != 3:
186 print "%s: [test log] [dst file]" % sys.argv[0]
189 logToXml(sys.argv[1], sys.argv[2])