blob: b0fd9633158efc395e3e90eb24dfe11e851700d3 [file] [log] [blame]
# -*- 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, 'rt') 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, 'rt') 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",
caseListFile = getCaseListFileName(package, config),
commandLine = getCommandLine(config),
name = config.name,
os = str(config.os),
useForFirstEGLConfig = str(package.useforfirsteglconfig)
)
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()"
if apiName == "GL42-COMPAT":
return "glu::ApiType::compatibility(4, 2)"
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).decode())
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)