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