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