# -*- coding: utf-8 -*- #------------------------------------------------------------------------- # drawElements Quality Program utilities # -------------------------------------- # # Copyright 2015 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #------------------------------------------------------------------------- import os import sys import codecs import xml.dom.minidom import xml.sax import xml.sax.handler from log_parser import BatchResultParser, StatusCode STYLESHEET_FILENAME = "testlog.xsl" LOG_VERSION = '0.3.2' class BuildXMLLogHandler(xml.sax.handler.ContentHandler): def __init__ (self, doc): self.doc = doc self.elementStack = [] self.rootElements = [] def getRootElements (self): return self.rootElements def pushElement (self, elem): if len(self.elementStack) == 0: self.rootElements.append(elem) else: self.getCurElement().appendChild(elem) self.elementStack.append(elem) def popElement (self): self.elementStack.pop() def getCurElement (self): if len(self.elementStack) > 0: return self.elementStack[-1] else: return None def startDocument (self): pass def endDocument (self): pass def startElement (self, name, attrs): elem = self.doc.createElement(name) for name in attrs.getNames(): value = attrs.getValue(name) elem.setAttribute(name, value) self.pushElement(elem) def endElement (self, name): self.popElement() def characters (self, content): # Discard completely empty content if len(content.strip()) == 0: return # Append as text node (not pushed to stack) if self.getCurElement() != None: txt = self.doc.createTextNode(content) self.getCurElement().appendChild(txt) class LogErrorHandler(xml.sax.handler.ErrorHandler): def __init__ (self): pass def error (self, err): #print "error(%s)" % str(err) pass def fatalError (self, err): #print "fatalError(%s)" % str(err) pass def warning (self, warn): #print "warning(%s)" % str(warn) pass def findFirstElementByName (nodes, name): for node in nodes: if node.nodeName == name: return node chFound = findFirstElementByName(node.childNodes, name) if chFound != None: return chFound return None # Normalizes potentially broken (due to crash for example) log data to XML element tree def normalizeToXml (result, doc): handler = BuildXMLLogHandler(doc) errHandler = LogErrorHandler() xml.sax.parseString(result.log, handler, errHandler) rootNodes = handler.getRootElements() # Check if we have TestCaseResult testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult') if testCaseResult == None: # Create TestCaseResult element testCaseResult = doc.createElement('TestCaseResult') testCaseResult.setAttribute('CasePath', result.name) testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable.. testCaseResult.setAttribute('Version', LOG_VERSION) rootNodes.append(testCaseResult) # Check if we have Result element resultElem = findFirstElementByName(rootNodes, 'Result') if resultElem == None: # Create result element resultElem = doc.createElement('Result') resultElem.setAttribute('StatusCode', result.statusCode) resultElem.appendChild(doc.createTextNode(result.statusDetails)) testCaseResult.appendChild(resultElem) return rootNodes def logToXml (inFile, outFile): parser = BatchResultParser() results = parser.parseFile(inFile) dstDoc = xml.dom.minidom.Document() batchResultNode = dstDoc.createElement('BatchResult') batchResultNode.setAttribute("FileName", os.path.basename(inFile)) dstDoc.appendChild(batchResultNode) for result in results: # Normalize log to valid XML rootNodes = normalizeToXml(result, dstDoc) for node in rootNodes: batchResultNode.appendChild(node) # Summary countByStatusCode = {} for code in StatusCode.STATUS_CODES: countByStatusCode[code] = 0 for result in results: countByStatusCode[result.statusCode] += 1 summaryElem = dstDoc.createElement('ResultTotals') for code in StatusCode.STATUS_CODES: summaryElem.setAttribute(code, "%d" % countByStatusCode[code]) summaryElem.setAttribute('All', "%d" % len(results)) batchResultNode.appendChild(summaryElem) text = dstDoc.toprettyxml() out = codecs.open(outFile, "wb", encoding="utf-8") # Write custom headers out.write("<?xml version=\"1.0\"?>\n") out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME) for line in text.splitlines()[1:]: out.write(line) out.write("\n") out.close() if __name__ == "__main__": if len(sys.argv) != 3: print "%s: [test log] [dst file]" % sys.argv[0] sys.exit(-1) logToXml(sys.argv[1], sys.argv[2])