log_parser.py revision 3c827367444ee418f129b2c238299f49d3264554
1# -*- coding: utf-8 -*-
2
3import shlex
4import xml.dom.minidom
5
6class 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
36class 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
46class 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
55def splitContainerLine (line):
56	return shlex.split(line)
57
58def 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
65class 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