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