13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry# -*- coding: utf-8 -*- 23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 33c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport os 43c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport sys 53c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport codecs 63c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.dom.minidom 73c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.sax 83c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.sax.handler 93c827367444ee418f129b2c238299f49d3264554Jarkko Poyryfrom log_parser import BatchResultParser, StatusCode 103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 113c827367444ee418f129b2c238299f49d3264554Jarkko PoyrySTYLESHEET_FILENAME = "testlog.xsl" 123c827367444ee418f129b2c238299f49d3264554Jarkko PoyryLOG_VERSION = '0.3.2' 133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 143c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass BuildXMLLogHandler(xml.sax.handler.ContentHandler): 153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __init__ (self, doc): 163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.doc = doc 173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.elementStack = [] 183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.rootElements = [] 193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def getRootElements (self): 213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return self.rootElements 223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def pushElement (self, elem): 243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(self.elementStack) == 0: 253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.rootElements.append(elem) 263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry else: 273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.getCurElement().appendChild(elem) 283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.elementStack.append(elem) 293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def popElement (self): 313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.elementStack.pop() 323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def getCurElement (self): 343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(self.elementStack) > 0: 353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return self.elementStack[-1] 363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry else: 373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return None 383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def startDocument (self): 403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def endDocument (self): 433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def startElement (self, name, attrs): 463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elem = self.doc.createElement(name) 473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for name in attrs.getNames(): 483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry value = attrs.getValue(name) 493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elem.setAttribute(name, value) 503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.pushElement(elem) 513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def endElement (self, name): 533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.popElement() 543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def characters (self, content): 563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Discard completely empty content 573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(content.strip()) == 0: 583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return 593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Append as text node (not pushed to stack) 613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if self.getCurElement() != None: 623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry txt = self.doc.createTextNode(content) 633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.getCurElement().appendChild(txt) 643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 653c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass LogErrorHandler(xml.sax.handler.ErrorHandler): 663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __init__ (self): 673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def error (self, err): 703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry #print "error(%s)" % str(err) 713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def fatalError (self, err): 743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry #print "fatalError(%s)" % str(err) 753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def warning (self, warn): 783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry #print "warning(%s)" % str(warn) 793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 813c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef findFirstElementByName (nodes, name): 823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for node in nodes: 833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if node.nodeName == name: 843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return node 853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry chFound = findFirstElementByName(node.childNodes, name) 863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if chFound != None: 873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return chFound 883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return None 893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry# Normalizes potentially broken (due to crash for example) log data to XML element tree 913c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef normalizeToXml (result, doc): 923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry handler = BuildXMLLogHandler(doc) 933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry errHandler = LogErrorHandler() 943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry xml.sax.parseString(result.log, handler, errHandler) 963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry rootNodes = handler.getRootElements() 983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Check if we have TestCaseResult 1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult') 1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if testCaseResult == None: 1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Create TestCaseResult element 1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult = doc.createElement('TestCaseResult') 1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult.setAttribute('CasePath', result.name) 1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable.. 1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult.setAttribute('Version', LOG_VERSION) 1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry rootNodes.append(testCaseResult) 1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Check if we have Result element 1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry resultElem = findFirstElementByName(rootNodes, 'Result') 1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if resultElem == None: 1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Create result element 1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry resultElem = doc.createElement('Result') 1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry resultElem.setAttribute('StatusCode', result.statusCode) 1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry resultElem.appendChild(doc.createTextNode(result.statusDetails)) 1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry testCaseResult.appendChild(resultElem) 1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return rootNodes 1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef logToXml (inFile, outFile): 1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry parser = BatchResultParser() 1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry results = parser.parseFile(inFile) 1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry dstDoc = xml.dom.minidom.Document() 1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry batchResultNode = dstDoc.createElement('BatchResult') 1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry batchResultNode.setAttribute("FileName", os.path.basename(inFile)) 1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry dstDoc.appendChild(batchResultNode) 1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for result in results: 1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Normalize log to valid XML 1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry rootNodes = normalizeToXml(result, dstDoc) 1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for node in rootNodes: 1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry batchResultNode.appendChild(node) 1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Summary 1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry countByStatusCode = {} 1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for code in StatusCode.STATUS_CODES: 1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry countByStatusCode[code] = 0 1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for result in results: 1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry countByStatusCode[result.statusCode] += 1 1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry summaryElem = dstDoc.createElement('ResultTotals') 1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for code in StatusCode.STATUS_CODES: 1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry summaryElem.setAttribute(code, "%d" % countByStatusCode[code]) 1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry summaryElem.setAttribute('All', "%d" % len(results)) 1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry batchResultNode.appendChild(summaryElem) 1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry text = dstDoc.toprettyxml() 1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out = codecs.open(outFile, "wb", encoding="utf-8") 1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Write custom headers 1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out.write("<?xml version=\"1.0\"?>\n") 1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME) 1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for line in text.splitlines()[1:]: 1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out.write(line) 1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out.write("\n") 1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry out.close() 1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyryif __name__ == "__main__": 1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(sys.argv) != 3: 1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry print "%s: [test log] [dst file]" % sys.argv[0] 1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sys.exit(-1) 1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry logToXml(sys.argv[1], sys.argv[2]) 170