1 # -*- coding: utf-8 -*- 2 3 import os 4 import sys 5 import codecs 6 import xml.dom.minidom 7 import xml.sax 8 import xml.sax.handler 9 from log_parser import BatchResultParser, StatusCode 10 11 STYLESHEET_FILENAME = "testlog.xsl" 12 LOG_VERSION = '0.3.2' 13 14 class BuildXMLLogHandler(xml.sax.handler.ContentHandler): 15 def __init__ (self, doc): 16 self.doc = doc 17 self.elementStack = [] 18 self.rootElements = [] 19 20 def getRootElements (self): 21 return self.rootElements 22 23 def pushElement (self, elem): 24 if len(self.elementStack) == 0: 25 self.rootElements.append(elem) 26 else: 27 self.getCurElement().appendChild(elem) 28 self.elementStack.append(elem) 29 30 def popElement (self): 31 self.elementStack.pop() 32 33 def getCurElement (self): 34 if len(self.elementStack) > 0: 35 return self.elementStack[-1] 36 else: 37 return None 38 39 def startDocument (self): 40 pass 41 42 def endDocument (self): 43 pass 44 45 def startElement (self, name, attrs): 46 elem = self.doc.createElement(name) 47 for name in attrs.getNames(): 48 value = attrs.getValue(name) 49 elem.setAttribute(name, value) 50 self.pushElement(elem) 51 52 def endElement (self, name): 53 self.popElement() 54 55 def characters (self, content): 56 # Discard completely empty content 57 if len(content.strip()) == 0: 58 return 59 60 # Append as text node (not pushed to stack) 61 if self.getCurElement() != None: 62 txt = self.doc.createTextNode(content) 63 self.getCurElement().appendChild(txt) 64 65 class LogErrorHandler(xml.sax.handler.ErrorHandler): 66 def __init__ (self): 67 pass 68 69 def error (self, err): 70 #print "error(%s)" % str(err) 71 pass 72 73 def fatalError (self, err): 74 #print "fatalError(%s)" % str(err) 75 pass 76 77 def warning (self, warn): 78 #print "warning(%s)" % str(warn) 79 pass 80 81 def findFirstElementByName (nodes, name): 82 for node in nodes: 83 if node.nodeName == name: 84 return node 85 chFound = findFirstElementByName(node.childNodes, name) 86 if chFound != None: 87 return chFound 88 return None 89 90 # Normalizes potentially broken (due to crash for example) log data to XML element tree 91 def normalizeToXml (result, doc): 92 handler = BuildXMLLogHandler(doc) 93 errHandler = LogErrorHandler() 94 95 xml.sax.parseString(result.log, handler, errHandler) 96 97 rootNodes = handler.getRootElements() 98 99 # Check if we have TestCaseResult 100 testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult') 101 if testCaseResult == None: 102 # Create TestCaseResult element 103 testCaseResult = doc.createElement('TestCaseResult') 104 testCaseResult.setAttribute('CasePath', result.name) 105 testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable.. 106 testCaseResult.setAttribute('Version', LOG_VERSION) 107 rootNodes.append(testCaseResult) 108 109 # Check if we have Result element 110 resultElem = findFirstElementByName(rootNodes, 'Result') 111 if resultElem == None: 112 # Create result element 113 resultElem = doc.createElement('Result') 114 resultElem.setAttribute('StatusCode', result.statusCode) 115 resultElem.appendChild(doc.createTextNode(result.statusDetails)) 116 testCaseResult.appendChild(resultElem) 117 118 return rootNodes 119 120 def logToXml (inFile, outFile): 121 parser = BatchResultParser() 122 results = parser.parseFile(inFile) 123 124 dstDoc = xml.dom.minidom.Document() 125 batchResultNode = dstDoc.createElement('BatchResult') 126 batchResultNode.setAttribute("FileName", os.path.basename(inFile)) 127 128 dstDoc.appendChild(batchResultNode) 129 130 for result in results: 131 # Normalize log to valid XML 132 rootNodes = normalizeToXml(result, dstDoc) 133 for node in rootNodes: 134 batchResultNode.appendChild(node) 135 136 # Summary 137 countByStatusCode = {} 138 for code in StatusCode.STATUS_CODES: 139 countByStatusCode[code] = 0 140 141 for result in results: 142 countByStatusCode[result.statusCode] += 1 143 144 summaryElem = dstDoc.createElement('ResultTotals') 145 for code in StatusCode.STATUS_CODES: 146 summaryElem.setAttribute(code, "%d" % countByStatusCode[code]) 147 summaryElem.setAttribute('All', "%d" % len(results)) 148 batchResultNode.appendChild(summaryElem) 149 150 text = dstDoc.toprettyxml() 151 152 out = codecs.open(outFile, "wb", encoding="utf-8") 153 154 # Write custom headers 155 out.write("<?xml version=\"1.0\"?>\n") 156 out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME) 157 158 for line in text.splitlines()[1:]: 159 out.write(line) 160 out.write("\n") 161 162 out.close() 163 164 if __name__ == "__main__": 165 if len(sys.argv) != 3: 166 print "%s: [test log] [dst file]" % sys.argv[0] 167 sys.exit(-1) 168 169 logToXml(sys.argv[1], sys.argv[2]) 170