Home | History | Annotate | Download | only in log
      1 # -*- coding: utf-8 -*-
      2 
      3 import shlex
      4 import xml.dom.minidom
      5 
      6 class StatusCode:
      7 	PASS					= 'Pass'
      8 	FAIL					= 'Fail'
      9 	QUALITY_WARNING			= 'QualityWarning'
     10 	COMPATIBILITY_WARNING	= 'CompatibilityWarning'
     11 	PENDING					= 'Pending'
     12 	NOT_SUPPORTED			= 'NotSupported'
     13 	RESOURCE_ERROR			= 'ResourceError'
     14 	INTERNAL_ERROR			= 'InternalError'
     15 	CRASH					= 'Crash'
     16 	TIMEOUT					= 'Timeout'
     17 
     18 	STATUS_CODES			= [
     19 		PASS,
     20 		FAIL,
     21 		QUALITY_WARNING,
     22 		COMPATIBILITY_WARNING,
     23 		PENDING,
     24 		NOT_SUPPORTED,
     25 		RESOURCE_ERROR,
     26 		INTERNAL_ERROR,
     27 		CRASH,
     28 		TIMEOUT
     29 		]
     30 	STATUS_CODE_SET			= set(STATUS_CODES)
     31 
     32 	@staticmethod
     33 	def isValid (code):
     34 		return code in StatusCode.STATUS_CODE_SET
     35 
     36 class TestCaseResult:
     37 	def __init__ (self, name, statusCode, statusDetails, log):
     38 		self.name			= name
     39 		self.statusCode		= statusCode
     40 		self.statusDetails	= statusDetails
     41 		self.log			= log
     42 
     43 	def __str__ (self):
     44 		return "%s: %s (%s)" % (self.name, self.statusCode, self.statusDetails)
     45 
     46 class ParseError(Exception):
     47 	def __init__ (self, filename, line, message):
     48 		self.filename	= filename
     49 		self.line		= line
     50 		self.message	= message
     51 
     52 	def __str__ (self):
     53 		return "%s:%d: %s" % (self.filename, self.line, self.message)
     54 
     55 def splitContainerLine (line):
     56 	return shlex.split(line)
     57 
     58 def getNodeText (node):
     59 	rc = []
     60 	for node in node.childNodes:
     61 		if node.nodeType == node.TEXT_NODE:
     62 			rc.append(node.data)
     63 	return ''.join(rc)
     64 
     65 class BatchResultParser:
     66 	def __init__ (self):
     67 		pass
     68 
     69 	def parseFile (self, filename):
     70 		self.init(filename)
     71 
     72 		f = open(filename, 'rb')
     73 		for line in f:
     74 			self.parseLine(line)
     75 			self.curLine += 1
     76 		f.close()
     77 
     78 		return self.testCaseResults
     79 
     80 	def init (self, filename):
     81 		# Results
     82 		self.sessionInfo		= []
     83 		self.testCaseResults	= []
     84 
     85 		# State
     86 		self.curResultText		= None
     87 		self.curCaseName		= None
     88 
     89 		# Error context
     90 		self.curLine			= 1
     91 		self.filename			= filename
     92 
     93 	def parseLine (self, line):
     94 		if len(line) > 0 and line[0] == '#':
     95 			self.parseContainerLine(line)
     96 		elif self.curResultText != None:
     97 			self.curResultText += line
     98 		# else: just ignored
     99 
    100 	def parseContainerLine (self, line):
    101 		args = splitContainerLine(line)
    102 		if args[0] == "#sessionInfo":
    103 			if len(args) < 3:
    104 				print args
    105 				self.parseError("Invalid #sessionInfo")
    106 			self.sessionInfo.append((args[1], ' '.join(args[2:])))
    107 		elif args[0] == "#beginSession" or args[0] == "#endSession":
    108 			pass # \todo [pyry] Validate
    109 		elif args[0] == "#beginTestCaseResult":
    110 			if len(args) != 2 or self.curCaseName != None:
    111 				self.parseError("Invalid #beginTestCaseResult")
    112 			self.curCaseName	= args[1]
    113 			self.curResultText	= ""
    114 		elif args[0] == "#endTestCaseResult":
    115 			if len(args) != 1 or self.curCaseName == None:
    116 				self.parseError("Invalid #endTestCaseResult")
    117 			self.parseTestCaseResult(self.curCaseName, self.curResultText)
    118 			self.curCaseName	= None
    119 			self.curResultText	= None
    120 		elif args[0] == "#terminateTestCaseResult":
    121 			if len(args) < 2 or self.curCaseName == None:
    122 				self.parseError("Invalid #terminateTestCaseResult")
    123 			statusCode		= ' '.join(args[1:])
    124 			statusDetails	= statusCode
    125 
    126 			if not StatusCode.isValid(statusCode):
    127 				# Legacy format
    128 				if statusCode == "Watchdog timeout occurred.":
    129 					statusCode = StatusCode.TIMEOUT
    130 				else:
    131 					statusCode = StatusCode.CRASH
    132 
    133 			# Do not try to parse at all since XML is likely broken
    134 			self.testCaseResults.append(TestCaseResult(self.curCaseName, statusCode, statusDetails, self.curResultText))
    135 
    136 			self.curCaseName	= None
    137 			self.curResultText	= None
    138 		else:
    139 			# Assume this is result text
    140 			if self.curResultText != None:
    141 				self.curResultText += line
    142 
    143 	def parseTestCaseResult (self, name, log):
    144 		try:
    145 			doc = xml.dom.minidom.parseString(log)
    146 			resultItems = doc.getElementsByTagName('Result')
    147 			if len(resultItems) != 1:
    148 				self.parseError("Expected 1 <Result>, found %d" % len(resultItems))
    149 
    150 			statusCode		= resultItems[0].getAttributeNode('StatusCode').nodeValue
    151 			statusDetails	= getNodeText(resultItems[0])
    152 		except Exception as e:
    153 			statusCode		= TestStatusCode.INTERNAL_ERROR
    154 			statusDetails	= "XML parsing failed: %s" % str(e)
    155 
    156 		self.testCaseResults.append(TestCaseResult(name, statusCode, statusDetails, log))
    157 
    158 	def parseError (self, message):
    159 		raise ParseError(self.filename, self.curLine, message)
    160