Fix missing dependency on sparse binds
[platform/upstream/VK-GL-CTS.git] / external / openglcts / scripts / mustpass.py
1 # -*- coding: utf-8 -*-
2
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
6 #
7 # Copyright 2016 The Android Open Source Project
8 #
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
12 #
13 #      http://www.apache.org/licenses/LICENSE-2.0
14 #
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.
20 #
21 #-------------------------------------------------------------------------
22
23 import sys
24 import os
25 import xml.etree.cElementTree as ElementTree
26 import xml.dom.minidom as minidom
27
28 from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME
29
30 sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts"))
31
32 from ctsbuild.common import *
33 from ctsbuild.config import ANY_GENERATOR
34 from ctsbuild.build import build
35 from fnmatch import fnmatch
36 from copy import copy
37
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.
41  */"""
42
43 class Project:
44         def __init__ (self, name, path, incpath, devicepath, copyright = None):
45                 self.name               = name
46                 self.path               = path
47                 self.incpath    = incpath
48                 self.devicepath = devicepath
49                 self.copyright  = copyright
50
51 class Configuration:
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"):
53                 self.name                               = name
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
64                 self.os                                 = os
65                 self.skipPlatform               = skip
66
67 class Package:
68         def __init__ (self, module, configurations, useforfirsteglconfig = True):
69                 self.module                                     = module
70                 self.useforfirsteglconfig       = useforfirsteglconfig
71                 self.configurations                     = configurations
72
73 class Mustpass:
74         def __init__ (self, project, version, packages, isCurrent):
75                 self.project            = project
76                 self.version            = version
77                 self.packages           = packages
78                 self.isCurrent          = isCurrent
79
80 class Filter:
81         TYPE_INCLUDE = 0
82         TYPE_EXCLUDE = 1
83
84         def __init__ (self, type, filename):
85                 self.type               = type
86                 self.filename   = filename
87
88 def getSrcDir (mustpass):
89         return os.path.join(mustpass.project.path, mustpass.version, "src")
90
91 def getTmpDir (mustpass):
92         return os.path.join(mustpass.project.path, mustpass.version, "tmp")
93
94 def getModuleShorthand (module):
95         return module.api.lower()
96
97 def getCaseListFileName (package, configuration):
98         return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
99
100 def getDstDir(mustpass):
101         return os.path.join(mustpass.project.path, mustpass.version)
102
103 def getDstCaseListPath (mustpass, package, configuration):
104         return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration))
105
106 def getCommandLine (config):
107         cmdLine = ""
108
109         if config.glconfig != None:
110                 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig
111
112         if config.rotation != None:
113                 cmdLine += "--deqp-screen-rotation=%s " % config.rotation
114
115         if config.surfacetype != None:
116                 cmdLine += "--deqp-surface-type=%s " % config.surfacetype
117
118         if config.surfacewidth != None:
119                 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth
120
121         if config.surfaceheight != None:
122                 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight
123
124         if config.baseseed != None:
125                 cmdLine += "--deqp-base-seed=%s " % config.baseseed
126
127         if config.fboconfig != None:
128                 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig
129
130         cmdLine += "--deqp-watchdog=disable"
131
132         return cmdLine
133
134 def readCaseList (filename):
135         cases = []
136         with open(filename, 'rt') as f:
137                 for line in f:
138                         if line[:6] == "TEST: ":
139                                 cases.append(line[6:].strip())
140         return cases
141
142 def getCaseList (buildCfg, generator, module):
143         return readCaseList(getCaseListPath(buildCfg, module, "txt"))
144
145 def readPatternList (filename):
146         ptrns = []
147         with open(filename, 'rt') as f:
148                 for line in f:
149                         line = line.strip()
150                         if len(line) > 0 and line[0] != '#':
151                                 ptrns.append(line)
152         return ptrns
153
154 def applyPatterns (caseList, patterns, filename, op):
155         matched                 = set()
156         errors                  = []
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]
160
161         # Apply trivial (just case paths)
162         allCasesSet             = set(caseList)
163         for path in trivialPtrns:
164                 if path in allCasesSet:
165                         if path in matched:
166                                 errors.append((path, "Same case specified more than once"))
167                         matched.add(path)
168                 else:
169                         errors.append((path, "Test case not found"))
170
171         curList = [c for c in curList if c not in matched]
172
173         for pattern in regularPtrns:
174                 matchedThisPtrn = set()
175
176                 for case in curList:
177                         if fnmatch(case, pattern):
178                                 matchedThisPtrn.add(case)
179
180                 if len(matchedThisPtrn) == 0:
181                         errors.append((pattern, "Pattern didn't match any cases"))
182
183                 matched = matched | matchedThisPtrn
184                 curList = [c for c in curList if c not in matched]
185
186         for pattern, reason in errors:
187                 print("ERROR: %s: %s" % (reason, pattern))
188
189         if len(errors) > 0:
190                 die("Found %s invalid patterns while processing file %s" % (len(errors), filename))
191
192         return [c for c in caseList if op(c in matched)]
193
194 def applyInclude (caseList, patterns, filename):
195         return applyPatterns(caseList, patterns, filename, lambda b: b)
196
197 def applyExclude (caseList, patterns, filename):
198         return applyPatterns(caseList, patterns, filename, lambda b: not b)
199
200 def readPatternLists (mustpass):
201         lists = {}
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))
207         return lists
208
209 def applyFilters (caseList, patternLists, filters):
210         res = copy(caseList)
211         for filter in filters:
212                 ptrnList = patternLists[filter.filename]
213                 if filter.type == Filter.TYPE_INCLUDE:
214                         res = applyInclude(res, ptrnList, filter.filename)
215                 else:
216                         assert filter.type == Filter.TYPE_EXCLUDE
217                         res = applyExclude(res, ptrnList, filter.filename)
218         return res
219
220
221 def include (filename):
222         return Filter(Filter.TYPE_INCLUDE, filename)
223
224 def exclude (filename):
225         return Filter(Filter.TYPE_EXCLUDE, filename)
226
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))
231
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')
236
237 def genSpecXML (mustpass):
238         mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version)
239         insertXMLHeaders(mustpass, mustpassElem)
240
241         packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name)
242
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),
248                                                         name                                    = config.name,
249                                                         os                                              = str(config.os),
250                                                         useForFirstEGLConfig    = str(package.useforfirsteglconfig)
251                                                         )
252
253         return mustpassElem
254
255 def getIncludeGuardName (headerFile):
256         return '_' + os.path.basename(headerFile).upper().replace('.', '_')
257
258 def convertToCamelcase(s):
259     return ''.join(w.capitalize() or '_' for w in s.split('_'))
260
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)"
292         if apiName == "EGL":
293                 return "glu::ApiType()"
294         if apiName == "GL42-COMPAT":
295                 return "glu::ApiType::compatibility(4, 2)"
296
297         raise Exception("Unknown API %s" % apiName)
298         return "Unknown"
299
300 def getConfigName(cfgName):
301         if cfgName == None:
302                 return "DE_NULL"
303         else:
304                 return '"' + cfgName + '"'
305
306 def getIntBaseSeed(baseSeed):
307         if baseSeed == None:
308                 return "-1"
309         else:
310                 return baseSeed
311
312 def genSpecCPPIncludeFile (specFilename, mustpass):
313         fileBody = ""
314
315         includeGuard = getIncludeGuardName(specFilename)
316         fileBody += "#ifndef %s\n" % includeGuard
317         fileBody += "#define %s\n" % includeGuard
318         fileBody += mustpass.project.copyright
319         fileBody += "\n\n"
320         fileBody += GENERATED_FILE_WARNING
321         fileBody += "\n\n"
322         fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
323
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}"
331
332         emitOtherCfgTbl = False
333         firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
334         firstCfgTbl = "{\n"
335
336         otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
337         otherCfgTbl = "{\n"
338
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
349                         elemFinal = ""
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
354
355                         if config.os == "android":
356                                 elemFinal += android_wrapper_open
357
358                         if config.skipPlatform == "x11":
359                                 elemFinal += skip_x11_wrapper_open
360
361                         elemFinal += elem
362
363                         if config.skipPlatform == "x11":
364                                 elemFinal += skip_x11_wrapper_close
365
366                         if config.os == "android":
367                                 elemFinal += android_wrapper_close
368
369                         if package.module.name[:3] == "GTF":
370                                 elemFinal += gtf_wrapper_close
371
372                         if package.useforfirsteglconfig == True:
373                                 firstCfgTbl += elemFinal
374                         else:
375                                 otherCfgTbl += elemFinal
376                                 emitOtherCfgTbl = True
377
378         firstCfgTbl += "};\n"
379         otherCfgTbl += "};\n"
380
381         fileBody += firstCfgDecl
382         fileBody += firstCfgTbl
383
384         if emitOtherCfgTbl == True:
385                 fileBody += "\n"
386                 fileBody += otherCfgDecl
387                 fileBody += otherCfgTbl
388
389         fileBody += "\n"
390         fileBody += "#endif // %s\n" % includeGuard
391         return fileBody
392
393
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)
399
400                         print("  Writing spec: " + specFilename)
401                         writeFile(specFilename, hpp)
402                         print("Done!")
403
404 def genMustpass (mustpass, moduleCaseLists):
405         print("Generating mustpass '%s'" % mustpass.version)
406
407         patternLists = readPatternLists(mustpass)
408
409         for package in mustpass.packages:
410                 allCasesInPkg   = moduleCaseLists[package.module]
411
412                 for config in package.configurations:
413                         filtered        = applyFilters(allCasesInPkg, patternLists, config.filters)
414                         dstFile         = getDstCaseListPath(mustpass, package, config)
415
416                         print("  Writing deqp caselist: " + dstFile)
417                         writeFile(dstFile, "\n".join(filtered) + "\n")
418
419         specXML                 = genSpecXML(mustpass)
420         specFilename    = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
421
422         print("  Writing spec: " + specFilename)
423         writeFile(specFilename, prettifyXML(specXML).decode())
424
425         print("Done!")
426
427 def genMustpassLists (mustpassLists, generator, buildCfg):
428         moduleCaseLists = {}
429
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)
437
438         for mustpass in mustpassLists:
439                 genMustpass(mustpass, moduleCaseLists)
440
441
442         genSpecCPPIncludes(mustpassLists)