1e5c31af7Sopenharmony_ci# -*- coding: utf-8 -*-
2e5c31af7Sopenharmony_ciimport logging
3e5c31af7Sopenharmony_ci
4e5c31af7Sopenharmony_ci#-------------------------------------------------------------------------
5e5c31af7Sopenharmony_ci# drawElements Quality Program utilities
6e5c31af7Sopenharmony_ci# --------------------------------------
7e5c31af7Sopenharmony_ci#
8e5c31af7Sopenharmony_ci# Copyright 2016 The Android Open Source Project
9e5c31af7Sopenharmony_ci#
10e5c31af7Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
11e5c31af7Sopenharmony_ci# you may not use this file except in compliance with the License.
12e5c31af7Sopenharmony_ci# You may obtain a copy of the License at
13e5c31af7Sopenharmony_ci#
14e5c31af7Sopenharmony_ci#      http://www.apache.org/licenses/LICENSE-2.0
15e5c31af7Sopenharmony_ci#
16e5c31af7Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
17e5c31af7Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
18e5c31af7Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19e5c31af7Sopenharmony_ci# See the License for the specific language governing permissions and
20e5c31af7Sopenharmony_ci# limitations under the License.
21e5c31af7Sopenharmony_ci#
22e5c31af7Sopenharmony_ci#-------------------------------------------------------------------------
23e5c31af7Sopenharmony_ci
24e5c31af7Sopenharmony_cifrom ctsbuild.common import *
25e5c31af7Sopenharmony_cifrom ctsbuild.build import build
26e5c31af7Sopenharmony_cifrom build_caselists import Module, getModuleByName, getBuildConfig, genCaseList, getCaseListPath, DEFAULT_BUILD_DIR, DEFAULT_TARGET
27e5c31af7Sopenharmony_cifrom fnmatch import fnmatch
28e5c31af7Sopenharmony_cifrom copy import copy
29e5c31af7Sopenharmony_cifrom collections import defaultdict
30e5c31af7Sopenharmony_ci
31e5c31af7Sopenharmony_ciimport argparse
32e5c31af7Sopenharmony_ciimport re
33e5c31af7Sopenharmony_ciimport xml.etree.cElementTree as ElementTree
34e5c31af7Sopenharmony_ciimport xml.dom.minidom as minidom
35e5c31af7Sopenharmony_ci
36e5c31af7Sopenharmony_ciGENERATED_FILE_WARNING = """
37e5c31af7Sopenharmony_ci     This file has been automatically generated. Edit with caution.
38e5c31af7Sopenharmony_ci     """
39e5c31af7Sopenharmony_ci
40e5c31af7Sopenharmony_ciclass Project:
41e5c31af7Sopenharmony_ci	def __init__ (self, path, copyright = None):
42e5c31af7Sopenharmony_ci		self.path		= path
43e5c31af7Sopenharmony_ci		self.copyright	= copyright
44e5c31af7Sopenharmony_ci
45e5c31af7Sopenharmony_ciclass Configuration:
46e5c31af7Sopenharmony_ci	def __init__ (self, name, filters, glconfig = None, rotation = None, surfacetype = None, required = False, runtime = None, runByDefault = True, listOfGroupsToSplit = []):
47e5c31af7Sopenharmony_ci		self.name					= name
48e5c31af7Sopenharmony_ci		self.glconfig				= glconfig
49e5c31af7Sopenharmony_ci		self.rotation				= rotation
50e5c31af7Sopenharmony_ci		self.surfacetype			= surfacetype
51e5c31af7Sopenharmony_ci		self.required				= required
52e5c31af7Sopenharmony_ci		self.filters				= filters
53e5c31af7Sopenharmony_ci		self.expectedRuntime		= runtime
54e5c31af7Sopenharmony_ci		self.runByDefault			= runByDefault
55e5c31af7Sopenharmony_ci		self.listOfGroupsToSplit	= listOfGroupsToSplit
56e5c31af7Sopenharmony_ci
57e5c31af7Sopenharmony_ciclass Package:
58e5c31af7Sopenharmony_ci	def __init__ (self, module, configurations):
59e5c31af7Sopenharmony_ci		self.module			= module
60e5c31af7Sopenharmony_ci		self.configurations	= configurations
61e5c31af7Sopenharmony_ci
62e5c31af7Sopenharmony_ciclass Mustpass:
63e5c31af7Sopenharmony_ci	def __init__ (self, project, version, packages):
64e5c31af7Sopenharmony_ci		self.project	= project
65e5c31af7Sopenharmony_ci		self.version	= version
66e5c31af7Sopenharmony_ci		self.packages	= packages
67e5c31af7Sopenharmony_ci
68e5c31af7Sopenharmony_ciclass Filter:
69e5c31af7Sopenharmony_ci	TYPE_INCLUDE = 0
70e5c31af7Sopenharmony_ci	TYPE_EXCLUDE = 1
71e5c31af7Sopenharmony_ci
72e5c31af7Sopenharmony_ci	def __init__ (self, type, filenames):
73e5c31af7Sopenharmony_ci		self.type		= type
74e5c31af7Sopenharmony_ci		self.filenames	= filenames
75e5c31af7Sopenharmony_ci		self.key		= ",".join(filenames)
76e5c31af7Sopenharmony_ci
77e5c31af7Sopenharmony_ciclass TestRoot:
78e5c31af7Sopenharmony_ci	def __init__ (self):
79e5c31af7Sopenharmony_ci		self.children	= []
80e5c31af7Sopenharmony_ci
81e5c31af7Sopenharmony_ciclass TestGroup:
82e5c31af7Sopenharmony_ci	def __init__ (self, name):
83e5c31af7Sopenharmony_ci		self.name		= name
84e5c31af7Sopenharmony_ci		self.children	= []
85e5c31af7Sopenharmony_ci
86e5c31af7Sopenharmony_ciclass TestCase:
87e5c31af7Sopenharmony_ci	def __init__ (self, name):
88e5c31af7Sopenharmony_ci		self.name			= name
89e5c31af7Sopenharmony_ci		self.configurations	= []
90e5c31af7Sopenharmony_ci
91e5c31af7Sopenharmony_cidef getSrcDir (mustpass):
92e5c31af7Sopenharmony_ci	return os.path.join(mustpass.project.path, mustpass.version, "src")
93e5c31af7Sopenharmony_ci
94e5c31af7Sopenharmony_cidef getModuleShorthand (module):
95e5c31af7Sopenharmony_ci	assert module.name[:5] == "dEQP-"
96e5c31af7Sopenharmony_ci	return module.name[5:].lower()
97e5c31af7Sopenharmony_ci
98e5c31af7Sopenharmony_cidef getCaseListFileName (package, configuration):
99e5c31af7Sopenharmony_ci	return "%s-%s.txt" % (getModuleShorthand(package.module), configuration.name)
100e5c31af7Sopenharmony_ci
101e5c31af7Sopenharmony_cidef getDstCaseListPath (mustpass):
102e5c31af7Sopenharmony_ci	return os.path.join(mustpass.project.path, mustpass.version)
103e5c31af7Sopenharmony_ci
104e5c31af7Sopenharmony_cidef getCommandLine (config):
105e5c31af7Sopenharmony_ci	cmdLine = ""
106e5c31af7Sopenharmony_ci
107e5c31af7Sopenharmony_ci	if config.glconfig != None:
108e5c31af7Sopenharmony_ci		cmdLine += "--deqp-gl-config-name=%s " % config.glconfig
109e5c31af7Sopenharmony_ci
110e5c31af7Sopenharmony_ci	if config.rotation != None:
111e5c31af7Sopenharmony_ci		cmdLine += "--deqp-screen-rotation=%s " % config.rotation
112e5c31af7Sopenharmony_ci
113e5c31af7Sopenharmony_ci	if config.surfacetype != None:
114e5c31af7Sopenharmony_ci		cmdLine += "--deqp-surface-type=%s " % config.surfacetype
115e5c31af7Sopenharmony_ci
116e5c31af7Sopenharmony_ci	cmdLine += "--deqp-watchdog=enable"
117e5c31af7Sopenharmony_ci
118e5c31af7Sopenharmony_ci	return cmdLine
119e5c31af7Sopenharmony_ci
120e5c31af7Sopenharmony_ciclass CaseList:
121e5c31af7Sopenharmony_ci	def __init__(self, filePath, sortedLines):
122e5c31af7Sopenharmony_ci		self.filePath = filePath
123e5c31af7Sopenharmony_ci		self.sortedLines = sortedLines
124e5c31af7Sopenharmony_ci
125e5c31af7Sopenharmony_cidef readAndSortCaseList (buildCfg, generator, module):
126e5c31af7Sopenharmony_ci	build(buildCfg, generator, [module.binName])
127e5c31af7Sopenharmony_ci	genCaseList(buildCfg, generator, module, "txt")
128e5c31af7Sopenharmony_ci	filePath = getCaseListPath(buildCfg, module, "txt")
129e5c31af7Sopenharmony_ci	with open(filePath, 'r') as first_file:
130e5c31af7Sopenharmony_ci		lines = first_file.readlines()
131e5c31af7Sopenharmony_ci		lines.sort()
132e5c31af7Sopenharmony_ci		caseList = CaseList(filePath, lines)
133e5c31af7Sopenharmony_ci		return caseList
134e5c31af7Sopenharmony_ci
135e5c31af7Sopenharmony_cidef readPatternList (filename, patternList):
136e5c31af7Sopenharmony_ci	with open(filename, 'rt') as f:
137e5c31af7Sopenharmony_ci		for line in f:
138e5c31af7Sopenharmony_ci			line = line.strip()
139e5c31af7Sopenharmony_ci			if len(line) > 0 and line[0] != '#':
140e5c31af7Sopenharmony_ci				patternList.append(line)
141e5c31af7Sopenharmony_ci
142e5c31af7Sopenharmony_cidef include (*filenames):
143e5c31af7Sopenharmony_ci	return Filter(Filter.TYPE_INCLUDE, filenames)
144e5c31af7Sopenharmony_ci
145e5c31af7Sopenharmony_cidef exclude (*filenames):
146e5c31af7Sopenharmony_ci	return Filter(Filter.TYPE_EXCLUDE, filenames)
147e5c31af7Sopenharmony_ci
148e5c31af7Sopenharmony_cidef insertXMLHeaders (mustpass, doc):
149e5c31af7Sopenharmony_ci	if mustpass.project.copyright != None:
150e5c31af7Sopenharmony_ci		doc.insert(0, ElementTree.Comment(mustpass.project.copyright))
151e5c31af7Sopenharmony_ci	doc.insert(1, ElementTree.Comment(GENERATED_FILE_WARNING))
152e5c31af7Sopenharmony_ci
153e5c31af7Sopenharmony_cidef prettifyXML (doc):
154e5c31af7Sopenharmony_ci	uglyString	= ElementTree.tostring(doc, 'utf-8')
155e5c31af7Sopenharmony_ci	reparsed	= minidom.parseString(uglyString)
156e5c31af7Sopenharmony_ci	return reparsed.toprettyxml(indent='\t', encoding='utf-8')
157e5c31af7Sopenharmony_ci
158e5c31af7Sopenharmony_cidef genSpecXML (mustpass):
159e5c31af7Sopenharmony_ci	mustpassElem = ElementTree.Element("Mustpass", version = mustpass.version)
160e5c31af7Sopenharmony_ci	insertXMLHeaders(mustpass, mustpassElem)
161e5c31af7Sopenharmony_ci
162e5c31af7Sopenharmony_ci	for package in mustpass.packages:
163e5c31af7Sopenharmony_ci		packageElem = ElementTree.SubElement(mustpassElem, "TestPackage", name = package.module.name)
164e5c31af7Sopenharmony_ci
165e5c31af7Sopenharmony_ci		for config in package.configurations:
166e5c31af7Sopenharmony_ci			configElem = ElementTree.SubElement(packageElem, "Configuration",
167e5c31af7Sopenharmony_ci												caseListFile	= getCaseListFileName(package, config),
168e5c31af7Sopenharmony_ci												commandLine		= getCommandLine(config),
169e5c31af7Sopenharmony_ci												name			= config.name)
170e5c31af7Sopenharmony_ci
171e5c31af7Sopenharmony_ci	return mustpassElem
172e5c31af7Sopenharmony_ci
173e5c31af7Sopenharmony_cidef addOptionElement (parent, optionName, optionValue):
174e5c31af7Sopenharmony_ci	ElementTree.SubElement(parent, "option", name=optionName, value=optionValue)
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_cidef genAndroidTestXml (mustpass):
177e5c31af7Sopenharmony_ci	RUNNER_CLASS = "com.drawelements.deqp.runner.DeqpTestRunner"
178e5c31af7Sopenharmony_ci	configElement = ElementTree.Element("configuration")
179e5c31af7Sopenharmony_ci
180e5c31af7Sopenharmony_ci	# have the deqp package installed on the device for us
181e5c31af7Sopenharmony_ci	preparerElement = ElementTree.SubElement(configElement, "target_preparer")
182e5c31af7Sopenharmony_ci	preparerElement.set("class", "com.android.tradefed.targetprep.suite.SuiteApkInstaller")
183e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "cleanup-apks", "true")
184e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "test-file-name", "com.drawelements.deqp.apk")
185e5c31af7Sopenharmony_ci
186e5c31af7Sopenharmony_ci	# Target preparer for incremental dEQP
187e5c31af7Sopenharmony_ci	preparerElement = ElementTree.SubElement(configElement, "target_preparer")
188e5c31af7Sopenharmony_ci	preparerElement.set("class", "com.android.compatibility.common.tradefed.targetprep.FilePusher")
189e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "cleanup", "true")
190e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "disable", "true")
191e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "deqp-binary32->/data/local/tmp/deqp-binary32")
192e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "deqp-binary64->/data/local/tmp/deqp-binary64")
193e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "gles2->/data/local/tmp/gles2")
194e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "gles3->/data/local/tmp/gles3")
195e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "gles3-incremental-deqp.txt->/data/local/tmp/gles3-incremental-deqp.txt")
196e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "gles31->/data/local/tmp/gles31")
197e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "internal->/data/local/tmp/internal")
198e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "vk-incremental-deqp.txt->/data/local/tmp/vk-incremental-deqp.txt")
199e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "push", "vulkan->/data/local/tmp/vulkan")
200e5c31af7Sopenharmony_ci	preparerElement = ElementTree.SubElement(configElement, "target_preparer")
201e5c31af7Sopenharmony_ci	preparerElement.set("class", "com.android.compatibility.common.tradefed.targetprep.IncrementalDeqpPreparer")
202e5c31af7Sopenharmony_ci	addOptionElement(preparerElement, "disable", "true")
203e5c31af7Sopenharmony_ci
204e5c31af7Sopenharmony_ci	# add in metadata option for component name
205e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", name="test-suite-tag", value="cts")
206e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", key="component", name="config-descriptor:metadata", value="deqp")
207e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", key="parameter", name="config-descriptor:metadata", value="not_instant_app")
208e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", key="parameter", name="config-descriptor:metadata", value="multi_abi")
209e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", key="parameter", name="config-descriptor:metadata", value="secondary_user")
210e5c31af7Sopenharmony_ci	ElementTree.SubElement(configElement, "option", key="parameter", name="config-descriptor:metadata", value="no_foldable_states")
211e5c31af7Sopenharmony_ci	controllerElement = ElementTree.SubElement(configElement, "object")
212e5c31af7Sopenharmony_ci	controllerElement.set("class", "com.android.tradefed.testtype.suite.module.TestFailureModuleController")
213e5c31af7Sopenharmony_ci	controllerElement.set("type", "module_controller")
214e5c31af7Sopenharmony_ci	addOptionElement(controllerElement, "screenshot-on-failure", "false")
215e5c31af7Sopenharmony_ci
216e5c31af7Sopenharmony_ci	for package in mustpass.packages:
217e5c31af7Sopenharmony_ci		for config in package.configurations:
218e5c31af7Sopenharmony_ci			if not config.runByDefault:
219e5c31af7Sopenharmony_ci				continue
220e5c31af7Sopenharmony_ci
221e5c31af7Sopenharmony_ci			testElement = ElementTree.SubElement(configElement, "test")
222e5c31af7Sopenharmony_ci			testElement.set("class", RUNNER_CLASS)
223e5c31af7Sopenharmony_ci			addOptionElement(testElement, "deqp-package", package.module.name)
224e5c31af7Sopenharmony_ci			caseListFile = getCaseListFileName(package,config)
225e5c31af7Sopenharmony_ci			addOptionElement(testElement, "deqp-caselist-file", caseListFile)
226e5c31af7Sopenharmony_ci			if caseListFile.startswith("gles3"):
227e5c31af7Sopenharmony_ci				addOptionElement(testElement, "incremental-deqp-include-file", "gles3-incremental-deqp.txt")
228e5c31af7Sopenharmony_ci			elif caseListFile.startswith("vk"):
229e5c31af7Sopenharmony_ci				addOptionElement(testElement, "incremental-deqp-include-file", "vk-incremental-deqp.txt")
230e5c31af7Sopenharmony_ci			# \todo [2015-10-16 kalle]: Replace with just command line? - requires simplifications in the runner/tests as well.
231e5c31af7Sopenharmony_ci			if config.glconfig != None:
232e5c31af7Sopenharmony_ci				addOptionElement(testElement, "deqp-gl-config-name", config.glconfig)
233e5c31af7Sopenharmony_ci
234e5c31af7Sopenharmony_ci			if config.surfacetype != None:
235e5c31af7Sopenharmony_ci				addOptionElement(testElement, "deqp-surface-type", config.surfacetype)
236e5c31af7Sopenharmony_ci
237e5c31af7Sopenharmony_ci			if config.rotation != None:
238e5c31af7Sopenharmony_ci				addOptionElement(testElement, "deqp-screen-rotation", config.rotation)
239e5c31af7Sopenharmony_ci
240e5c31af7Sopenharmony_ci			if config.expectedRuntime != None:
241e5c31af7Sopenharmony_ci				addOptionElement(testElement, "runtime-hint", config.expectedRuntime)
242e5c31af7Sopenharmony_ci
243e5c31af7Sopenharmony_ci			if config.required:
244e5c31af7Sopenharmony_ci				addOptionElement(testElement, "deqp-config-required", "true")
245e5c31af7Sopenharmony_ci
246e5c31af7Sopenharmony_ci	insertXMLHeaders(mustpass, configElement)
247e5c31af7Sopenharmony_ci
248e5c31af7Sopenharmony_ci	return configElement
249e5c31af7Sopenharmony_ci
250e5c31af7Sopenharmony_ciclass PatternSet:
251e5c31af7Sopenharmony_ci	def __init__(self):
252e5c31af7Sopenharmony_ci		self.namedPatternsTree = {}
253e5c31af7Sopenharmony_ci		self.namedPatternsDict = {}
254e5c31af7Sopenharmony_ci		self.wildcardPatternsDict = {}
255e5c31af7Sopenharmony_ci
256e5c31af7Sopenharmony_cidef readPatternSets (mustpass):
257e5c31af7Sopenharmony_ci	patternSets = {}
258e5c31af7Sopenharmony_ci	for package in mustpass.packages:
259e5c31af7Sopenharmony_ci		for cfg in package.configurations:
260e5c31af7Sopenharmony_ci			for filter in cfg.filters:
261e5c31af7Sopenharmony_ci				if not filter.key in patternSets:
262e5c31af7Sopenharmony_ci					patternList = []
263e5c31af7Sopenharmony_ci					for filename in filter.filenames:
264e5c31af7Sopenharmony_ci						readPatternList(os.path.join(getSrcDir(mustpass), filename), patternList)
265e5c31af7Sopenharmony_ci					patternSet = PatternSet()
266e5c31af7Sopenharmony_ci					for pattern in patternList:
267e5c31af7Sopenharmony_ci						if pattern.find('*') == -1:
268e5c31af7Sopenharmony_ci							patternSet.namedPatternsDict[pattern] = 0
269e5c31af7Sopenharmony_ci							t = patternSet.namedPatternsTree
270e5c31af7Sopenharmony_ci							parts = pattern.split('.')
271e5c31af7Sopenharmony_ci							for part in parts:
272e5c31af7Sopenharmony_ci								t = t.setdefault(part, {})
273e5c31af7Sopenharmony_ci						else:
274e5c31af7Sopenharmony_ci							# We use regex instead of fnmatch because it's faster
275e5c31af7Sopenharmony_ci							patternSet.wildcardPatternsDict[re.compile("^" + pattern.replace(".", r"\.").replace("*", ".*?") + "$")] = 0
276e5c31af7Sopenharmony_ci					patternSets[filter.key] = patternSet
277e5c31af7Sopenharmony_ci	return patternSets
278e5c31af7Sopenharmony_ci
279e5c31af7Sopenharmony_cidef genMustpassFromLists (mustpass, moduleCaseLists):
280e5c31af7Sopenharmony_ci	print("Generating mustpass '%s'" % mustpass.version)
281e5c31af7Sopenharmony_ci	patternSets = readPatternSets(mustpass)
282e5c31af7Sopenharmony_ci
283e5c31af7Sopenharmony_ci	for package in mustpass.packages:
284e5c31af7Sopenharmony_ci		currentCaseList = moduleCaseLists[package.module]
285e5c31af7Sopenharmony_ci		logging.debug("Reading " + currentCaseList.filePath)
286e5c31af7Sopenharmony_ci
287e5c31af7Sopenharmony_ci		for config in package.configurations:
288e5c31af7Sopenharmony_ci			# construct components of path to main destination file
289e5c31af7Sopenharmony_ci			mainDstFileDir = getDstCaseListPath(mustpass)
290e5c31af7Sopenharmony_ci			mainDstFileName = getCaseListFileName(package, config)
291e5c31af7Sopenharmony_ci			mainDstFilePath = os.path.join(mainDstFileDir, mainDstFileName)
292e5c31af7Sopenharmony_ci			mainGroupSubDir = mainDstFileName[:-4]
293e5c31af7Sopenharmony_ci
294e5c31af7Sopenharmony_ci			if not os.path.exists(mainDstFileDir):
295e5c31af7Sopenharmony_ci				os.makedirs(mainDstFileDir)
296e5c31af7Sopenharmony_ci			mainDstFile = open(mainDstFilePath, 'w')
297e5c31af7Sopenharmony_ci			print(mainDstFilePath)
298e5c31af7Sopenharmony_ci			output_files = {}
299e5c31af7Sopenharmony_ci			def openAndStoreFile(filePath, testFilePath, parentFile):
300e5c31af7Sopenharmony_ci				if filePath not in output_files:
301e5c31af7Sopenharmony_ci					try:
302e5c31af7Sopenharmony_ci						print("    " + filePath)
303e5c31af7Sopenharmony_ci						parentFile.write(mainGroupSubDir + "/" + testFilePath + "\n")
304e5c31af7Sopenharmony_ci						currentDir = os.path.dirname(filePath)
305e5c31af7Sopenharmony_ci						if not os.path.exists(currentDir):
306e5c31af7Sopenharmony_ci							os.makedirs(currentDir)
307e5c31af7Sopenharmony_ci						output_files[filePath] = open(filePath, 'w')
308e5c31af7Sopenharmony_ci
309e5c31af7Sopenharmony_ci					except FileNotFoundError:
310e5c31af7Sopenharmony_ci						print(f"File not found: {filePath}")
311e5c31af7Sopenharmony_ci				return output_files[filePath]
312e5c31af7Sopenharmony_ci
313e5c31af7Sopenharmony_ci			lastOutputFile = ""
314e5c31af7Sopenharmony_ci			currentOutputFile = None
315e5c31af7Sopenharmony_ci			for line in currentCaseList.sortedLines:
316e5c31af7Sopenharmony_ci				if not line.startswith("TEST: "):
317e5c31af7Sopenharmony_ci					continue
318e5c31af7Sopenharmony_ci				caseName = line.replace("TEST: ", "").strip("\n")
319e5c31af7Sopenharmony_ci				caseParts = caseName.split(".")
320e5c31af7Sopenharmony_ci				keep = True
321e5c31af7Sopenharmony_ci				# Do the includes with the complex patterns first
322e5c31af7Sopenharmony_ci				for filter in config.filters:
323e5c31af7Sopenharmony_ci					if filter.type == Filter.TYPE_INCLUDE:
324e5c31af7Sopenharmony_ci						keep = False
325e5c31af7Sopenharmony_ci						patterns = patternSets[filter.key].wildcardPatternsDict
326e5c31af7Sopenharmony_ci						for pattern in patterns.keys():
327e5c31af7Sopenharmony_ci							keep = pattern.match(caseName)
328e5c31af7Sopenharmony_ci							if keep:
329e5c31af7Sopenharmony_ci								patterns[pattern] += 1
330e5c31af7Sopenharmony_ci								break
331e5c31af7Sopenharmony_ci
332e5c31af7Sopenharmony_ci						if not keep:
333e5c31af7Sopenharmony_ci							t = patternSets[filter.key].namedPatternsTree
334e5c31af7Sopenharmony_ci							if len(t.keys()) == 0:
335e5c31af7Sopenharmony_ci								continue
336e5c31af7Sopenharmony_ci							for part in caseParts:
337e5c31af7Sopenharmony_ci								if part in t:
338e5c31af7Sopenharmony_ci									t = t[part]
339e5c31af7Sopenharmony_ci								else:
340e5c31af7Sopenharmony_ci									t = None  # Not found
341e5c31af7Sopenharmony_ci									break
342e5c31af7Sopenharmony_ci							keep = t == {}
343e5c31af7Sopenharmony_ci							if keep:
344e5c31af7Sopenharmony_ci								patternSets[filter.key].namedPatternsDict[caseName] += 1
345e5c31af7Sopenharmony_ci
346e5c31af7Sopenharmony_ci					# Do the excludes
347e5c31af7Sopenharmony_ci					if filter.type == Filter.TYPE_EXCLUDE:
348e5c31af7Sopenharmony_ci						patterns = patternSets[filter.key].wildcardPatternsDict
349e5c31af7Sopenharmony_ci						for pattern in patterns.keys():
350e5c31af7Sopenharmony_ci							discard = pattern.match(caseName)
351e5c31af7Sopenharmony_ci							if discard:
352e5c31af7Sopenharmony_ci								patterns[pattern] += 1
353e5c31af7Sopenharmony_ci								keep = False
354e5c31af7Sopenharmony_ci								break
355e5c31af7Sopenharmony_ci						if keep:
356e5c31af7Sopenharmony_ci							t = patternSets[filter.key].namedPatternsTree
357e5c31af7Sopenharmony_ci							if len(t.keys()) == 0:
358e5c31af7Sopenharmony_ci								continue
359e5c31af7Sopenharmony_ci							for part in caseParts:
360e5c31af7Sopenharmony_ci								if part in t:
361e5c31af7Sopenharmony_ci									t = t[part]
362e5c31af7Sopenharmony_ci								else:
363e5c31af7Sopenharmony_ci									t = None  # Not found
364e5c31af7Sopenharmony_ci									break
365e5c31af7Sopenharmony_ci							if t == {}:
366e5c31af7Sopenharmony_ci								patternSets[filter.key].namedPatternsDict[caseName] += 1
367e5c31af7Sopenharmony_ci								keep = False
368e5c31af7Sopenharmony_ci					if not keep:
369e5c31af7Sopenharmony_ci						break
370e5c31af7Sopenharmony_ci				if not keep:
371e5c31af7Sopenharmony_ci					continue
372e5c31af7Sopenharmony_ci
373e5c31af7Sopenharmony_ci				parts = caseName.split('.')
374e5c31af7Sopenharmony_ci				if len(config.listOfGroupsToSplit) > 0:
375e5c31af7Sopenharmony_ci					if len(parts) > 2:
376e5c31af7Sopenharmony_ci						groupName = parts[1].replace("_", "-")
377e5c31af7Sopenharmony_ci						for splitPattern in config.listOfGroupsToSplit:
378e5c31af7Sopenharmony_ci							splitParts = splitPattern.split(".")
379e5c31af7Sopenharmony_ci							if len(splitParts) > 1 and caseName.startswith(splitPattern + "."):
380e5c31af7Sopenharmony_ci								groupName = groupName + "/" + parts[2].replace("_", "-")
381e5c31af7Sopenharmony_ci						filePath = os.path.join(mainDstFileDir, mainGroupSubDir, groupName + ".txt")
382e5c31af7Sopenharmony_ci						if lastOutputFile != filePath:
383e5c31af7Sopenharmony_ci							currentOutputFile = openAndStoreFile(filePath, groupName + ".txt", mainDstFile)
384e5c31af7Sopenharmony_ci							lastOutputFile = filePath
385e5c31af7Sopenharmony_ci						currentOutputFile.write(caseName + "\n")
386e5c31af7Sopenharmony_ci				else:
387e5c31af7Sopenharmony_ci					mainDstFile.write(caseName + "\n")
388e5c31af7Sopenharmony_ci
389e5c31af7Sopenharmony_ci			# Check that all patterns have been used in the filters
390e5c31af7Sopenharmony_ci			# This check will help identifying typos and patterns becoming stale
391e5c31af7Sopenharmony_ci			for filter in config.filters:
392e5c31af7Sopenharmony_ci				if filter.type == Filter.TYPE_INCLUDE:
393e5c31af7Sopenharmony_ci					patternSet = patternSets[filter.key]
394e5c31af7Sopenharmony_ci					for pattern, usage in patternSet.namedPatternsDict.items():
395e5c31af7Sopenharmony_ci						if usage == 0:
396e5c31af7Sopenharmony_ci							logging.warning("Case %s in file %s for module %s was never used!" % (pattern, filter.key, config.name))
397e5c31af7Sopenharmony_ci					for pattern, usage in patternSet.wildcardPatternsDict.items():
398e5c31af7Sopenharmony_ci						if usage == 0:
399e5c31af7Sopenharmony_ci							logging.warning("Pattern %s in file %s for module %s was never used!" % (pattern, filter.key, config.name))
400e5c31af7Sopenharmony_ci
401e5c31af7Sopenharmony_ci	# Generate XML
402e5c31af7Sopenharmony_ci	specXML = genSpecXML(mustpass)
403e5c31af7Sopenharmony_ci	specFilename = os.path.join(mustpass.project.path, mustpass.version, "mustpass.xml")
404e5c31af7Sopenharmony_ci
405e5c31af7Sopenharmony_ci	print("  Writing spec: " + specFilename)
406e5c31af7Sopenharmony_ci	writeFile(specFilename, prettifyXML(specXML).decode())
407e5c31af7Sopenharmony_ci
408e5c31af7Sopenharmony_ci	# TODO: Which is the best selector mechanism?
409e5c31af7Sopenharmony_ci	if (mustpass.version == "master"):
410e5c31af7Sopenharmony_ci		androidTestXML		= genAndroidTestXml(mustpass)
411e5c31af7Sopenharmony_ci		androidTestFilename	= os.path.join(mustpass.project.path, "AndroidTest.xml")
412e5c31af7Sopenharmony_ci
413e5c31af7Sopenharmony_ci		print("  Writing AndroidTest.xml: " + androidTestFilename)
414e5c31af7Sopenharmony_ci		writeFile(androidTestFilename, prettifyXML(androidTestXML).decode())
415e5c31af7Sopenharmony_ci
416e5c31af7Sopenharmony_ci	print("Done!")
417e5c31af7Sopenharmony_ci
418e5c31af7Sopenharmony_ci
419e5c31af7Sopenharmony_cidef genMustpassLists (mustpassLists, generator, buildCfg):
420e5c31af7Sopenharmony_ci	moduleCaseLists = {}
421e5c31af7Sopenharmony_ci
422e5c31af7Sopenharmony_ci	# Getting case lists involves invoking build, so we want to cache the results
423e5c31af7Sopenharmony_ci	for mustpass in mustpassLists:
424e5c31af7Sopenharmony_ci		for package in mustpass.packages:
425e5c31af7Sopenharmony_ci			if not package.module in moduleCaseLists:
426e5c31af7Sopenharmony_ci				moduleCaseLists[package.module] = readAndSortCaseList(buildCfg, generator, package.module)
427e5c31af7Sopenharmony_ci
428e5c31af7Sopenharmony_ci	for mustpass in mustpassLists:
429e5c31af7Sopenharmony_ci		genMustpassFromLists(mustpass, moduleCaseLists)
430e5c31af7Sopenharmony_ci
431e5c31af7Sopenharmony_cidef parseCmdLineArgs ():
432e5c31af7Sopenharmony_ci	parser = argparse.ArgumentParser(description = "Build Android CTS mustpass",
433e5c31af7Sopenharmony_ci									 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
434e5c31af7Sopenharmony_ci	parser.add_argument("-b",
435e5c31af7Sopenharmony_ci						"--build-dir",
436e5c31af7Sopenharmony_ci						dest="buildDir",
437e5c31af7Sopenharmony_ci						default=DEFAULT_BUILD_DIR,
438e5c31af7Sopenharmony_ci						help="Temporary build directory")
439e5c31af7Sopenharmony_ci	parser.add_argument("-t",
440e5c31af7Sopenharmony_ci						"--build-type",
441e5c31af7Sopenharmony_ci						dest="buildType",
442e5c31af7Sopenharmony_ci						default="Debug",
443e5c31af7Sopenharmony_ci						help="Build type")
444e5c31af7Sopenharmony_ci	parser.add_argument("-c",
445e5c31af7Sopenharmony_ci						"--deqp-target",
446e5c31af7Sopenharmony_ci						dest="targetName",
447e5c31af7Sopenharmony_ci						default=DEFAULT_TARGET,
448e5c31af7Sopenharmony_ci						help="dEQP build target")
449e5c31af7Sopenharmony_ci	return parser.parse_args()
450e5c31af7Sopenharmony_ci
451e5c31af7Sopenharmony_cidef parseBuildConfigFromCmdLineArgs ():
452e5c31af7Sopenharmony_ci	args = parseCmdLineArgs()
453e5c31af7Sopenharmony_ci	return getBuildConfig(args.buildDir, args.targetName, args.buildType)
454