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 ctsbuild.common import *
33 from ctsbuild.config import ANY_GENERATOR
34 from ctsbuild.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", skip = "none"):
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
65 self.skipPlatform = skip
68 def __init__ (self, module, configurations, useforfirsteglconfig = True):
70 self.useforfirsteglconfig = useforfirsteglconfig
71 self.configurations = configurations
74 def __init__ (self, project, version, packages, isCurrent):
75 self.project = project
76 self.version = version
77 self.packages = packages
78 self.isCurrent = isCurrent
84 def __init__ (self, type, filename):
86 self.filename = filename
88 def getSrcDir (mustpass):
89 return os.path.join(mustpass.project.path, mustpass.version, "src")
91 def getTmpDir (mustpass):
92 return os.path.join(mustpass.project.path, mustpass.version, "tmp")
94 def getModuleShorthand (module):
95 return module.api.lower()
97 def getCaseListFileName (package, configuration):
98 return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
100 def getDstDir(mustpass):
101 return os.path.join(mustpass.project.path, mustpass.version)
103 def getDstCaseListPath (mustpass, package, configuration):
104 return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration))
106 def getCommandLine (config):
109 if config.glconfig != None:
110 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig
112 if config.rotation != None:
113 cmdLine += "--deqp-screen-rotation=%s " % config.rotation
115 if config.surfacetype != None:
116 cmdLine += "--deqp-surface-type=%s " % config.surfacetype
118 if config.surfacewidth != None:
119 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth
121 if config.surfaceheight != None:
122 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight
124 if config.baseseed != None:
125 cmdLine += "--deqp-base-seed=%s " % config.baseseed
127 if config.fboconfig != None:
128 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig
130 cmdLine += "--deqp-watchdog=disable"
134 def readCaseList (filename):
136 with open(filename, 'rt') as f:
138 if line[:6] == "TEST: ":
139 cases.append(line[6:].strip())
142 def getCaseList (buildCfg, generator, module):
143 return readCaseList(getCaseListPath(buildCfg, module, "txt"))
145 def readPatternList (filename):
147 with open(filename, 'rt') as f:
150 if len(line) > 0 and line[0] != '#':
154 def applyPatterns (caseList, patterns, filename, op):
157 curList = copy(caseList)
158 trivialPtrns = [p for p in patterns if p.find('*') < 0]
159 regularPtrns = [p for p in patterns if p.find('*') >= 0]
161 # Apply trivial (just case paths)
162 allCasesSet = set(caseList)
163 for path in trivialPtrns:
164 if path in allCasesSet:
166 errors.append((path, "Same case specified more than once"))
169 errors.append((path, "Test case not found"))
171 curList = [c for c in curList if c not in matched]
173 for pattern in regularPtrns:
174 matchedThisPtrn = set()
177 if fnmatch(case, pattern):
178 matchedThisPtrn.add(case)
180 if len(matchedThisPtrn) == 0:
181 errors.append((pattern, "Pattern didn't match any cases"))
183 matched = matched | matchedThisPtrn
184 curList = [c for c in curList if c not in matched]
186 for pattern, reason in errors:
187 print("ERROR: %s: %s" % (reason, pattern))
190 die("Found %s invalid patterns while processing file %s" % (len(errors), filename))
192 return [c for c in caseList if op(c in matched)]
194 def applyInclude (caseList, patterns, filename):
195 return applyPatterns(caseList, patterns, filename, lambda b: b)
197 def applyExclude (caseList, patterns, filename):
198 return applyPatterns(caseList, patterns, filename, lambda b: not b)
200 def readPatternLists (mustpass):
202 for package in mustpass.packages:
203 for cfg in package.configurations:
204 for filter in cfg.filters:
205 if not filter.filename in lists:
206 lists[filter.filename] = readPatternList(os.path.join(getSrcDir(mustpass), filter.filename))
209 def applyFilters (caseList, patternLists, filters):
211 for filter in filters:
212 ptrnList = patternLists[filter.filename]
213 if filter.type == Filter.TYPE_INCLUDE:
214 res = applyInclude(res, ptrnList, filter.filename)
216 assert filter.type == Filter.TYPE_EXCLUDE
217 res = applyExclude(res, ptrnList, filter.filename)
221 def include (filename):
222 return Filter(Filter.TYPE_INCLUDE, filename)
224 def exclude (filename):
225 return Filter(Filter.TYPE_EXCLUDE, filename)
227 def insertXMLHeaders (mustpass, doc):
228 if mustpass.project.copyright != None:
229 doc.insert(0, ElementTree.Comment(mustpass.project.copyright))
230 doc.insert(1, ElementTree.Comment(GENERATED_FILE_WARNING))
232 def prettifyXML (doc):
233 uglyString = ElementTree.tostring(doc, 'utf-8')
234 reparsed = minidom.parseString(uglyString)
235 return reparsed.toprettyxml(indent='\t', encoding='utf-8')
237 def genSpecXML (mustpass):
238 mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version)
239 insertXMLHeaders(mustpass, mustpassElem)
241 packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name)
243 for package in mustpass.packages:
244 for config in package.configurations:
245 configElem = ElementTree.SubElement(packageElem, "Configuration",
246 caseListFile = getCaseListFileName(package, config),
247 commandLine = getCommandLine(config),
250 useForFirstEGLConfig = str(package.useforfirsteglconfig)
255 def getIncludeGuardName (headerFile):
256 return '_' + os.path.basename(headerFile).upper().replace('.', '_')
258 def convertToCamelcase(s):
259 return ''.join(w.capitalize() or '_' for w in s.split('_'))
261 def getApiType(apiName):
262 if apiName == "GLES2":
263 return "glu::ApiType::es(2, 0)"
264 if apiName == "GLES3":
265 return "glu::ApiType::es(3, 0)"
266 if apiName == "GLES31":
267 return "glu::ApiType::es(3, 1)"
268 if apiName == "GLES32":
269 return "glu::ApiType::es(3, 2)"
270 if apiName == "GL46":
271 return "glu::ApiType::core(4, 6)"
272 if apiName == "GL45":
273 return "glu::ApiType::core(4, 5)"
274 if apiName == "GL44":
275 return "glu::ApiType::core(4, 4)"
276 if apiName == "GL43":
277 return "glu::ApiType::core(4, 3)"
278 if apiName == "GL42":
279 return "glu::ApiType::core(4, 2)"
280 if apiName == "GL41":
281 return "glu::ApiType::core(4, 1)"
282 if apiName == "GL40":
283 return "glu::ApiType::core(4, 0)"
284 if apiName == "GL33":
285 return "glu::ApiType::core(3, 3)"
286 if apiName == "GL32":
287 return "glu::ApiType::core(3, 2)"
288 if apiName == "GL31":
289 return "glu::ApiType::core(3, 1)"
290 if apiName == "GL30":
291 return "glu::ApiType::core(3, 0)"
293 return "glu::ApiType()"
294 if apiName == "GL42-COMPAT":
295 return "glu::ApiType::compatibility(4, 2)"
297 raise Exception("Unknown API %s" % apiName)
300 def getConfigName(cfgName):
304 return '"' + cfgName + '"'
306 def getIntBaseSeed(baseSeed):
312 def genSpecCPPIncludeFile (specFilename, mustpass):
315 includeGuard = getIncludeGuardName(specFilename)
316 fileBody += "#ifndef %s\n" % includeGuard
317 fileBody += "#define %s\n" % includeGuard
318 fileBody += mustpass.project.copyright
320 fileBody += GENERATED_FILE_WARNING
322 fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
324 gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n"
325 gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n"
326 android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n"
327 android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n"
328 skip_x11_wrapper_open = "#ifndef DEQP_SUPPORT_X11\n"
329 skip_x11_wrapper_close = "#endif // DEQP_SUPPORT_X11\n"
330 TABLE_ELEM_PATTERN = "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}"
332 emitOtherCfgTbl = False
333 firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
336 otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
339 for package in mustpass.packages:
340 for config in package.configurations:
341 pApiType = getApiType(package.module.api) + ','
342 pConfigName = '"' + config.name + '",'
343 pGLConfig = getConfigName(config.glconfig) + ','
344 pRotation = '"' + config.rotation + '",'
345 pSeed = getIntBaseSeed(config.baseseed) + ','
346 pFBOConfig = getConfigName(config.fboconfig) + ','
347 pWidth = config.surfacewidth + ','
348 pHeight = config.surfaceheight
350 elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight)
351 elem = "\t{ " + elemContent + " },\n"
352 if package.module.name[:3] == "GTF":
353 elemFinal += gtf_wrapper_open
355 if config.os == "android":
356 elemFinal += android_wrapper_open
358 if config.skipPlatform == "x11":
359 elemFinal += skip_x11_wrapper_open
363 if config.skipPlatform == "x11":
364 elemFinal += skip_x11_wrapper_close
366 if config.os == "android":
367 elemFinal += android_wrapper_close
369 if package.module.name[:3] == "GTF":
370 elemFinal += gtf_wrapper_close
372 if package.useforfirsteglconfig == True:
373 firstCfgTbl += elemFinal
375 otherCfgTbl += elemFinal
376 emitOtherCfgTbl = True
378 firstCfgTbl += "};\n"
379 otherCfgTbl += "};\n"
381 fileBody += firstCfgDecl
382 fileBody += firstCfgTbl
384 if emitOtherCfgTbl == True:
386 fileBody += otherCfgDecl
387 fileBody += otherCfgTbl
390 fileBody += "#endif // %s\n" % includeGuard
394 def genSpecCPPIncludes (mustpassLists):
395 for mustpass in mustpassLists:
396 if mustpass.isCurrent == True:
397 specFilename = os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_')))
398 hpp = genSpecCPPIncludeFile(specFilename, mustpass)
400 print(" Writing spec: " + specFilename)
401 writeFile(specFilename, hpp)
404 def genMustpass (mustpass, moduleCaseLists):
405 print("Generating mustpass '%s'" % mustpass.version)
407 patternLists = readPatternLists(mustpass)
409 for package in mustpass.packages:
410 allCasesInPkg = moduleCaseLists[package.module]
412 for config in package.configurations:
413 filtered = applyFilters(allCasesInPkg, patternLists, config.filters)
414 dstFile = getDstCaseListPath(mustpass, package, config)
416 print(" Writing deqp caselist: " + dstFile)
417 writeFile(dstFile, "\n".join(filtered) + "\n")
419 specXML = genSpecXML(mustpass)
420 specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
422 print(" Writing spec: " + specFilename)
423 writeFile(specFilename, prettifyXML(specXML).decode())
427 def genMustpassLists (mustpassLists, generator, buildCfg):
430 # Getting case lists involves invoking build, so we want to cache the results
431 build(buildCfg, generator, [GLCTS_BIN_NAME])
432 genCaseList(buildCfg, generator, "txt")
433 for mustpass in mustpassLists:
434 for package in mustpass.packages:
435 if not package.module in moduleCaseLists:
436 moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module)
438 for mustpass in mustpassLists:
439 genMustpass(mustpass, moduleCaseLists)
442 genSpecCPPIncludes(mustpassLists)