1 # -*- coding: utf-8 -*-
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
7 # Copyright 2016 The Android Open Source Project
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 #-------------------------------------------------------------------------
25 import xml.etree.cElementTree as ElementTree
26 import xml.dom.minidom as minidom
28 from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME
30 sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts"))
32 from build.common import *
33 from build.config import ANY_GENERATOR
34 from build.build import build
35 from fnmatch import fnmatch
38 GENERATED_FILE_WARNING = """\
39 /* WARNING: This is auto-generated file. Do not modify, since changes will
40 * be lost! Modify the generating script instead.
44 def __init__ (self, name, path, incpath, devicepath, copyright = None):
47 self.incpath = incpath
48 self.devicepath = devicepath
49 self.copyright = copyright
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"):
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
67 def __init__ (self, module, configurations, useforfirsteglconfig = True):
69 self.useforfirsteglconfig = useforfirsteglconfig
70 self.configurations = configurations
73 def __init__ (self, project, version, packages, isCurrent):
74 self.project = project
75 self.version = version
76 self.packages = packages
77 self.isCurrent = isCurrent
83 def __init__ (self, type, filename):
85 self.filename = filename
87 def getSrcDir (mustpass):
88 return os.path.join(mustpass.project.path, mustpass.version, "src")
90 def getTmpDir (mustpass):
91 return os.path.join(mustpass.project.path, mustpass.version, "tmp")
93 def getModuleShorthand (module):
94 return module.api.lower()
96 def getCaseListFileName (package, configuration):
97 return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
99 def getDstDir(mustpass):
100 return os.path.join(mustpass.project.path, mustpass.version)
102 def getDstCaseListPath (mustpass, package, configuration):
103 return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration))
105 def getCommandLine (config):
108 if config.glconfig != None:
109 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig
111 if config.rotation != None:
112 cmdLine += "--deqp-screen-rotation=%s " % config.rotation
114 if config.surfacetype != None:
115 cmdLine += "--deqp-surface-type=%s " % config.surfacetype
117 if config.surfacewidth != None:
118 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth
120 if config.surfaceheight != None:
121 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight
123 if config.baseseed != None:
124 cmdLine += "--deqp-base-seed=%s " % config.baseseed
126 if config.fboconfig != None:
127 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig
129 cmdLine += "--deqp-watchdog=disable"
133 def readCaseList (filename):
135 with open(filename, 'rb') as f:
137 if line[:6] == "TEST: ":
138 cases.append(line[6:].strip())
141 def getCaseList (buildCfg, generator, module):
142 return readCaseList(getCaseListPath(buildCfg, module, "txt"))
144 def readPatternList (filename):
146 with open(filename, 'rb') as f:
149 if len(line) > 0 and line[0] != '#':
153 def applyPatterns (caseList, patterns, filename, op):
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]
160 # Apply trivial (just case paths)
161 allCasesSet = set(caseList)
162 for path in trivialPtrns:
163 if path in allCasesSet:
165 errors.append((path, "Same case specified more than once"))
168 errors.append((path, "Test case not found"))
170 curList = [c for c in curList if c not in matched]
172 for pattern in regularPtrns:
173 matchedThisPtrn = set()
176 if fnmatch(case, pattern):
177 matchedThisPtrn.add(case)
179 if len(matchedThisPtrn) == 0:
180 errors.append((pattern, "Pattern didn't match any cases"))
182 matched = matched | matchedThisPtrn
183 curList = [c for c in curList if c not in matched]
185 for pattern, reason in errors:
186 print "ERROR: %s: %s" % (reason, pattern)
189 die("Found %s invalid patterns while processing file %s" % (len(errors), filename))
191 return [c for c in caseList if op(c in matched)]
193 def applyInclude (caseList, patterns, filename):
194 return applyPatterns(caseList, patterns, filename, lambda b: b)
196 def applyExclude (caseList, patterns, filename):
197 return applyPatterns(caseList, patterns, filename, lambda b: not b)
199 def readPatternLists (mustpass):
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))
208 def applyFilters (caseList, patternLists, filters):
210 for filter in filters:
211 ptrnList = patternLists[filter.filename]
212 if filter.type == Filter.TYPE_INCLUDE:
213 res = applyInclude(res, ptrnList, filter.filename)
215 assert filter.type == Filter.TYPE_EXCLUDE
216 res = applyExclude(res, ptrnList, filter.filename)
220 def include (filename):
221 return Filter(Filter.TYPE_INCLUDE, filename)
223 def exclude (filename):
224 return Filter(Filter.TYPE_EXCLUDE, filename)
226 def 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))
231 def prettifyXML (doc):
232 uglyString = ElementTree.tostring(doc, 'utf-8')
233 reparsed = minidom.parseString(uglyString)
234 return reparsed.toprettyxml(indent='\t', encoding='utf-8')
236 def genSpecXML (mustpass):
237 mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version)
238 insertXMLHeaders(mustpass, mustpassElem)
240 packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name)
242 for package in mustpass.packages:
243 for config in package.configurations:
244 configElem = ElementTree.SubElement(packageElem, "Configuration",
245 useForFirstEGLConfig = str(package.useforfirsteglconfig),
247 caseListFile = getCaseListFileName(package, config),
248 commandLine = getCommandLine(config),
253 def getIncludeGuardName (headerFile):
254 return '_' + os.path.basename(headerFile).upper().replace('.', '_')
256 def convertToCamelcase(s):
257 return ''.join(w.capitalize() or '_' for w in s.split('_'))
259 def 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 == "GL45":
269 return "glu::ApiType::core(4, 5)"
270 if apiName == "GL44":
271 return "glu::ApiType::core(4, 4)"
272 if apiName == "GL43":
273 return "glu::ApiType::core(4, 3)"
274 if apiName == "GL42":
275 return "glu::ApiType::core(4, 2)"
276 if apiName == "GL41":
277 return "glu::ApiType::core(4, 1)"
278 if apiName == "GL40":
279 return "glu::ApiType::core(4, 0)"
280 if apiName == "GL33":
281 return "glu::ApiType::core(3, 3)"
282 if apiName == "GL32":
283 return "glu::ApiType::core(3, 2)"
284 if apiName == "GL31":
285 return "glu::ApiType::core(3, 1)"
286 if apiName == "GL30":
287 return "glu::ApiType::core(3, 0)"
289 return "glu::ApiType()"
291 raise Exception("Unknown API %s" % apiName)
294 def getConfigName(cfgName):
298 return '"' + cfgName + '"'
300 def getIntBaseSeed(baseSeed):
306 def genSpecCPPIncludeFile (specFilename, mustpass):
309 includeGuard = getIncludeGuardName(specFilename)
310 fileBody += "#ifndef %s\n" % includeGuard
311 fileBody += "#define %s\n" % includeGuard
312 fileBody += mustpass.project.copyright
314 fileBody += GENERATED_FILE_WARNING
316 fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
318 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n"
319 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n"
320 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n"
321 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n"
322 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}"
324 emitOtherCfgTbl = False
325 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
328 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
331 for package in mustpass.packages:
332 for config in package.configurations:
333 pApiType = getApiType(package.module.api) + ','
334 pConfigName = '"' + config.name + '",'
335 pGLConfig = getConfigName(config.glconfig) + ','
336 pRotation = '"' + config.rotation + '",'
337 pSeed = getIntBaseSeed(config.baseseed) + ','
338 pFBOConfig = getConfigName(config.fboconfig) + ','
339 pWidth = config.surfacewidth + ','
340 pHeight = config.surfaceheight
342 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight)
343 elem = "\t{ " + elemContent + " },\n"
344 if package.module.name[:3] == "GTF":
345 elemFinal += gtf_wrapper_open
347 if config.os == "android":
348 elemFinal += android_wrapper_open
352 if config.os == "android":
353 elemFinal += android_wrapper_close
355 if package.module.name[:3] == "GTF":
356 elemFinal += gtf_wrapper_close
358 if package.useforfirsteglconfig == True:
359 firstCfgTbl += elemFinal
361 otherCfgTbl += elemFinal
362 emitOtherCfgTbl = True
364 firstCfgTbl += "};\n"
365 otherCfgTbl += "};\n"
367 fileBody += firstCfgDecl
368 fileBody += firstCfgTbl
370 if emitOtherCfgTbl == True:
372 fileBody += otherCfgDecl
373 fileBody += otherCfgTbl
376 fileBody += "#endif // %s\n" % includeGuard
380 def genSpecCPPIncludes (mustpassLists):
381 for mustpass in mustpassLists:
382 if mustpass.isCurrent == True:
383 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_')))
384 hpp = genSpecCPPIncludeFile(specFilename, mustpass)
386 print " Writing spec: " + specFilename
387 writeFile(specFilename, hpp)
390 def genMustpass (mustpass, moduleCaseLists):
391 print "Generating mustpass '%s'" % mustpass.version
393 patternLists = readPatternLists(mustpass)
395 for package in mustpass.packages:
396 allCasesInPkg = moduleCaseLists[package.module]
398 for config in package.configurations:
399 filtered = applyFilters(allCasesInPkg, patternLists, config.filters)
400 dstFile = getDstCaseListPath(mustpass, package, config)
402 print " Writing deqp caselist: " + dstFile
403 writeFile(dstFile, "\n".join(filtered) + "\n")
405 specXML = genSpecXML(mustpass)
406 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
408 print " Writing spec: " + specFilename
409 writeFile(specFilename, prettifyXML(specXML))
413 def genMustpassLists (mustpassLists, generator, buildCfg):
416 # Getting case lists involves invoking build, so we want to cache the results
417 build(buildCfg, generator, [GLCTS_BIN_NAME])
418 genCaseList(buildCfg, generator, "txt")
419 for mustpass in mustpassLists:
420 for package in mustpass.packages:
421 if not package.module in moduleCaseLists:
422 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module)
424 for mustpass in mustpassLists:
425 genMustpass(mustpass, moduleCaseLists)
428 genSpecCPPIncludes(mustpassLists)