| # -*- coding: utf-8 -*- |
| |
| #------------------------------------------------------------------------- |
| # drawElements Quality Program utilities |
| # -------------------------------------- |
| # |
| # Copyright 2016 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| #------------------------------------------------------------------------- |
| |
| import sys |
| import os |
| import xml.etree.cElementTree as ElementTree |
| import xml.dom.minidom as minidom |
| |
| from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME |
| |
| sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts")) |
| |
| from build.common import * |
| from build.config import ANY_GENERATOR |
| from build.build import build |
| from fnmatch import fnmatch |
| from copy import copy |
| |
| GENERATED_FILE_WARNING = """\ |
| /* WARNING: This is auto-generated file. Do not modify, since changes will |
| * be lost! Modify the generating script instead. |
| */""" |
| |
| class Project: |
| def __init__ (self, name, path, incpath, devicepath, copyright = None): |
| self.name = name |
| self.path = path |
| self.incpath = incpath |
| self.devicepath = devicepath |
| self.copyright = copyright |
| |
| class Configuration: |
| 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", skip = "none"): |
| self.name = name |
| self.glconfig = glconfig |
| self.rotation = rotation |
| self.surfacetype = surfacetype |
| self.required = required |
| self.surfacewidth = surfacewidth |
| self.surfaceheight = surfaceheight |
| self.baseseed = baseseed |
| self.fboconfig = fboconfig |
| self.filters = filters |
| self.expectedRuntime = runtime |
| self.os = os |
| self.skipPlatform = skip |
| |
| class Package: |
| def __init__ (self, module, configurations, useforfirsteglconfig = True): |
| self.module = module |
| self.useforfirsteglconfig = useforfirsteglconfig |
| self.configurations = configurations |
| |
| class Mustpass: |
| def __init__ (self, project, version, packages, isCurrent): |
| self.project = project |
| self.version = version |
| self.packages = packages |
| self.isCurrent = isCurrent |
| |
| class Filter: |
| TYPE_INCLUDE = 0 |
| TYPE_EXCLUDE = 1 |
| |
| def __init__ (self, type, filename): |
| self.type = type |
| self.filename = filename |
| |
| def getSrcDir (mustpass): |
| return os.path.join(mustpass.project.path, mustpass.version, "src") |
| |
| def getTmpDir (mustpass): |
| return os.path.join(mustpass.project.path, mustpass.version, "tmp") |
| |
| def getModuleShorthand (module): |
| return module.api.lower() |
| |
| def getCaseListFileName (package, configuration): |
| return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name) |
| |
| def getDstDir(mustpass): |
| return os.path.join(mustpass.project.path, mustpass.version) |
| |
| def getDstCaseListPath (mustpass, package, configuration): |
| return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration)) |
| |
| def getCommandLine (config): |
| cmdLine = "" |
| |
| if config.glconfig != None: |
| cmdLine += "--deqp-gl-config-name=%s " % config.glconfig |
| |
| if config.rotation != None: |
| cmdLine += "--deqp-screen-rotation=%s " % config.rotation |
| |
| if config.surfacetype != None: |
| cmdLine += "--deqp-surface-type=%s " % config.surfacetype |
| |
| if config.surfacewidth != None: |
| cmdLine += "--deqp-surface-width=%s " % config.surfacewidth |
| |
| if config.surfaceheight != None: |
| cmdLine += "--deqp-surface-height=%s " % config.surfaceheight |
| |
| if config.baseseed != None: |
| cmdLine += "--deqp-base-seed=%s " % config.baseseed |
| |
| if config.fboconfig != None: |
| cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig |
| |
| cmdLine += "--deqp-watchdog=disable" |
| |
| return cmdLine |
| |
| def readCaseList (filename): |
| cases = [] |
| with open(filename, 'rb') as f: |
| for line in f: |
| if line[:6] == "TEST: ": |
| cases.append(line[6:].strip()) |
| return cases |
| |
| def getCaseList (buildCfg, generator, module): |
| return readCaseList(getCaseListPath(buildCfg, module, "txt")) |
| |
| def readPatternList (filename): |
| ptrns = [] |
| with open(filename, 'rb') as f: |
| for line in f: |
| line = line.strip() |
| if len(line) > 0 and line[0] != '#': |
| ptrns.append(line) |
| return ptrns |
| |
| def applyPatterns (caseList, patterns, filename, op): |
| matched = set() |
| errors = [] |
| curList = copy(caseList) |
| trivialPtrns = [p for p in patterns if p.find('*') < 0] |
| regularPtrns = [p for p in patterns if p.find('*') >= 0] |
| |
| # Apply trivial (just case paths) |
| allCasesSet = set(caseList) |
| for path in trivialPtrns: |
| if path in allCasesSet: |
| if path in matched: |
| errors.append((path, "Same case specified more than once")) |
| matched.add(path) |
| else: |
| errors.append((path, "Test case not found")) |
| |
| curList = [c for c in curList if c not in matched] |
| |
| for pattern in regularPtrns: |
| matchedThisPtrn = set() |
| |
| for case in curList: |
| if fnmatch(case, pattern): |
| matchedThisPtrn.add(case) |
| |
| if len(matchedThisPtrn) == 0: |
| errors.append((pattern, "Pattern didn't match any cases")) |
| |
| matched = matched | matchedThisPtrn |
| curList = [c for c in curList if c not in matched] |
| |
| for pattern, reason in errors: |
| print("ERROR: %s: %s" % (reason, pattern)) |
| |
| if len(errors) > 0: |
| die("Found %s invalid patterns while processing file %s" % (len(errors), filename)) |
| |
| return [c for c in caseList if op(c in matched)] |
| |
| def applyInclude (caseList, patterns, filename): |
| return applyPatterns(caseList, patterns, filename, lambda b: b) |
| |
| def applyExclude (caseList, patterns, filename): |
| return applyPatterns(caseList, patterns, filename, lambda b: not b) |
| |
| def readPatternLists (mustpass): |
| lists = {} |
| for package in mustpass.packages: |
| for cfg in package.configurations: |
| for filter in cfg.filters: |
| if not filter.filename in lists: |
| lists[filter.filename] = readPatternList(os.path.join(getSrcDir(mustpass), filter.filename)) |
| return lists |
| |
| def applyFilters (caseList, patternLists, filters): |
| res = copy(caseList) |
| for filter in filters: |
| ptrnList = patternLists[filter.filename] |
| if filter.type == Filter.TYPE_INCLUDE: |
| res = applyInclude(res, ptrnList, filter.filename) |
| else: |
| assert filter.type == Filter.TYPE_EXCLUDE |
| res = applyExclude(res, ptrnList, filter.filename) |
| return res |
| |
| |
| def include (filename): |
| return Filter(Filter.TYPE_INCLUDE, filename) |
| |
| def exclude (filename): |
| return Filter(Filter.TYPE_EXCLUDE, filename) |
| |
| def insertXMLHeaders (mustpass, doc): |
| if mustpass.project.copyright != None: |
| doc.insert(0, ElementTree.Comment(mustpass.project.copyright)) |
| doc.insert(1, ElementTree.Comment(GENERATED_FILE_WARNING)) |
| |
| def prettifyXML (doc): |
| uglyString = ElementTree.tostring(doc, 'utf-8') |
| reparsed = minidom.parseString(uglyString) |
| return reparsed.toprettyxml(indent='\t', encoding='utf-8') |
| |
| def genSpecXML (mustpass): |
| mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version) |
| insertXMLHeaders(mustpass, mustpassElem) |
| |
| packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name) |
| |
| for package in mustpass.packages: |
| for config in package.configurations: |
| configElem = ElementTree.SubElement(packageElem, "Configuration", |
| useForFirstEGLConfig = str(package.useforfirsteglconfig), |
| name = config.name, |
| caseListFile = getCaseListFileName(package, config), |
| commandLine = getCommandLine(config), |
| os = str(config.os)) |
| |
| return mustpassElem |
| |
| def getIncludeGuardName (headerFile): |
| return '_' + os.path.basename(headerFile).upper().replace('.', '_') |
| |
| def convertToCamelcase(s): |
| return ''.join(w.capitalize() or '_' for w in s.split('_')) |
| |
| def getApiType(apiName): |
| if apiName == "GLES2": |
| return "glu::ApiType::es(2, 0)" |
| if apiName == "GLES3": |
| return "glu::ApiType::es(3, 0)" |
| if apiName == "GLES31": |
| return "glu::ApiType::es(3, 1)" |
| if apiName == "GLES32": |
| return "glu::ApiType::es(3, 2)" |
| if apiName == "GL46": |
| return "glu::ApiType::core(4, 6)" |
| if apiName == "GL45": |
| return "glu::ApiType::core(4, 5)" |
| if apiName == "GL44": |
| return "glu::ApiType::core(4, 4)" |
| if apiName == "GL43": |
| return "glu::ApiType::core(4, 3)" |
| if apiName == "GL42": |
| return "glu::ApiType::core(4, 2)" |
| if apiName == "GL41": |
| return "glu::ApiType::core(4, 1)" |
| if apiName == "GL40": |
| return "glu::ApiType::core(4, 0)" |
| if apiName == "GL33": |
| return "glu::ApiType::core(3, 3)" |
| if apiName == "GL32": |
| return "glu::ApiType::core(3, 2)" |
| if apiName == "GL31": |
| return "glu::ApiType::core(3, 1)" |
| if apiName == "GL30": |
| return "glu::ApiType::core(3, 0)" |
| if apiName == "EGL": |
| return "glu::ApiType()" |
| |
| raise Exception("Unknown API %s" % apiName) |
| return "Unknown" |
| |
| def getConfigName(cfgName): |
| if cfgName == None: |
| return "DE_NULL" |
| else: |
| return '"' + cfgName + '"' |
| |
| def getIntBaseSeed(baseSeed): |
| if baseSeed == None: |
| return "-1" |
| else: |
| return baseSeed |
| |
| def genSpecCPPIncludeFile (specFilename, mustpass): |
| fileBody = "" |
| |
| includeGuard = getIncludeGuardName(specFilename) |
| fileBody += "#ifndef %s\n" % includeGuard |
| fileBody += "#define %s\n" % includeGuard |
| fileBody += mustpass.project.copyright |
| fileBody += "\n\n" |
| fileBody += GENERATED_FILE_WARNING |
| fileBody += "\n\n" |
| fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n' |
| |
| gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n" |
| gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n" |
| android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n" |
| android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n" |
| skip_x11_wrapper_open = "#ifndef DEQP_SUPPORT_X11\n" |
| skip_x11_wrapper_close = "#endif // DEQP_SUPPORT_X11\n" |
| TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}" |
| |
| emitOtherCfgTbl = False |
| firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_') |
| firstCfgTbl = "{\n" |
| |
| otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_') |
| otherCfgTbl = "{\n" |
| |
| for package in mustpass.packages: |
| for config in package.configurations: |
| pApiType = getApiType(package.module.api) + ',' |
| pConfigName = '"' + config.name + '",' |
| pGLConfig = getConfigName(config.glconfig) + ',' |
| pRotation = '"' + config.rotation + '",' |
| pSeed = getIntBaseSeed(config.baseseed) + ',' |
| pFBOConfig = getConfigName(config.fboconfig) + ',' |
| pWidth = config.surfacewidth + ',' |
| pHeight = config.surfaceheight |
| elemFinal = "" |
| elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight) |
| elem = "\t{ " + elemContent + " },\n" |
| if package.module.name[:3] == "GTF": |
| elemFinal += gtf_wrapper_open |
| |
| if config.os == "android": |
| elemFinal += android_wrapper_open |
| |
| if config.skipPlatform == "x11": |
| elemFinal += skip_x11_wrapper_open |
| |
| elemFinal += elem |
| |
| if config.skipPlatform == "x11": |
| elemFinal += skip_x11_wrapper_close |
| |
| if config.os == "android": |
| elemFinal += android_wrapper_close |
| |
| if package.module.name[:3] == "GTF": |
| elemFinal += gtf_wrapper_close |
| |
| if package.useforfirsteglconfig == True: |
| firstCfgTbl += elemFinal |
| else: |
| otherCfgTbl += elemFinal |
| emitOtherCfgTbl = True |
| |
| firstCfgTbl += "};\n" |
| otherCfgTbl += "};\n" |
| |
| fileBody += firstCfgDecl |
| fileBody += firstCfgTbl |
| |
| if emitOtherCfgTbl == True: |
| fileBody += "\n" |
| fileBody += otherCfgDecl |
| fileBody += otherCfgTbl |
| |
| fileBody += "\n" |
| fileBody += "#endif // %s\n" % includeGuard |
| return fileBody |
| |
| |
| def genSpecCPPIncludes (mustpassLists): |
| for mustpass in mustpassLists: |
| if mustpass.isCurrent == True: |
| specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_'))) |
| hpp = genSpecCPPIncludeFile(specFilename, mustpass) |
| |
| print(" Writing spec: " + specFilename) |
| writeFile(specFilename, hpp) |
| print("Done!") |
| |
| def genMustpass (mustpass, moduleCaseLists): |
| print("Generating mustpass '%s'" % mustpass.version) |
| |
| patternLists = readPatternLists(mustpass) |
| |
| for package in mustpass.packages: |
| allCasesInPkg = moduleCaseLists[package.module] |
| |
| for config in package.configurations: |
| filtered = applyFilters(allCasesInPkg, patternLists, config.filters) |
| dstFile = getDstCaseListPath(mustpass, package, config) |
| |
| print(" Writing deqp caselist: " + dstFile) |
| writeFile(dstFile, "\n".join(filtered) + "\n") |
| |
| specXML = genSpecXML(mustpass) |
| specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml") |
| |
| print(" Writing spec: " + specFilename) |
| writeFile(specFilename, prettifyXML(specXML)) |
| |
| print("Done!") |
| |
| def genMustpassLists (mustpassLists, generator, buildCfg): |
| moduleCaseLists = {} |
| |
| # Getting case lists involves invoking build, so we want to cache the results |
| build(buildCfg, generator, [GLCTS_BIN_NAME]) |
| genCaseList(buildCfg, generator, "txt") |
| for mustpass in mustpassLists: |
| for package in mustpass.packages: |
| if not package.module in moduleCaseLists: |
| moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module) |
| |
| for mustpass in mustpassLists: |
| genMustpass(mustpass, moduleCaseLists) |
| |
| |
| genSpecCPPIncludes(mustpassLists) |