• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
23import sys
24import os
25import xml.etree.cElementTree as ElementTree
26import xml.dom.minidom as minidom
27
28from build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET, GLCTS_BIN_NAME
29
30sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts"))
31
32from build.common import *
33from build.config import ANY_GENERATOR
34from build.build import build
35from fnmatch import fnmatch
36from copy import copy
37
38GENERATED_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
43class 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
51class 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
66class Package:
67	def __init__ (self, module, configurations, useforfirsteglconfig = True):
68		self.module					= module
69		self.useforfirsteglconfig	= useforfirsteglconfig
70		self.configurations			= configurations
71
72class 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
79class 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
87def getSrcDir (mustpass):
88	return os.path.join(mustpass.project.path, mustpass.version, "src")
89
90def getTmpDir (mustpass):
91	return os.path.join(mustpass.project.path, mustpass.version, "tmp")
92
93def getModuleShorthand (module):
94	return module.api.lower()
95
96def getCaseListFileName (package, configuration):
97	return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
98
99def getDstDir(mustpass):
100	return os.path.join(mustpass.project.path, mustpass.version)
101
102def getDstCaseListPath (mustpass, package, configuration):
103	return os.path.join(getDstDir(mustpass), getCaseListFileName(package, configuration))
104
105def 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
133def 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
141def getCaseList (buildCfg, generator, module):
142	return readCaseList(getCaseListPath(buildCfg, module, "txt"))
143
144def 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
153def 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
193def applyInclude (caseList, patterns, filename):
194	return applyPatterns(caseList, patterns, filename, lambda b: b)
195
196def applyExclude (caseList, patterns, filename):
197	return applyPatterns(caseList, patterns, filename, lambda b: not b)
198
199def 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
208def 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
220def include (filename):
221	return Filter(Filter.TYPE_INCLUDE, filename)
222
223def exclude (filename):
224	return Filter(Filter.TYPE_EXCLUDE, filename)
225
226def 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
231def prettifyXML (doc):
232	uglyString	= ElementTree.tostring(doc, 'utf-8')
233	reparsed	= minidom.parseString(uglyString)
234	return reparsed.toprettyxml(indent='\t', encoding='utf-8')
235
236def 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
253def getIncludeGuardName (headerFile):
254	return '_' + os.path.basename(headerFile).upper().replace('.', '_')
255
256def convertToCamelcase(s):
257    return ''.join(w.capitalize() or '_' for w in s.split('_'))
258
259def 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 == "GL46":
269		return "glu::ApiType::core(4, 6)"
270	if apiName == "GL45":
271		return "glu::ApiType::core(4, 5)"
272	if apiName == "GL44":
273		return "glu::ApiType::core(4, 4)"
274	if apiName == "GL43":
275		return "glu::ApiType::core(4, 3)"
276	if apiName == "GL42":
277		return "glu::ApiType::core(4, 2)"
278	if apiName == "GL41":
279		return "glu::ApiType::core(4, 1)"
280	if apiName == "GL40":
281		return "glu::ApiType::core(4, 0)"
282	if apiName == "GL33":
283		return "glu::ApiType::core(3, 3)"
284	if apiName == "GL32":
285		return "glu::ApiType::core(3, 2)"
286	if apiName == "GL31":
287		return "glu::ApiType::core(3, 1)"
288	if apiName == "GL30":
289		return "glu::ApiType::core(3, 0)"
290	if apiName == "EGL":
291		return "glu::ApiType()"
292
293	raise Exception("Unknown API %s" % apiName)
294	return "Unknown"
295
296def getConfigName(cfgName):
297	if cfgName == None:
298		return "DE_NULL"
299	else:
300		return '"' + cfgName + '"'
301
302def getIntBaseSeed(baseSeed):
303	if baseSeed == None:
304		return "-1"
305	else:
306		return baseSeed
307
308def genSpecCPPIncludeFile (specFilename, mustpass):
309	fileBody = ""
310
311	includeGuard = getIncludeGuardName(specFilename)
312	fileBody += "#ifndef %s\n" % includeGuard
313	fileBody += "#define %s\n" % includeGuard
314	fileBody += mustpass.project.copyright
315	fileBody += "\n\n"
316	fileBody += GENERATED_FILE_WARNING
317	fileBody += "\n\n"
318	fileBody += 'const char* mustpassDir = "' + mustpass.project.devicepath + '/' + mustpass.version + '/";\n\n'
319
320	gtf_wrapper_open = "#if defined(DEQP_GTF_AVAILABLE)\n"
321	gtf_wrapper_close = "#endif // defined(DEQP_GTF_AVAILABLE)\n"
322	android_wrapper_open = "#if DE_OS == DE_OS_ANDROID\n"
323	android_wrapper_close = "#endif // DE_OS == DE_OS_ANDROID\n"
324	TABLE_ELEM_PATTERN	= "{apiType} {configName} {glConfigName} {screenRotation} {baseSeed} {fboConfig} {surfaceWidth} {surfaceHeight}"
325
326	emitOtherCfgTbl = False
327	firstCfgDecl = "static const RunParams %s_first_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
328	firstCfgTbl = "{\n"
329
330	otherCfgDecl = "static const RunParams %s_other_cfg[] = " % mustpass.project.name.lower().replace(' ','_')
331	otherCfgTbl = "{\n"
332
333	for package in mustpass.packages:
334		for config in package.configurations:
335			pApiType = getApiType(package.module.api) + ','
336			pConfigName = '"' + config.name + '",'
337			pGLConfig = getConfigName(config.glconfig) + ','
338			pRotation = '"' + config.rotation + '",'
339			pSeed =  getIntBaseSeed(config.baseseed) + ','
340			pFBOConfig = getConfigName(config.fboconfig) + ','
341			pWidth = config.surfacewidth + ','
342			pHeight = config.surfaceheight
343			elemFinal = ""
344			elemContent = TABLE_ELEM_PATTERN.format(apiType = pApiType, configName = pConfigName, glConfigName = pGLConfig, screenRotation = pRotation, baseSeed = pSeed, fboConfig = pFBOConfig, surfaceWidth = pWidth, surfaceHeight = pHeight)
345			elem = "\t{ " + elemContent + " },\n"
346			if package.module.name[:3] == "GTF":
347				elemFinal += gtf_wrapper_open
348
349			if config.os == "android":
350				elemFinal += android_wrapper_open
351
352			elemFinal += elem
353
354			if config.os == "android":
355				elemFinal += android_wrapper_close
356
357			if package.module.name[:3] == "GTF":
358				elemFinal += gtf_wrapper_close
359
360			if package.useforfirsteglconfig == True:
361				firstCfgTbl += elemFinal
362			else:
363				otherCfgTbl += elemFinal
364				emitOtherCfgTbl = True
365
366	firstCfgTbl += "};\n"
367	otherCfgTbl += "};\n"
368
369	fileBody += firstCfgDecl
370	fileBody += firstCfgTbl
371
372	if emitOtherCfgTbl == True:
373		fileBody += "\n"
374		fileBody += otherCfgDecl
375		fileBody += otherCfgTbl
376
377	fileBody += "\n"
378	fileBody += "#endif // %s\n" % includeGuard
379	return fileBody
380
381
382def genSpecCPPIncludes (mustpassLists):
383	for mustpass in mustpassLists:
384		if mustpass.isCurrent == True:
385			specFilename	= os.path.join(mustpass.project.incpath, "glc%s.hpp" % convertToCamelcase(mustpass.project.name.lower().replace(' ','_')))
386			hpp = genSpecCPPIncludeFile(specFilename, mustpass)
387
388			print "  Writing spec: " + specFilename
389			writeFile(specFilename, hpp)
390			print "Done!"
391
392def genMustpass (mustpass, moduleCaseLists):
393	print "Generating mustpass '%s'" % mustpass.version
394
395	patternLists = readPatternLists(mustpass)
396
397	for package in mustpass.packages:
398		allCasesInPkg	= moduleCaseLists[package.module]
399
400		for config in package.configurations:
401			filtered	= applyFilters(allCasesInPkg, patternLists, config.filters)
402			dstFile		= getDstCaseListPath(mustpass, package, config)
403
404			print "  Writing deqp caselist: " + dstFile
405			writeFile(dstFile, "\n".join(filtered) + "\n")
406
407	specXML			= genSpecXML(mustpass)
408	specFilename	= os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
409
410	print "  Writing spec: " + specFilename
411	writeFile(specFilename, prettifyXML(specXML))
412
413	print "Done!"
414
415def genMustpassLists (mustpassLists, generator, buildCfg):
416	moduleCaseLists = {}
417
418	# Getting case lists involves invoking build, so we want to cache the results
419	build(buildCfg, generator, [GLCTS_BIN_NAME])
420	genCaseList(buildCfg, generator, "txt")
421	for mustpass in mustpassLists:
422		for package in mustpass.packages:
423			if not package.module in moduleCaseLists:
424				moduleCaseLists[package.module] = getCaseList(buildCfg, generator, package.module)
425
426	for mustpass in mustpassLists:
427		genMustpass(mustpass, moduleCaseLists)
428
429
430	genSpecCPPIncludes(mustpassLists)
431