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 string mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
317 fileBody += "struct RunParams\n"
319 fileBody += "\tglu::ApiType apiType;\n"
320 fileBody += "\tconst string configName;\n"
321 fileBody += "\tconst char* glConfigName;\n"
322 fileBody += "\tconst string screenRotation;\n"
323 fileBody += "\tint\t\t\t baseSeed;\n"
324 fileBody += "\tconst char* fboConfig;\n"
325 fileBody += "\tint\t\t\t surfaceWidth;\n"
326 fileBody += "\tint\t\t\t surfaceHeight;\n"
330 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n"
331 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n"
332 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n"
333 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n"
334 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}"
336 emitOtherCfgTbl = False
337 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
340 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
343 for package in mustpass.packages:
344 for config in package.configurations:
345 pApiType = getApiType(package.module.api) + ','
346 pConfigName = '"' + config.name + '",'
347 pGLConfig = getConfigName(config.glconfig) + ','
348 pRotation = '"' + config.rotation + '",'
349 pSeed = getIntBaseSeed(config.baseseed) + ','
350 pFBOConfig = getConfigName(config.fboconfig) + ','
351 pWidth = config.surfacewidth + ','
352 pHeight = config.surfaceheight
354 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight)
355 elem = "\t{ " + elemContent + " },\n"
356 if package.module.name[:3] == "GTF":
357 elemFinal += gtf_wrapper_open
359 if config.os == "android":
360 elemFinal += android_wrapper_open
364 if config.os == "android":
365 elemFinal += android_wrapper_close
367 if package.module.name[:3] == "GTF":
368 elemFinal += gtf_wrapper_close
370 if package.useforfirsteglconfig == True:
371 firstCfgTbl += elemFinal
373 otherCfgTbl += elemFinal
374 emitOtherCfgTbl = True
376 firstCfgTbl += "};\n"
377 otherCfgTbl += "};\n"
379 fileBody += firstCfgDecl
380 fileBody += firstCfgTbl
382 if emitOtherCfgTbl == True:
384 fileBody += otherCfgDecl
385 fileBody += otherCfgTbl
388 fileBody += "#endif // %s\n" % includeGuard
392 def genSpecCPPIncludes (mustpassLists):
393 for mustpass in mustpassLists:
394 if mustpass.isCurrent == True:
395 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_')))
396 hpp = genSpecCPPIncludeFile(specFilename, mustpass)
398 print " Writing spec: " + specFilename
399 writeFile(specFilename, hpp)
402 def genMustpass (mustpass, moduleCaseLists):
403 print "Generating mustpass '%s'" % mustpass.version
405 patternLists = readPatternLists(mustpass)
407 for package in mustpass.packages:
408 allCasesInPkg = moduleCaseLists[package.module]
410 for config in package.configurations:
411 filtered = applyFilters(allCasesInPkg, patternLists, config.filters)
412 dstFile = getDstCaseListPath(mustpass, package, config)
414 print " Writing deqp caselist: " + dstFile
415 writeFile(dstFile, "\n".join(filtered) + "\n")
417 specXML = genSpecXML(mustpass)
418 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
420 print " Writing spec: " + specFilename
421 writeFile(specFilename, prettifyXML(specXML))
425 def genMustpassLists (mustpassLists, generator, buildCfg):
428 # Getting case lists involves invoking build, so we want to cache the results
429 build(buildCfg, generator, [GLCTS_BIN_NAME])
430 genCaseList(buildCfg, generator, "txt")
431 for mustpass in mustpassLists:
432 for package in mustpass.packages:
433 if not package.module in moduleCaseLists:
434 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module)
436 for mustpass in mustpassLists:
437 genMustpass(mustpass, moduleCaseLists)
440 genSpecCPPIncludes(mustpassLists)