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