13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry# -*- coding: utf-8 -*- 23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 33c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport shlex 43c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport xml.dom.minidom 53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 63c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass StatusCode: 73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PASS = 'Pass' 83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry FAIL = 'Fail' 93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry QUALITY_WARNING = 'QualityWarning' 103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry COMPATIBILITY_WARNING = 'CompatibilityWarning' 113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PENDING = 'Pending' 123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry NOT_SUPPORTED = 'NotSupported' 133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry RESOURCE_ERROR = 'ResourceError' 143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry INTERNAL_ERROR = 'InternalError' 153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry CRASH = 'Crash' 163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry TIMEOUT = 'Timeout' 173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry STATUS_CODES = [ 193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PASS, 203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry FAIL, 213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry QUALITY_WARNING, 223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry COMPATIBILITY_WARNING, 233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PENDING, 243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry NOT_SUPPORTED, 253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry RESOURCE_ERROR, 263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry INTERNAL_ERROR, 273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry CRASH, 283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry TIMEOUT 293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ] 303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry STATUS_CODE_SET = set(STATUS_CODES) 313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry @staticmethod 333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def isValid (code): 343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return code in StatusCode.STATUS_CODE_SET 353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 363c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass TestCaseResult: 373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __init__ (self, name, statusCode, statusDetails, log): 383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.name = name 393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.statusCode = statusCode 403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.statusDetails = statusDetails 413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.log = log 423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __str__ (self): 443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return "%s: %s (%s)" % (self.name, self.statusCode, self.statusDetails) 453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 463c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass ParseError(Exception): 473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __init__ (self, filename, line, message): 483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.filename = filename 493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.line = line 503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.message = message 513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __str__ (self): 533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return "%s:%d: %s" % (self.filename, self.line, self.message) 543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 553c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef splitContainerLine (line): 563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return shlex.split(line) 573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 583c827367444ee418f129b2c238299f49d3264554Jarkko Poyrydef getNodeText (node): 593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry rc = [] 603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for node in node.childNodes: 613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if node.nodeType == node.TEXT_NODE: 623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry rc.append(node.data) 633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return ''.join(rc) 643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 653c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass BatchResultParser: 663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def __init__ (self): 673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass 683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def parseFile (self, filename): 703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.init(filename) 713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry f = open(filename, 'rb') 733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for line in f: 743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseLine(line) 753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curLine += 1 763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry f.close() 773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return self.testCaseResults 793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def init (self, filename): 813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Results 823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.sessionInfo = [] 833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.testCaseResults = [] 843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # State 863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText = None 873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curCaseName = None 883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Error context 903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curLine = 1 913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.filename = filename 923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def parseLine (self, line): 943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(line) > 0 and line[0] == '#': 953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseContainerLine(line) 963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elif self.curResultText != None: 973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText += line 983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # else: just ignored 993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def parseContainerLine (self, line): 1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry args = splitContainerLine(line) 1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if args[0] == "#sessionInfo": 1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(args) < 3: 1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry print args 1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseError("Invalid #sessionInfo") 1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.sessionInfo.append((args[1], ' '.join(args[2:]))) 1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elif args[0] == "#beginSession" or args[0] == "#endSession": 1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry pass # \todo [pyry] Validate 1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elif args[0] == "#beginTestCaseResult": 1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(args) != 2 or self.curCaseName != None: 1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseError("Invalid #beginTestCaseResult") 1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curCaseName = args[1] 1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText = "" 1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elif args[0] == "#endTestCaseResult": 1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(args) != 1 or self.curCaseName == None: 1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseError("Invalid #endTestCaseResult") 1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseTestCaseResult(self.curCaseName, self.curResultText) 1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curCaseName = None 1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText = None 1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry elif args[0] == "#terminateTestCaseResult": 1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(args) < 2 or self.curCaseName == None: 1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseError("Invalid #terminateTestCaseResult") 1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusCode = ' '.join(args[1:]) 1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusDetails = statusCode 1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if not StatusCode.isValid(statusCode): 1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Legacy format 1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if statusCode == "Watchdog timeout occurred.": 1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusCode = StatusCode.TIMEOUT 1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry else: 1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusCode = StatusCode.CRASH 1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Do not try to parse at all since XML is likely broken 1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.testCaseResults.append(TestCaseResult(self.curCaseName, statusCode, statusDetails, self.curResultText)) 1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curCaseName = None 1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText = None 1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry else: 1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry # Assume this is result text 1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if self.curResultText != None: 1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.curResultText += line 1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def parseTestCaseResult (self, name, log): 1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry try: 1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry doc = xml.dom.minidom.parseString(log) 1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry resultItems = doc.getElementsByTagName('Result') 1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if len(resultItems) != 1: 1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.parseError("Expected 1 <Result>, found %d" % len(resultItems)) 1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusCode = resultItems[0].getAttributeNode('StatusCode').nodeValue 1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusDetails = getNodeText(resultItems[0]) 1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry except Exception as e: 1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusCode = TestStatusCode.INTERNAL_ERROR 1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry statusDetails = "XML parsing failed: %s" % str(e) 1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry self.testCaseResults.append(TestCaseResult(name, statusCode, statusDetails, log)) 1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def parseError (self, message): 1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry raise ParseError(self.filename, self.curLine, message) 160