resolve merge conflicts of eb48f49dd to oc-dev am: 7e37841e4f -s ours
[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 build.common import *
33 from build.config import ANY_GENERATOR
34 from build.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"):
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
66 class Package:
67         def __init__ (self, module, configurations, useforfirsteglconfig = True):
68                 self.module                                     = module
69                 self.useforfirsteglconfig       = useforfirsteglconfig
70                 self.configurations                     = configurations
71
72 class Mustpass:
73         def __init__ (self, project, version, packages, isCurrent):
74                 self.project            = project
75                 self.version            = version
76                 self.packages           = packages
77                 self.isCurrent          = isCurrent
78
79 class Filter:
80         TYPE_INCLUDE = 0
81         TYPE_EXCLUDE = 1
82
83         def __init__ (self, type, filename):
84                 self.type               = type
85                 self.filename   = filename
86
87 def getSrcDir (mustpass):
88         return os.path.join(mustpass.project.path, mustpass.version, "src")
89
90 def getTmpDir (mustpass):
91         return os.path.join(mustpass.project.path, mustpass.version, "tmp")
92
93 def getModuleShorthand (module):
94         return module.api.lower()
95
96 def getCaseListFileName (package, configuration):
97         return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
98
99 def getDstDir(mustpass):
100         return os.path.join(mustpass.project.path, mustpass.version)
101
102 def getDstCaseListPath (mustpass, package, configuration):
103         return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration))
104
105 def getCommandLine (config):
106         cmdLine = ""
107
108         if config.glconfig != None:
109                 cmdLine += "--deqp-gl-config-name=%s " % config.glconfig
110
111         if config.rotation != None:
112                 cmdLine += "--deqp-screen-rotation=%s " % config.rotation
113
114         if config.surfacetype != None:
115                 cmdLine += "--deqp-surface-type=%s " % config.surfacetype
116
117         if config.surfacewidth != None:
118                 cmdLine += "--deqp-surface-width=%s " % config.surfacewidth
119
120         if config.surfaceheight != None:
121                 cmdLine += "--deqp-surface-height=%s " % config.surfaceheight
122
123         if config.baseseed != None:
124                 cmdLine += "--deqp-base-seed=%s " % config.baseseed
125
126         if config.fboconfig != None:
127                 cmdLine += "--deqp-gl-config-name=%s --deqp-surface-type=fbo " % config.fboconfig
128
129         cmdLine += "--deqp-watchdog=disable"
130
131         return cmdLine
132
133 def readCaseList (filename):
134         cases = []
135         with open(filename, 'rb') as f:
136                 for line in f:
137                         if line[:6] == "TEST: ":
138                                 cases.append(line[6:].strip())
139         return cases
140
141 def getCaseList (buildCfg, generator, module):
142         return readCaseList(getCaseListPath(buildCfg, module, "txt"))
143
144 def readPatternList (filename):
145         ptrns = []
146         with open(filename, 'rb') as f:
147                 for line in f:
148                         line = line.strip()
149                         if len(line) > 0 and line[0] != '#':
150                                 ptrns.append(line)
151         return ptrns
152
153 def applyPatterns (caseList, patterns, filename, op):
154         matched                 = set()
155         errors                  = []
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]
159
160         # Apply trivial (just case paths)
161         allCasesSet             = set(caseList)
162         for path in trivialPtrns:
163                 if path in allCasesSet:
164                         if path in matched:
165                                 errors.append((path, "Same case specified more than once"))
166                         matched.add(path)
167                 else:
168                         errors.append((path, "Test case not found"))
169
170         curList = [c for c in curList if c not in matched]
171
172         for pattern in regularPtrns:
173                 matchedThisPtrn = set()
174
175                 for case in curList:
176                         if fnmatch(case, pattern):
177                                 matchedThisPtrn.add(case)
178
179                 if len(matchedThisPtrn) == 0:
180                         errors.append((pattern, "Pattern didn't match any cases"))
181
182                 matched = matched | matchedThisPtrn
183                 curList = [c for c in curList if c not in matched]
184
185         for pattern, reason in errors:
186                 print "ERROR: %s: %s" % (reason, pattern)
187
188         if len(errors) > 0:
189                 die("Found %s invalid patterns while processing file %s" % (len(errors), filename))
190
191         return [c for c in caseList if op(c in matched)]
192
193 def applyInclude (caseList, patterns, filename):
194         return applyPatterns(caseList, patterns, filename, lambda b: b)
195
196 def applyExclude (caseList, patterns, filename):
197         return applyPatterns(caseList, patterns, filename, lambda b: not b)
198
199 def readPatternLists (mustpass):
200         lists = {}
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))
206         return lists
207
208 def applyFilters (caseList, patternLists, filters):
209         res = copy(caseList)
210         for filter in filters:
211                 ptrnList = patternLists[filter.filename]
212                 if filter.type == Filter.TYPE_INCLUDE:
213                         res = applyInclude(res, ptrnList, filter.filename)
214                 else:
215                         assert filter.type == Filter.TYPE_EXCLUDE
216                         res = applyExclude(res, ptrnList, filter.filename)
217         return res
218
219
220 def include (filename):
221         return Filter(Filter.TYPE_INCLUDE, filename)
222
223 def exclude (filename):
224         return Filter(Filter.TYPE_EXCLUDE, filename)
225
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))
230
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')
235
236 def genSpecXML (mustpass):
237         mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version)
238         insertXMLHeaders(mustpass, mustpassElem)
239
240         packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = mustpass.project.name)
241
242         for package in mustpass.packages:
243                 for config in package.configurations:
244                         configElem = ElementTree.SubElement(packageElem, "Configuration",
245                                                         useForFirstEGLConfig    = str(package.useforfirsteglconfig),
246                                                         name                                    = config.name,
247                                                         caseListFile                    = getCaseListFileName(package, config),
248                                                         commandLine                             = getCommandLine(config),
249                                                         os                                              = str(config.os))
250
251         return mustpassElem
252
253 def getIncludeGuardName (headerFile):
254         return '_' + os.path.basename(headerFile).upper().replace('.', '_')
255
256 def convertToCamelcase(s):
257     return ''.join(w.capitalize() or '_' for w in s.split('_'))
258
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)"
288         if apiName == "EGL" or apiName == "NOCTX":
289                 return "glu::ApiType()"
290
291         raise Exception("Unknown API %s" % apiName)
292         return "Unknown"
293
294 def getConfigName(cfgName):
295         if cfgName == None:
296                 return "DE_NULL"
297         else:
298                 return '"' + cfgName + '"'
299
300 def getIntBaseSeed(baseSeed):
301         if baseSeed == None:
302                 return "-1"
303         else:
304                 return baseSeed
305
306 def genSpecCPPIncludeFile (specFilename, mustpass):
307         fileBody = ""
308
309         includeGuard = getIncludeGuardName(specFilename)
310         fileBody += "#ifndef %s\n" % includeGuard
311         fileBody += "#define %s\n" % includeGuard
312         fileBody += mustpass.project.copyright
313         fileBody += "\n\n"
314         fileBody += GENERATED_FILE_WARNING
315         fileBody += "\n\n"
316         fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
317
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}"
323
324         emitOtherCfgTbl = False
325         firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
326         firstCfgTbl = "{\n"
327
328         otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
329         otherCfgTbl = "{\n"
330
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
341                         elemFinal = ""
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
346
347                         if config.os == "android":
348                                 elemFinal += android_wrapper_open
349
350                         elemFinal += elem
351
352                         if config.os == "android":
353                                 elemFinal += android_wrapper_close
354
355                         if package.module.name[:3] == "GTF":
356                                 elemFinal += gtf_wrapper_close
357
358                         if package.useforfirsteglconfig == True:
359                                 firstCfgTbl += elemFinal
360                         else:
361                                 otherCfgTbl += elemFinal
362                                 emitOtherCfgTbl = True
363
364         firstCfgTbl += "};\n"
365         otherCfgTbl += "};\n"
366
367         fileBody += firstCfgDecl
368         fileBody += firstCfgTbl
369
370         if emitOtherCfgTbl == True:
371                 fileBody += "\n"
372                 fileBody += otherCfgDecl
373                 fileBody += otherCfgTbl
374
375         fileBody += "\n"
376         fileBody += "#endif // %s\n" % includeGuard
377         return fileBody
378
379
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)
385
386                         print "  Writing spec: " + specFilename
387                         writeFile(specFilename, hpp)
388                         print "Done!"
389
390 def genMustpass (mustpass, moduleCaseLists):
391         print "Generating mustpass '%s'" % mustpass.version
392
393         patternLists = readPatternLists(mustpass)
394
395         for package in mustpass.packages:
396                 allCasesInPkg   = moduleCaseLists[package.module]
397
398                 for config in package.configurations:
399                         filtered        = applyFilters(allCasesInPkg, patternLists, config.filters)
400                         dstFile         = getDstCaseListPath(mustpass, package, config)
401
402                         print "  Writing deqp caselist: " + dstFile
403                         writeFile(dstFile, "\n".join(filtered) + "\n")
404
405         specXML                 = genSpecXML(mustpass)
406         specFilename    = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
407
408         print "  Writing spec: " + specFilename
409         writeFile(specFilename, prettifyXML(specXML))
410
411         print "Done!"
412
413 def genMustpassLists (mustpassLists, generator, buildCfg):
414         moduleCaseLists = {}
415
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)
423
424         for mustpass in mustpassLists:
425                 genMustpass(mustpass, moduleCaseLists)
426
427
428         genSpecCPPIncludes(mustpassLists)