xref: /aosp_15_r20/external/deqp/scripts/log/log_parser.py (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 *35238bceSAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2 *35238bceSAndroid Build Coastguard Worker
3 *35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
4 *35238bceSAndroid Build Coastguard Worker# drawElements Quality Program utilities
5 *35238bceSAndroid Build Coastguard Worker# --------------------------------------
6 *35238bceSAndroid Build Coastguard Worker#
7 *35238bceSAndroid Build Coastguard Worker# Copyright 2015 The Android Open Source Project
8 *35238bceSAndroid Build Coastguard Worker#
9 *35238bceSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
10 *35238bceSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
11 *35238bceSAndroid Build Coastguard Worker# You may obtain a copy of the License at
12 *35238bceSAndroid Build Coastguard Worker#
13 *35238bceSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
14 *35238bceSAndroid Build Coastguard Worker#
15 *35238bceSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
16 *35238bceSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
17 *35238bceSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 *35238bceSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
19 *35238bceSAndroid Build Coastguard Worker# limitations under the License.
20 *35238bceSAndroid Build Coastguard Worker#
21 *35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
22 *35238bceSAndroid Build Coastguard Worker
23 *35238bceSAndroid Build Coastguard Workerimport shlex
24 *35238bceSAndroid Build Coastguard Workerimport sys
25 *35238bceSAndroid Build Coastguard Workerimport xml.dom.minidom
26 *35238bceSAndroid Build Coastguard Worker
27 *35238bceSAndroid Build Coastguard Workerclass StatusCode:
28 *35238bceSAndroid Build Coastguard Worker    PASS = 'Pass'
29 *35238bceSAndroid Build Coastguard Worker    FAIL = 'Fail'
30 *35238bceSAndroid Build Coastguard Worker    QUALITY_WARNING = 'QualityWarning'
31 *35238bceSAndroid Build Coastguard Worker    COMPATIBILITY_WARNING = 'CompatibilityWarning'
32 *35238bceSAndroid Build Coastguard Worker    PENDING = 'Pending'
33 *35238bceSAndroid Build Coastguard Worker    NOT_SUPPORTED = 'NotSupported'
34 *35238bceSAndroid Build Coastguard Worker    RESOURCE_ERROR = 'ResourceError'
35 *35238bceSAndroid Build Coastguard Worker    INTERNAL_ERROR = 'InternalError'
36 *35238bceSAndroid Build Coastguard Worker    CRASH = 'Crash'
37 *35238bceSAndroid Build Coastguard Worker    TIMEOUT = 'Timeout'
38 *35238bceSAndroid Build Coastguard Worker
39 *35238bceSAndroid Build Coastguard Worker    STATUS_CODES = [
40 *35238bceSAndroid Build Coastguard Worker        PASS,
41 *35238bceSAndroid Build Coastguard Worker        FAIL,
42 *35238bceSAndroid Build Coastguard Worker        QUALITY_WARNING,
43 *35238bceSAndroid Build Coastguard Worker        COMPATIBILITY_WARNING,
44 *35238bceSAndroid Build Coastguard Worker        PENDING,
45 *35238bceSAndroid Build Coastguard Worker        NOT_SUPPORTED,
46 *35238bceSAndroid Build Coastguard Worker        RESOURCE_ERROR,
47 *35238bceSAndroid Build Coastguard Worker        INTERNAL_ERROR,
48 *35238bceSAndroid Build Coastguard Worker        CRASH,
49 *35238bceSAndroid Build Coastguard Worker        TIMEOUT
50 *35238bceSAndroid Build Coastguard Worker        ]
51 *35238bceSAndroid Build Coastguard Worker    STATUS_CODE_SET = set(STATUS_CODES)
52 *35238bceSAndroid Build Coastguard Worker
53 *35238bceSAndroid Build Coastguard Worker    @staticmethod
54 *35238bceSAndroid Build Coastguard Worker    def isValid (code):
55 *35238bceSAndroid Build Coastguard Worker        return code in StatusCode.STATUS_CODE_SET
56 *35238bceSAndroid Build Coastguard Worker
57 *35238bceSAndroid Build Coastguard Workerclass TestCaseResult:
58 *35238bceSAndroid Build Coastguard Worker    def __init__ (self, name, statusCode, statusDetails, log):
59 *35238bceSAndroid Build Coastguard Worker        self.name = name
60 *35238bceSAndroid Build Coastguard Worker        self.statusCode = statusCode
61 *35238bceSAndroid Build Coastguard Worker        self.statusDetails = statusDetails
62 *35238bceSAndroid Build Coastguard Worker        self.log = log
63 *35238bceSAndroid Build Coastguard Worker
64 *35238bceSAndroid Build Coastguard Worker    def __str__ (self):
65 *35238bceSAndroid Build Coastguard Worker        return "%s: %s (%s)" % (self.name, self.statusCode, self.statusDetails)
66 *35238bceSAndroid Build Coastguard Worker
67 *35238bceSAndroid Build Coastguard Workerclass ParseError(Exception):
68 *35238bceSAndroid Build Coastguard Worker    def __init__ (self, filename, line, message):
69 *35238bceSAndroid Build Coastguard Worker        self.filename = filename
70 *35238bceSAndroid Build Coastguard Worker        self.line = line
71 *35238bceSAndroid Build Coastguard Worker        self.message = message
72 *35238bceSAndroid Build Coastguard Worker
73 *35238bceSAndroid Build Coastguard Worker    def __str__ (self):
74 *35238bceSAndroid Build Coastguard Worker        return "%s:%d: %s" % (self.filename, self.line, self.message)
75 *35238bceSAndroid Build Coastguard Worker
76 *35238bceSAndroid Build Coastguard Workerdef splitContainerLine (line):
77 *35238bceSAndroid Build Coastguard Worker    if sys.version_info > (3, 0):
78 *35238bceSAndroid Build Coastguard Worker        # In Python 3, shlex works better with unicode.
79 *35238bceSAndroid Build Coastguard Worker        return shlex.split(line)
80 *35238bceSAndroid Build Coastguard Worker    else:
81 *35238bceSAndroid Build Coastguard Worker        # In Python 2, shlex works better with bytes, so encode and decode again upon return.
82 *35238bceSAndroid Build Coastguard Worker        return [w.decode('utf-8') for w in shlex.split(line.encode('utf-8'))]
83 *35238bceSAndroid Build Coastguard Worker
84 *35238bceSAndroid Build Coastguard Workerdef getNodeText (node):
85 *35238bceSAndroid Build Coastguard Worker    rc = []
86 *35238bceSAndroid Build Coastguard Worker    for node in node.childNodes:
87 *35238bceSAndroid Build Coastguard Worker        if node.nodeType == node.TEXT_NODE:
88 *35238bceSAndroid Build Coastguard Worker            rc.append(node.data)
89 *35238bceSAndroid Build Coastguard Worker    return ''.join(rc)
90 *35238bceSAndroid Build Coastguard Worker
91 *35238bceSAndroid Build Coastguard Workerclass BatchResultParser:
92 *35238bceSAndroid Build Coastguard Worker    def __init__ (self):
93 *35238bceSAndroid Build Coastguard Worker        pass
94 *35238bceSAndroid Build Coastguard Worker
95 *35238bceSAndroid Build Coastguard Worker    def parseFile (self, filename):
96 *35238bceSAndroid Build Coastguard Worker        self.init(filename)
97 *35238bceSAndroid Build Coastguard Worker
98 *35238bceSAndroid Build Coastguard Worker        f = open(filename, 'rb')
99 *35238bceSAndroid Build Coastguard Worker        for line in f:
100 *35238bceSAndroid Build Coastguard Worker            self.parseLine(line)
101 *35238bceSAndroid Build Coastguard Worker            self.curLine += 1
102 *35238bceSAndroid Build Coastguard Worker        f.close()
103 *35238bceSAndroid Build Coastguard Worker
104 *35238bceSAndroid Build Coastguard Worker        return self.testCaseResults
105 *35238bceSAndroid Build Coastguard Worker
106 *35238bceSAndroid Build Coastguard Worker    def getNextTestCaseResult (self, file):
107 *35238bceSAndroid Build Coastguard Worker        try:
108 *35238bceSAndroid Build Coastguard Worker            del self.testCaseResults[:]
109 *35238bceSAndroid Build Coastguard Worker            self.curResultText = None
110 *35238bceSAndroid Build Coastguard Worker
111 *35238bceSAndroid Build Coastguard Worker            isNextResult = self.parseLine(next(file))
112 *35238bceSAndroid Build Coastguard Worker            while not isNextResult:
113 *35238bceSAndroid Build Coastguard Worker                isNextResult = self.parseLine(next(file))
114 *35238bceSAndroid Build Coastguard Worker
115 *35238bceSAndroid Build Coastguard Worker            # Return the next TestCaseResult
116 *35238bceSAndroid Build Coastguard Worker            return self.testCaseResults.pop()
117 *35238bceSAndroid Build Coastguard Worker
118 *35238bceSAndroid Build Coastguard Worker        except StopIteration:
119 *35238bceSAndroid Build Coastguard Worker            # If end of file was reached and there is no log left, the parsing finished successful (return None).
120 *35238bceSAndroid Build Coastguard Worker            # Otherwise, if there is still log to be parsed, it means that there was a crash.
121 *35238bceSAndroid Build Coastguard Worker            if self.curResultText:
122 *35238bceSAndroid Build Coastguard Worker                return TestCaseResult(self.curCaseName, StatusCode.CRASH, StatusCode.CRASH, self.curResultText)
123 *35238bceSAndroid Build Coastguard Worker            else:
124 *35238bceSAndroid Build Coastguard Worker                return None
125 *35238bceSAndroid Build Coastguard Worker
126 *35238bceSAndroid Build Coastguard Worker    def init (self, filename):
127 *35238bceSAndroid Build Coastguard Worker        # Results
128 *35238bceSAndroid Build Coastguard Worker        self.sessionInfo = []
129 *35238bceSAndroid Build Coastguard Worker        self.testCaseResults = []
130 *35238bceSAndroid Build Coastguard Worker
131 *35238bceSAndroid Build Coastguard Worker        # State
132 *35238bceSAndroid Build Coastguard Worker        self.curResultText = None
133 *35238bceSAndroid Build Coastguard Worker        self.curCaseName = None
134 *35238bceSAndroid Build Coastguard Worker
135 *35238bceSAndroid Build Coastguard Worker        # Error context
136 *35238bceSAndroid Build Coastguard Worker        self.curLine = 1
137 *35238bceSAndroid Build Coastguard Worker        self.filename = filename
138 *35238bceSAndroid Build Coastguard Worker
139 *35238bceSAndroid Build Coastguard Worker    def parseLine (self, line):
140 *35238bceSAndroid Build Coastguard Worker        # Some test shaders contain invalid characters.
141 *35238bceSAndroid Build Coastguard Worker        text = line.decode('utf-8', 'ignore')
142 *35238bceSAndroid Build Coastguard Worker        if len(text) > 0 and text[0] == '#':
143 *35238bceSAndroid Build Coastguard Worker            return self.parseContainerLine(line)
144 *35238bceSAndroid Build Coastguard Worker        elif self.curResultText != None:
145 *35238bceSAndroid Build Coastguard Worker            self.curResultText += line
146 *35238bceSAndroid Build Coastguard Worker            return None
147 *35238bceSAndroid Build Coastguard Worker        # else: just ignored
148 *35238bceSAndroid Build Coastguard Worker
149 *35238bceSAndroid Build Coastguard Worker    def parseContainerLine (self, line):
150 *35238bceSAndroid Build Coastguard Worker        isTestCaseResult = False
151 *35238bceSAndroid Build Coastguard Worker        # Some test shaders contain invalid characters.
152 *35238bceSAndroid Build Coastguard Worker        text = line.decode('utf-8', 'ignore')
153 *35238bceSAndroid Build Coastguard Worker        args = splitContainerLine(text)
154 *35238bceSAndroid Build Coastguard Worker        if args[0] == "#sessionInfo":
155 *35238bceSAndroid Build Coastguard Worker            if len(args) < 3:
156 *35238bceSAndroid Build Coastguard Worker                print(args)
157 *35238bceSAndroid Build Coastguard Worker                self.parseError("Invalid #sessionInfo")
158 *35238bceSAndroid Build Coastguard Worker            self.sessionInfo.append((args[1], ' '.join(args[2:])))
159 *35238bceSAndroid Build Coastguard Worker        elif args[0] == "#beginSession" or args[0] == "#endSession":
160 *35238bceSAndroid Build Coastguard Worker            pass # \todo [pyry] Validate
161 *35238bceSAndroid Build Coastguard Worker        elif args[0] == "#beginTestCaseResult":
162 *35238bceSAndroid Build Coastguard Worker            if len(args) != 2 or self.curCaseName != None:
163 *35238bceSAndroid Build Coastguard Worker                self.parseError("Invalid #beginTestCaseResult")
164 *35238bceSAndroid Build Coastguard Worker            self.curCaseName = args[1]
165 *35238bceSAndroid Build Coastguard Worker            self.curResultText = b""
166 *35238bceSAndroid Build Coastguard Worker        elif args[0] == "#endTestCaseResult":
167 *35238bceSAndroid Build Coastguard Worker            if len(args) != 1 or self.curCaseName == None:
168 *35238bceSAndroid Build Coastguard Worker                self.parseError("Invalid #endTestCaseResult")
169 *35238bceSAndroid Build Coastguard Worker            self.parseTestCaseResult(self.curCaseName, self.curResultText)
170 *35238bceSAndroid Build Coastguard Worker            self.curCaseName = None
171 *35238bceSAndroid Build Coastguard Worker            self.curResultText = None
172 *35238bceSAndroid Build Coastguard Worker            isTestCaseResult = True
173 *35238bceSAndroid Build Coastguard Worker        elif args[0] == "#terminateTestCaseResult":
174 *35238bceSAndroid Build Coastguard Worker            if len(args) < 2 or self.curCaseName == None:
175 *35238bceSAndroid Build Coastguard Worker                self.parseError("Invalid #terminateTestCaseResult")
176 *35238bceSAndroid Build Coastguard Worker            statusCode = ' '.join(args[1:])
177 *35238bceSAndroid Build Coastguard Worker            statusDetails = statusCode
178 *35238bceSAndroid Build Coastguard Worker
179 *35238bceSAndroid Build Coastguard Worker            if not StatusCode.isValid(statusCode):
180 *35238bceSAndroid Build Coastguard Worker                # Legacy format
181 *35238bceSAndroid Build Coastguard Worker                if statusCode == "Watchdog timeout occurred.":
182 *35238bceSAndroid Build Coastguard Worker                    statusCode = StatusCode.TIMEOUT
183 *35238bceSAndroid Build Coastguard Worker                else:
184 *35238bceSAndroid Build Coastguard Worker                    statusCode = StatusCode.CRASH
185 *35238bceSAndroid Build Coastguard Worker
186 *35238bceSAndroid Build Coastguard Worker            # Do not try to parse at all since XML is likely broken
187 *35238bceSAndroid Build Coastguard Worker            self.testCaseResults.append(TestCaseResult(self.curCaseName, statusCode, statusDetails, self.curResultText))
188 *35238bceSAndroid Build Coastguard Worker
189 *35238bceSAndroid Build Coastguard Worker            self.curCaseName = None
190 *35238bceSAndroid Build Coastguard Worker            self.curResultText = None
191 *35238bceSAndroid Build Coastguard Worker            isTestCaseResult = True
192 *35238bceSAndroid Build Coastguard Worker        else:
193 *35238bceSAndroid Build Coastguard Worker            # Assume this is result text
194 *35238bceSAndroid Build Coastguard Worker            if self.curResultText != None:
195 *35238bceSAndroid Build Coastguard Worker                self.curResultText += line
196 *35238bceSAndroid Build Coastguard Worker
197 *35238bceSAndroid Build Coastguard Worker        return isTestCaseResult
198 *35238bceSAndroid Build Coastguard Worker
199 *35238bceSAndroid Build Coastguard Worker    def parseTestCaseResult (self, name, log):
200 *35238bceSAndroid Build Coastguard Worker        try:
201 *35238bceSAndroid Build Coastguard Worker            # The XML parser has troubles with invalid characters deliberately included in the shaders.
202 *35238bceSAndroid Build Coastguard Worker            # This line removes such characters before calling the parser
203 *35238bceSAndroid Build Coastguard Worker            log = log.decode('utf-8','ignore').encode("utf-8")
204 *35238bceSAndroid Build Coastguard Worker            doc = xml.dom.minidom.parseString(log)
205 *35238bceSAndroid Build Coastguard Worker            resultItems = doc.getElementsByTagName('Result')
206 *35238bceSAndroid Build Coastguard Worker            if len(resultItems) != 1:
207 *35238bceSAndroid Build Coastguard Worker                self.parseError("Expected 1 <Result>, found %d" % len(resultItems))
208 *35238bceSAndroid Build Coastguard Worker
209 *35238bceSAndroid Build Coastguard Worker            statusCode = resultItems[0].getAttributeNode('StatusCode').nodeValue
210 *35238bceSAndroid Build Coastguard Worker            statusDetails = getNodeText(resultItems[0])
211 *35238bceSAndroid Build Coastguard Worker        except Exception as e:
212 *35238bceSAndroid Build Coastguard Worker            statusCode = StatusCode.INTERNAL_ERROR
213 *35238bceSAndroid Build Coastguard Worker            statusDetails = "XML parsing failed: %s" % str(e)
214 *35238bceSAndroid Build Coastguard Worker
215 *35238bceSAndroid Build Coastguard Worker        self.testCaseResults.append(TestCaseResult(name, statusCode, statusDetails, log))
216 *35238bceSAndroid Build Coastguard Worker
217 *35238bceSAndroid Build Coastguard Worker    def parseError (self, message):
218 *35238bceSAndroid Build Coastguard Worker        raise ParseError(self.filename, self.curLine, message)
219