1# -*- coding: utf-8 -*- 2 3#------------------------------------------------------------------------- 4# drawElements Quality Program utilities 5# -------------------------------------- 6# 7# Copyright 2016 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 23import sys 24import os 25import xml.etree.cElementTree as ElementTree 26import xml.dom.minidom as minidom 27 28from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME 29 30sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts")) 31 32from build.common import * 33from build.config import ANY_GENERATOR 34from build.build import build 35from fnmatch import fnmatch 36from copy import copy 37 38GENERATED_FILE_WARNING = """\ 39/* WARNING: This is auto-generated file. Do not modify, since changes will 40 * be lost! Modify the generating script instead. 41 */""" 42 43class Project: 44 def __init__ (self, name, path, incpath, devicepath, copyright = None): 45 self.name = name 46 self.path = path 47 self.incpath = incpath 48 self.devicepath = devicepath 49 self.copyright = copyright 50 51class Configuration: 52 def __init__ (self, name, filters, glconfig = None, rotation = "unspecified", surfacetype = None, surfacewidth = None, surfaceheight = None, baseseed = None, fboconfig = None, required = False, runtime = None, os = "any"): 53 self.name = name 54 self.glconfig = glconfig 55 self.rotation = rotation 56 self.surfacetype = surfacetype 57 self.required = required 58 self.surfacewidth = surfacewidth 59 self.surfaceheight = surfaceheight 60 self.baseseed = baseseed 61 self.fboconfig = fboconfig 62 self.filters = filters 63 self.expectedRuntime = runtime 64 self.os = os 65 66class Package: 67 def __init__ (self, module, configurations, useforfirsteglconfig = True): 68 self.module = module 69 self.useforfirsteglconfig = useforfirsteglconfig 70 self.configurations = configurations 71 72class Mustpass: 73 def __init__ (self, project, version, packages, isCurrent): 74 self.project = project 75 self.version = version 76 self.packages = packages 77 self.isCurrent = isCurrent 78 79class Filter: 80 TYPE_INCLUDE = 0 81 TYPE_EXCLUDE = 1 82 83 def __init__ (self, type, filename): 84 self.type = type 85 self.filename = filename 86 87def getSrcDir (mustpass): 88 return os.path.join(mustpass.project.path, mustpass.version, "src") 89 90def getTmpDir (mustpass): 91 return os.path.join(mustpass.project.path, mustpass.version, "tmp") 92 93def getModuleShorthand (module): 94 return module.api.lower() 95 96def getCaseListFileName (package, configuration): 97 return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name) 98 99def getDstDir(mustpass): 100 return os.path.join(mustpass.project.path, mustpass.version) 101 102def getDstCaseListPath (mustpass, package, configuration): 103 return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration)) 104 105def getCommandLine (config): 106 cmdLine = "" 107 108 if config.glconfig != None: 109 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig 110 111 if config.rotation != None: 112 cmdLine += "--deqp-screen-rotation=%s " % config.rotation 113 114 if config.surfacetype != None: 115 cmdLine += "--deqp-surface-type=%s " % config.surfacetype 116 117 if config.surfacewidth != None: 118 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth 119 120 if config.surfaceheight != None: 121 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight 122 123 if config.baseseed != None: 124 cmdLine += "--deqp-base-seed=%s " % config.baseseed 125 126 if config.fboconfig != None: 127 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig 128 129 cmdLine += "--deqp-watchdog=disable" 130 131 return cmdLine 132 133def readCaseList (filename): 134 cases = [] 135 with open(filename, 'rb') as f: 136 for line in f: 137 if line[:6] == "TEST: ": 138 cases.append(line[6:].strip()) 139 return cases 140 141def getCaseList (buildCfg, generator, module): 142 return readCaseList(getCaseListPath(buildCfg, module, "txt")) 143 144def readPatternList (filename): 145 ptrns = [] 146 with open(filename, 'rb') as f: 147 for line in f: 148 line = line.strip() 149 if len(line) > 0 and line[0] != '#': 150 ptrns.append(line) 151 return ptrns 152 153def applyPatterns (caseList, patterns, filename, op): 154 matched = set() 155 errors = [] 156 curList = copy(caseList) 157 trivialPtrns = [p for p in patterns if p.find('*') < 0] 158 regularPtrns = [p for p in patterns if p.find('*') >= 0] 159 160 # Apply trivial (just case paths) 161 allCasesSet = set(caseList) 162 for path in trivialPtrns: 163 if path in allCasesSet: 164 if path in matched: 165 errors.append((path, "Same case specified more than once")) 166 matched.add(path) 167 else: 168 errors.append((path, "Test case not found")) 169 170 curList = [c for c in curList if c not in matched] 171 172 for pattern in regularPtrns: 173 matchedThisPtrn = set() 174 175 for case in curList: 176 if fnmatch(case, pattern): 177 matchedThisPtrn.add(case) 178 179 if len(matchedThisPtrn) == 0: 180 errors.append((pattern, "Pattern didn't match any cases")) 181 182 matched = matched | matchedThisPtrn 183 curList = [c for c in curList if c not in matched] 184 185 for pattern, reason in errors: 186 print "ERROR: %s: %s" % (reason, pattern) 187 188 if len(errors) > 0: 189 die("Found %s invalid patterns while processing file %s" % (len(errors), filename)) 190 191 return [c for c in caseList if op(c in matched)] 192 193def applyInclude (caseList, patterns, filename): 194 return applyPatterns(caseList, patterns, filename, lambda b: b) 195 196def applyExclude (caseList, patterns, filename): 197 return applyPatterns(caseList, patterns, filename, lambda b: not b) 198 199def readPatternLists (mustpass): 200 lists = {} 201 for package in mustpass.packages: 202 for cfg in package.configurations: 203 for filter in cfg.filters: 204 if not filter.filename in lists: 205 lists[filter.filename] = readPatternList(os.path.join(getSrcDir(mustpass), filter.filename)) 206 return lists 207 208def applyFilters (caseList, patternLists, filters): 209 res = copy(caseList) 210 for filter in filters: 211 ptrnList = patternLists[filter.filename] 212 if filter.type == Filter.TYPE_INCLUDE: 213 res = applyInclude(res, ptrnList, filter.filename) 214 else: 215 assert filter.type == Filter.TYPE_EXCLUDE 216 res = applyExclude(res, ptrnList, filter.filename) 217 return res 218 219 220def include (filename): 221 return Filter(Filter.TYPE_INCLUDE, filename) 222 223def exclude (filename): 224 return Filter(Filter.TYPE_EXCLUDE, filename) 225 226def insertXMLHeaders (mustpass, doc): 227 if mustpass.project.copyright != None: 228 doc.insert(0, ElementTree.Comment(mustpass.project.copyright)) 229 doc.insert(1, ElementTree.Comment(GENERATED_FILE_WARNING)) 230 231def prettifyXML (doc): 232 uglyString = ElementTree.tostring(doc, 'utf-8') 233 reparsed = minidom.parseString(uglyString) 234 return reparsed.toprettyxml(indent='\t', encoding='utf-8') 235 236def genSpecXML (mustpass): 237 mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version) 238 insertXMLHeaders(mustpass, mustpassElem) 239 240 packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name) 241 242 for package in mustpass.packages: 243 for config in package.configurations: 244 configElem = ElementTree.SubElement(packageElem, "Configuration", 245 useForFirstEGLConfig = str(package.useforfirsteglconfig), 246 name = config.name, 247 caseListFile = getCaseListFileName(package, config), 248 commandLine = getCommandLine(config), 249 os = str(config.os)) 250 251 return mustpassElem 252 253def getIncludeGuardName (headerFile): 254 return '_' + os.path.basename(headerFile).upper().replace('.', '_') 255 256def convertToCamelcase(s): 257 return ''.join(w.capitalize() or '_' for w in s.split('_')) 258 259def getApiType(apiName): 260 if apiName == "GLES2": 261 return "glu::ApiType::es(2, 0)" 262 if apiName == "GLES3": 263 return "glu::ApiType::es(3, 0)" 264 if apiName == "GLES31": 265 return "glu::ApiType::es(3, 1)" 266 if apiName == "GLES32": 267 return "glu::ApiType::es(3, 2)" 268 if apiName == "GL46": 269 return "glu::ApiType::core(4, 6)" 270 if apiName == "GL45": 271 return "glu::ApiType::core(4, 5)" 272 if apiName == "GL44": 273 return "glu::ApiType::core(4, 4)" 274 if apiName == "GL43": 275 return "glu::ApiType::core(4, 3)" 276 if apiName == "GL42": 277 return "glu::ApiType::core(4, 2)" 278 if apiName == "GL41": 279 return "glu::ApiType::core(4, 1)" 280 if apiName == "GL40": 281 return "glu::ApiType::core(4, 0)" 282 if apiName == "GL33": 283 return "glu::ApiType::core(3, 3)" 284 if apiName == "GL32": 285 return "glu::ApiType::core(3, 2)" 286 if apiName == "GL31": 287 return "glu::ApiType::core(3, 1)" 288 if apiName == "GL30": 289 return "glu::ApiType::core(3, 0)" 290 if apiName == "EGL": 291 return "glu::ApiType()" 292 293 raise Exception("Unknown API %s" % apiName) 294 return "Unknown" 295 296def getConfigName(cfgName): 297 if cfgName == None: 298 return "DE_NULL" 299 else: 300 return '"' + cfgName + '"' 301 302def getIntBaseSeed(baseSeed): 303 if baseSeed == None: 304 return "-1" 305 else: 306 return baseSeed 307 308def genSpecCPPIncludeFile (specFilename, mustpass): 309 fileBody = "" 310 311 includeGuard = getIncludeGuardName(specFilename) 312 fileBody += "#ifndef %s\n" % includeGuard 313 fileBody += "#define %s\n" % includeGuard 314 fileBody += mustpass.project.copyright 315 fileBody += "\n\n" 316 fileBody += GENERATED_FILE_WARNING 317 fileBody += "\n\n" 318 fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n' 319 320 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n" 321 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n" 322 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n" 323 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n" 324 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}" 325 326 emitOtherCfgTbl = False 327 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 328 firstCfgTbl = "{\n" 329 330 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_') 331 otherCfgTbl = "{\n" 332 333 for package in mustpass.packages: 334 for config in package.configurations: 335 pApiType = getApiType(package.module.api) + ',' 336 pConfigName = '"' + config.name + '",' 337 pGLConfig = getConfigName(config.glconfig) + ',' 338 pRotation = '"' + config.rotation + '",' 339 pSeed = getIntBaseSeed(config.baseseed) + ',' 340 pFBOConfig = getConfigName(config.fboconfig) + ',' 341 pWidth = config.surfacewidth + ',' 342 pHeight = config.surfaceheight 343 elemFinal = "" 344 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight) 345 elem = "\t{ " + elemContent + " },\n" 346 if package.module.name[:3] == "GTF": 347 elemFinal += gtf_wrapper_open 348 349 if config.os == "android": 350 elemFinal += android_wrapper_open 351 352 elemFinal += elem 353 354 if config.os == "android": 355 elemFinal += android_wrapper_close 356 357 if package.module.name[:3] == "GTF": 358 elemFinal += gtf_wrapper_close 359 360 if package.useforfirsteglconfig == True: 361 firstCfgTbl += elemFinal 362 else: 363 otherCfgTbl += elemFinal 364 emitOtherCfgTbl = True 365 366 firstCfgTbl += "};\n" 367 otherCfgTbl += "};\n" 368 369 fileBody += firstCfgDecl 370 fileBody += firstCfgTbl 371 372 if emitOtherCfgTbl == True: 373 fileBody += "\n" 374 fileBody += otherCfgDecl 375 fileBody += otherCfgTbl 376 377 fileBody += "\n" 378 fileBody += "#endif // %s\n" % includeGuard 379 return fileBody 380 381 382def genSpecCPPIncludes (mustpassLists): 383 for mustpass in mustpassLists: 384 if mustpass.isCurrent == True: 385 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_'))) 386 hpp = genSpecCPPIncludeFile(specFilename, mustpass) 387 388 print " Writing spec: " + specFilename 389 writeFile(specFilename, hpp) 390 print "Done!" 391 392def genMustpass (mustpass, moduleCaseLists): 393 print "Generating mustpass '%s'" % mustpass.version 394 395 patternLists = readPatternLists(mustpass) 396 397 for package in mustpass.packages: 398 allCasesInPkg = moduleCaseLists[package.module] 399 400 for config in package.configurations: 401 filtered = applyFilters(allCasesInPkg, patternLists, config.filters) 402 dstFile = getDstCaseListPath(mustpass, package, config) 403 404 print " Writing deqp caselist: " + dstFile 405 writeFile(dstFile, "\n".join(filtered) + "\n") 406 407 specXML = genSpecXML(mustpass) 408 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml") 409 410 print " Writing spec: " + specFilename 411 writeFile(specFilename, prettifyXML(specXML)) 412 413 print "Done!" 414 415def genMustpassLists (mustpassLists, generator, buildCfg): 416 moduleCaseLists = {} 417 418 # Getting case lists involves invoking build, so we want to cache the results 419 build(buildCfg, generator, [GLCTS_BIN_NAME]) 420 genCaseList(buildCfg, generator, "txt") 421 for mustpass in mustpassLists: 422 for package in mustpass.packages: 423 if not package.module in moduleCaseLists: 424 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module) 425 426 for mustpass in mustpassLists: 427 genMustpass(mustpass, moduleCaseLists) 428 429 430 genSpecCPPIncludes(mustpassLists) 431