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 (inFile, outFile): 141 parser = BatchResultParser() 142 results = parser.parseFile(inFile) 143 144 dstDoc = xml.dom.minidom.Document() 145 batchResultNode = dstDoc.createElement('BatchResult') 146 batchResultNode.setAttribute("FileName", os.path.basename(inFile)) 147 148 dstDoc.appendChild(batchResultNode) 149 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) 155 156 # Summary 157 countByStatusCode = {} 158 for code in StatusCode.STATUS_CODES: 159 countByStatusCode[code] = 0 160 161 for result in results: 162 countByStatusCode[result.statusCode] += 1 163 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) 169 170 text = dstDoc.toprettyxml() 171 172 out = codecs.open(outFile, "wb", encoding="utf-8") 173 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) 177 178 for line in text.splitlines()[1:]: 179 out.write(line) 180 out.write("\n") 181 182 out.close() 183 184if __name__ == "__main__": 185 if len(sys.argv) != 3: 186 print "%s: [test log] [dst file]" % sys.argv[0] 187 sys.exit(-1) 188 189 logToXml(sys.argv[1], sys.argv[2]) 190