# -*- coding: utf-8 -*- #------------------------------------------------------------------------- # drawElements Quality Program utilities # -------------------------------------- # # Copyright 2015 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. # #------------------------------------------------------------------------- from build.common import * from build.config import ANY_GENERATOR from build.build import build from build_caselists import Module, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET from fnmatch import fnmatch from copy import copy import xml.etree.cElementTree as ElementTree import xml.dom.minidom as minidom CTS_DATA_DIR = os.path.join(DEQP_DIR, "android", "cts") class Configuration: def __init__ (self, name, glconfig, rotation, surfacetype, filters): self.name = name self.glconfig = glconfig self.rotation = rotation self.surfacetype = surfacetype self.filters = filters class Package: def __init__ (self, module, configurations, splitFilters = {}): self.module = module self.configurations = configurations # Map of name:[include filters]. Each will generate <api>.<name> package # Test cases that didn't match any split filter will be in <api> package, # i.e., the default value keeps everything in one package. self.splitFilters = splitFilters class Mustpass: def __init__ (self, version, packages): self.version = version self.packages = packages class Filter: TYPE_INCLUDE = 0 TYPE_EXCLUDE = 1 def __init__ (self, type, filename): self.type = type self.filename = filename class TestRoot: def __init__ (self): self.children = [] class TestGroup: def __init__ (self, name): self.name = name self.children = [] class TestCase: def __init__ (self, name): self.name = name self.configurations = [] class GLESVersion: def __init__(self, major, minor): self.major = major self.minor = minor def encode (self): return (self.major << 16) | (self.minor) def getModuleGLESVersion (module): versions = { 'dEQP-EGL': GLESVersion(2,0), 'dEQP-GLES2': GLESVersion(2,0), 'dEQP-GLES3': GLESVersion(3,0), 'dEQP-GLES31': GLESVersion(3,1) } return versions[module.name] def getSrcDir (mustpass): return os.path.join(CTS_DATA_DIR, mustpass.version, "src") def getTmpDir (mustpass): return os.path.join(CTS_DATA_DIR, mustpass.version, "tmp") def getModuleShorthand (module): assert module.name[:5] == "dEQP-" return module.name[5:].lower() def getCaseListFileName (package, configuration): return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name) def getDstCaseListPath (mustpass, package, configuration): return os.path.join(CTS_DATA_DIR, mustpass.version, getCaseListFileName(package, configuration)) def getCTSPackageName (package, splitName): if splitName == None: return "com.drawelements.deqp." + getModuleShorthand(package.module) return "com.drawelements.deqp." + getModuleShorthand(package.module) + "." + splitName def getCommandLine (config): return "--deqp-gl-config-name=%s --deqp-screen-rotation=%s --deqp-surface-type=%s --deqp-watchdog=enable" % (config.glconfig, config.rotation, config.surfacetype) 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 (mustpass, module): generator = ANY_GENERATOR buildCfg = getBuildConfig(DEFAULT_BUILD_DIR, DEFAULT_TARGET, "Debug") #build(buildCfg, generator, [module.binName]) genCaseList(buildCfg, generator, module, "txt") 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, 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" % len(errors)) return [c for c in caseList if op(c in matched)] def applyInclude (caseList, patterns): return applyPatterns(caseList, patterns, lambda b: b) def applyExclude (caseList, patterns): return applyPatterns(caseList, patterns, 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) else: assert filter.type == Filter.TYPE_EXCLUDE res = applyExclude(res, ptrnList) return res def appendToHierarchy (root, casePath): def findChild (node, name): for child in node.children: if child.name == name: return child return None curNode = root components = casePath.split('.') for component in components[:-1]: nextNode = findChild(curNode, component) if not nextNode: nextNode = TestGroup(component) curNode.children.append(nextNode) curNode = nextNode if not findChild(curNode, components[-1]): curNode.children.append(TestCase(components[-1])) def buildTestHierachy (caseList): root = TestRoot() for case in caseList: appendToHierarchy(root, case) return root def buildTestCaseMap (root): caseMap = {} def recursiveBuild (curNode, prefix): curPath = prefix + curNode.name if isinstance(curNode, TestCase): caseMap[curPath] = curNode else: for child in curNode.children: recursiveBuild(child, curPath + '.') for child in root.children: recursiveBuild(child, '') return caseMap def include (filename): return Filter(Filter.TYPE_INCLUDE, filename) def exclude (filename): return Filter(Filter.TYPE_EXCLUDE, filename) def prettifyXML (doc): uglyString = ElementTree.tostring(doc, 'utf-8') reparsed = minidom.parseString(uglyString) return reparsed.toprettyxml(indent='\t', encoding='utf-8') def genCTSPackageXML (package, root, name): def isLeafGroup (testGroup): numGroups = 0 numTests = 0 for child in testGroup.children: if isinstance(child, TestCase): numTests += 1 else: numGroups += 1 assert numGroups + numTests > 0 if numGroups > 0 and numTests > 0: die("Mixed groups and cases in %s" % testGroup.name) return numGroups == 0 def makeConfiguration (parentElem, configuration): return ElementTree.SubElement(parentElem, "TestInstance", glconfig=configuration.glconfig, rotation=configuration.rotation, surfacetype=configuration.surfacetype) def makeTestCase (parentElem, testCase): caseElem = ElementTree.SubElement(parentElem, "Test", name=testCase.name) for config in testCase.configurations: makeConfiguration(caseElem, config) return caseElem def makeTestGroup (parentElem, testGroup): groupElem = ElementTree.SubElement(parentElem, "TestCase" if isLeafGroup(testGroup) else "TestSuite", name=testGroup.name) for child in testGroup.children: if isinstance(child, TestCase): makeTestCase(groupElem, child) else: makeTestGroup(groupElem, child) return groupElem pkgElem = ElementTree.Element("TestPackage", name = package.module.name, appPackageName = name, testType = "deqpTest") pkgElem.set("xmlns:deqp", "http://drawelements.com/deqp") pkgElem.set("deqp:glesVersion", str(getModuleGLESVersion(package.module).encode())) for child in root.children: makeTestGroup(pkgElem, child) return pkgElem def genSpecXML (mustpass): mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version) for package in mustpass.packages: packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = package.module.name) for config in package.configurations: configElem = ElementTree.SubElement(packageElem, "Configuration", name = config.name, caseListFile = getCaseListFileName(package, config), commandLine = getCommandLine(config)) return mustpassElem def genCTSPackage (package, cases, matchingByConfig, packageName, xmlFilename): root = buildTestHierachy(cases) testCaseMap = buildTestCaseMap(root) for config in package.configurations: for case in matchingByConfig[config]: if case in testCaseMap: testCaseMap[case].configurations.append(config) packageXml = genCTSPackageXML(package, root, packageName) print " Writing CTS caselist: " + xmlFilename writeFile(xmlFilename, prettifyXML(packageXml)) def genMustpass (mustpass, moduleCaseLists): print "Generating mustpass '%s'" % mustpass.version patternLists = readPatternLists(mustpass) for package in mustpass.packages: allCasesInPkg = moduleCaseLists[package.module] matchingByConfig = {} allMatchingSet = set() 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") matchingByConfig[config] = filtered allMatchingSet = allMatchingSet | set(filtered) allMatchingCases = [c for c in allCasesInPkg if c in allMatchingSet] # To preserve ordering splitFilters = package.splitFilters for splitName in splitFilters.keys(): splitIncludeFilters = splitFilters[splitName] splitCases = applyInclude(allMatchingCases, splitIncludeFilters) packageName = getCTSPackageName(package, splitName) xmlFilename = os.path.join(CTS_DATA_DIR, mustpass.version, packageName + ".xml") genCTSPackage(package, splitCases, matchingByConfig, packageName, xmlFilename) # The cases not matching any of the includes combinedSplitFilters = reduce(lambda x,y: x+y, splitFilters.values(), []) restOfCases = applyExclude(allMatchingCases, combinedSplitFilters) packageName = getCTSPackageName(package, None) xmlFilename = os.path.join(CTS_DATA_DIR, mustpass.version, packageName + ".xml") genCTSPackage(package, restOfCases, matchingByConfig, packageName, xmlFilename) specXML = genSpecXML(mustpass) specFilename = os.path.join(CTS_DATA_DIR, mustpass.version, "mustpass.xml") print " Writing spec: " + specFilename writeFile(specFilename, prettifyXML(specXML)) print "Done!" def genMustpassLists (mustpassLists): moduleCaseLists = {} # Getting case lists involves invoking build, so we want to cache the results for mustpass in mustpassLists: for package in mustpass.packages: if not package.module in moduleCaseLists: moduleCaseLists[package.module] = getCaseList(mustpass, package.module) for mustpass in mustpassLists: genMustpass(mustpass, moduleCaseLists) EGL_MODULE = Module(name = "dEQP-EGL", dirName = "egl", binName = "deqp-egl") GLES2_MODULE = Module(name = "dEQP-GLES2", dirName = "gles2", binName = "deqp-gles2") GLES3_MODULE = Module(name = "dEQP-GLES3", dirName = "gles3", binName = "deqp-gles3") GLES31_MODULE = Module(name = "dEQP-GLES31", dirName = "gles31", binName = "deqp-gles31") LMP_GLES3_PKG = Package(module = GLES3_MODULE, configurations = [ Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = [include("es30-lmp.txt")]), ]) LMP_GLES31_PKG = Package(module = GLES31_MODULE, configurations = [ Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = [include("es31-lmp.txt")]), ]) LMP_MR1_GLES3_PKG = Package(module = GLES3_MODULE, configurations = [ Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = [include("es30-lmp-mr1.txt")]), ]) LMP_MR1_GLES31_PKG = Package(module = GLES31_MODULE, configurations = [ Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = [include("es31-lmp-mr1.txt")]), ]) MASTER_EGL_COMMON_FILTERS = [include("egl-master.txt"), exclude("egl-failures.txt")] MASTER_EGL_PKG = Package(module = EGL_MODULE, configurations = [ # Master Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_EGL_COMMON_FILTERS), ]) MASTER_GLES2_COMMON_FILTERS = [ include("gles2-master.txt"), exclude("gles2-test-issues.txt"), exclude("gles2-failures.txt") ] MASTER_GLES2_PKG = Package(module = GLES2_MODULE, configurations = [ # Master Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES2_COMMON_FILTERS), ]) MASTER_GLES3_COMMON_FILTERS = [ include("gles3-master.txt"), exclude("gles3-hw-issues.txt"), exclude("gles3-driver-issues.txt"), exclude("gles3-test-issues.txt"), exclude("gles3-spec-issues.txt") ] MASTER_GLES3_PKG = Package(module = GLES3_MODULE, configurations = [ # Master Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS), # Rotations Configuration(name = "rotate-portrait", glconfig = "rgba8888d24s8ms0", rotation = "0", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-rotation.txt")]), Configuration(name = "rotate-landscape", glconfig = "rgba8888d24s8ms0", rotation = "90", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-rotation.txt")]), Configuration(name = "rotate-reverse-portrait", glconfig = "rgba8888d24s8ms0", rotation = "180", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-rotation.txt")]), Configuration(name = "rotate-reverse-landscape", glconfig = "rgba8888d24s8ms0", rotation = "270", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-rotation.txt")]), # MSAA Configuration(name = "multisample", glconfig = "rgba8888d24s8ms4", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-multisample.txt"), exclude("gles3-multisample-issues.txt")]), # Pixel format Configuration(name = "565-no-depth-no-stencil", glconfig = "rgb565d0s0ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES3_COMMON_FILTERS + [include("gles3-pixelformat.txt"), exclude("gles3-pixelformat-issues.txt")]), ]) MASTER_GLES31_COMMON_FILTERS = [ include("gles31-master.txt"), exclude("gles31-hw-issues.txt"), exclude("gles31-driver-issues.txt"), exclude("gles31-test-issues.txt"), exclude("gles31-spec-issues.txt"), ] MASTER_GLES31_PKG = Package(module = GLES31_MODULE, configurations = [ # Master Configuration(name = "master", glconfig = "rgba8888d24s8ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS), # Rotations Configuration(name = "rotate-portrait", glconfig = "rgba8888d24s8ms0", rotation = "0", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-rotation.txt")]), Configuration(name = "rotate-landscape", glconfig = "rgba8888d24s8ms0", rotation = "90", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-rotation.txt")]), Configuration(name = "rotate-reverse-portrait", glconfig = "rgba8888d24s8ms0", rotation = "180", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-rotation.txt")]), Configuration(name = "rotate-reverse-landscape", glconfig = "rgba8888d24s8ms0", rotation = "270", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-rotation.txt")]), # MSAA Configuration(name = "multisample", glconfig = "rgba8888d24s8ms4", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-multisample.txt")]), # Pixel format Configuration(name = "565-no-depth-no-stencil", glconfig = "rgb565d0s0ms0", rotation = "unspecified", surfacetype = "window", filters = MASTER_GLES31_COMMON_FILTERS + [include("gles31-pixelformat.txt")]), ], splitFilters = {"copy_image_compressed": ["dEQP-GLES31.functional.copy_image.compressed.*"], "copy_image_non_compressed": ["dEQP-GLES31.functional.copy_image.non_compressed.*"], "copy_image_mixed": ["dEQP-GLES31.functional.copy_image.mixed.*"], } ) MUSTPASS_LISTS = [ Mustpass(version = "lmp", packages = [LMP_GLES3_PKG, LMP_GLES31_PKG]), Mustpass(version = "lmp-mr1", packages = [LMP_MR1_GLES3_PKG, LMP_MR1_GLES31_PKG]), Mustpass(version = "master", packages = [MASTER_EGL_PKG, MASTER_GLES2_PKG, MASTER_GLES3_PKG, MASTER_GLES31_PKG]) ] if __name__ == "__main__": genMustpassLists(MUSTPASS_LISTS)