Merge nougat-mr1-cts-dev into master. am: 2dcacdbe0a am: b196334de5 am: 769492af1d...
[platform/upstream/VK-GL-CTS.git] / scripts / log / log_parser.py
1 # -*- coding: utf-8 -*-
2
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
6 #
7 # Copyright 2015 The Android Open Source Project
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 #      http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 #
21 #-------------------------------------------------------------------------
22
23 import shlex
24 import xml.dom.minidom
25
26 class StatusCode:
27         PASS                                    = 'Pass'
28         FAIL                                    = 'Fail'
29         QUALITY_WARNING                 = 'QualityWarning'
30         COMPATIBILITY_WARNING   = 'CompatibilityWarning'
31         PENDING                                 = 'Pending'
32         NOT_SUPPORTED                   = 'NotSupported'
33         RESOURCE_ERROR                  = 'ResourceError'
34         INTERNAL_ERROR                  = 'InternalError'
35         CRASH                                   = 'Crash'
36         TIMEOUT                                 = 'Timeout'
37
38         STATUS_CODES                    = [
39                 PASS,
40                 FAIL,
41                 QUALITY_WARNING,
42                 COMPATIBILITY_WARNING,
43                 PENDING,
44                 NOT_SUPPORTED,
45                 RESOURCE_ERROR,
46                 INTERNAL_ERROR,
47                 CRASH,
48                 TIMEOUT
49                 ]
50         STATUS_CODE_SET                 = set(STATUS_CODES)
51
52         @staticmethod
53         def isValid (code):
54                 return code in StatusCode.STATUS_CODE_SET
55
56 class TestCaseResult:
57         def __init__ (self, name, statusCode, statusDetails, log):
58                 self.name                       = name
59                 self.statusCode         = statusCode
60                 self.statusDetails      = statusDetails
61                 self.log                        = log
62
63         def __str__ (self):
64                 return "%s: %s (%s)" % (self.name, self.statusCode, self.statusDetails)
65
66 class ParseError(Exception):
67         def __init__ (self, filename, line, message):
68                 self.filename   = filename
69                 self.line               = line
70                 self.message    = message
71
72         def __str__ (self):
73                 return "%s:%d: %s" % (self.filename, self.line, self.message)
74
75 def splitContainerLine (line):
76         return shlex.split(line)
77
78 def getNodeText (node):
79         rc = []
80         for node in node.childNodes:
81                 if node.nodeType == node.TEXT_NODE:
82                         rc.append(node.data)
83         return ''.join(rc)
84
85 class BatchResultParser:
86         def __init__ (self):
87                 pass
88
89         def parseFile (self, filename):
90                 self.init(filename)
91
92                 f = open(filename, 'rb')
93                 for line in f:
94                         self.parseLine(line)
95                         self.curLine += 1
96                 f.close()
97
98                 return self.testCaseResults
99
100         def init (self, filename):
101                 # Results
102                 self.sessionInfo                = []
103                 self.testCaseResults    = []
104
105                 # State
106                 self.curResultText              = None
107                 self.curCaseName                = None
108
109                 # Error context
110                 self.curLine                    = 1
111                 self.filename                   = filename
112
113         def parseLine (self, line):
114                 if len(line) > 0 and line[0] == '#':
115                         self.parseContainerLine(line)
116                 elif self.curResultText != None:
117                         self.curResultText += line
118                 # else: just ignored
119
120         def parseContainerLine (self, line):
121                 args = splitContainerLine(line)
122                 if args[0] == "#sessionInfo":
123                         if len(args) < 3:
124                                 print args
125                                 self.parseError("Invalid #sessionInfo")
126                         self.sessionInfo.append((args[1], ' '.join(args[2:])))
127                 elif args[0] == "#beginSession" or args[0] == "#endSession":
128                         pass # \todo [pyry] Validate
129                 elif args[0] == "#beginTestCaseResult":
130                         if len(args) != 2 or self.curCaseName != None:
131                                 self.parseError("Invalid #beginTestCaseResult")
132                         self.curCaseName        = args[1]
133                         self.curResultText      = ""
134                 elif args[0] == "#endTestCaseResult":
135                         if len(args) != 1 or self.curCaseName == None:
136                                 self.parseError("Invalid #endTestCaseResult")
137                         self.parseTestCaseResult(self.curCaseName, self.curResultText)
138                         self.curCaseName        = None
139                         self.curResultText      = None
140                 elif args[0] == "#terminateTestCaseResult":
141                         if len(args) < 2 or self.curCaseName == None:
142                                 self.parseError("Invalid #terminateTestCaseResult")
143                         statusCode              = ' '.join(args[1:])
144                         statusDetails   = statusCode
145
146                         if not StatusCode.isValid(statusCode):
147                                 # Legacy format
148                                 if statusCode == "Watchdog timeout occurred.":
149                                         statusCode = StatusCode.TIMEOUT
150                                 else:
151                                         statusCode = StatusCode.CRASH
152
153                         # Do not try to parse at all since XML is likely broken
154                         self.testCaseResults.append(TestCaseResult(self.curCaseName, statusCode, statusDetails, self.curResultText))
155
156                         self.curCaseName        = None
157                         self.curResultText      = None
158                 else:
159                         # Assume this is result text
160                         if self.curResultText != None:
161                                 self.curResultText += line
162
163         def parseTestCaseResult (self, name, log):
164                 try:
165                         # The XML parser has troubles with invalid characters deliberately included in the shaders.
166                         # This line removes such characters before calling the parser
167                         log = log.decode('utf-8','ignore').encode("utf-8")
168                         doc = xml.dom.minidom.parseString(log)
169                         resultItems = doc.getElementsByTagName('Result')
170                         if len(resultItems) != 1:
171                                 self.parseError("Expected 1 <Result>, found %d" % len(resultItems))
172
173                         statusCode              = resultItems[0].getAttributeNode('StatusCode').nodeValue
174                         statusDetails   = getNodeText(resultItems[0])
175                 except Exception as e:
176                         statusCode              = StatusCode.INTERNAL_ERROR
177                         statusDetails   = "XML parsing failed: %s" % str(e)
178
179                 self.testCaseResults.append(TestCaseResult(name, statusCode, statusDetails, log))
180
181         def parseError (self, message):
182                 raise ParseError(self.filename, self.curLine, message)