• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Command line parsing.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuCommandLine.hpp"
25 #include "tcuPlatform.hpp"
26 #include "tcuTestCase.hpp"
27 #include "tcuResource.hpp"
28 #include "deFilePath.hpp"
29 #include "deStringUtil.hpp"
30 #include "deString.h"
31 #include "deInt32.h"
32 #include "deCommandLine.h"
33 #include "qpTestLog.h"
34 #include "qpDebugOut.h"
35 
36 #include <string>
37 #include <vector>
38 #include <sstream>
39 #include <fstream>
40 #include <iostream>
41 #include <algorithm>
42 
43 using std::string;
44 using std::vector;
45 
46 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
47 #if (DE_OS == DE_OS_WIN32)
48 #	define TEST_OOM_DEFAULT		"enable"
49 #else
50 #	define TEST_OOM_DEFAULT		"disable"
51 #endif
52 
53 namespace tcu
54 {
55 
56 namespace opt
57 {
58 
59 DE_DECLARE_COMMAND_LINE_OPT(CasePath,					std::string);
60 DE_DECLARE_COMMAND_LINE_OPT(CaseList,					std::string);
61 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile,				std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource,			std::string);
63 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList,				bool);
64 DE_DECLARE_COMMAND_LINE_OPT(LogFilename,				std::string);
65 DE_DECLARE_COMMAND_LINE_OPT(RunMode,					tcu::RunMode);
66 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern,		std::string);
67 DE_DECLARE_COMMAND_LINE_OPT(WatchDog,					bool);
68 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler,				bool);
69 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed,					int);
70 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount,			int);
71 DE_DECLARE_COMMAND_LINE_OPT(Visibility,					WindowVisibility);
72 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth,				int);
73 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight,				int);
74 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType,				tcu::SurfaceType);
75 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation,				tcu::ScreenRotation);
76 DE_DECLARE_COMMAND_LINE_OPT(GLContextType,				std::string);
77 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID,					int);
78 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName,				std::string);
79 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags,				std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID,				int);
81 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs,				std::vector<int>);
82 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions,				std::string);
83 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType,				std::string);
84 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType,				std::string);
85 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType,				std::string);
86 DE_DECLARE_COMMAND_LINE_OPT(LogImages,					bool);
87 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources,			bool);
88 DE_DECLARE_COMMAND_LINE_OPT(LogDecompiledSpirv,			bool);
89 DE_DECLARE_COMMAND_LINE_OPT(LogEmptyLoginfo,			bool);
90 DE_DECLARE_COMMAND_LINE_OPT(TestOOM,					bool);
91 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir,					std::string);
92 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID,					int);
93 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID,			int);
94 DE_DECLARE_COMMAND_LINE_OPT(LogFlush,					bool);
95 DE_DECLARE_COMMAND_LINE_OPT(Validation,					bool);
96 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors,		bool);
97 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,				bool);
98 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,		std::string);
99 DE_DECLARE_COMMAND_LINE_OPT(Optimization,				int);
100 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,				bool);
101 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,		bool);
102 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheIPC,				bool);
103 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,					bool);
104 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction,				std::vector<int>);
105 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests,	std::string);
106 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile,					std::string);
107 DE_DECLARE_COMMAND_LINE_OPT(RunnerType,					tcu::TestRunnerType);
108 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail,			bool);
109 DE_DECLARE_COMMAND_LINE_OPT(SubProcess,					bool);
110 DE_DECLARE_COMMAND_LINE_OPT(SubprocessTestCount,		int);
111 DE_DECLARE_COMMAND_LINE_OPT(SubprocessConfigFile,		std::string);
112 DE_DECLARE_COMMAND_LINE_OPT(ServerAddress,				std::string);
113 DE_DECLARE_COMMAND_LINE_OPT(CommandPoolMinSize,			int);
114 DE_DECLARE_COMMAND_LINE_OPT(CommandBufferMinSize,		int);
115 DE_DECLARE_COMMAND_LINE_OPT(CommandDefaultSize,			int);
116 DE_DECLARE_COMMAND_LINE_OPT(PipelineDefaultSize,		int);
117 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath,		std::string);
118 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir,	std::string);
119 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs,		std::string);
120 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile,	std::string);
121 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile,	std::string);
122 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerFilePrefix,	std::string);
123 DE_DECLARE_COMMAND_LINE_OPT(VkLibraryPath,				std::string);
124 DE_DECLARE_COMMAND_LINE_OPT(ApplicationParametersInputFile,	std::string);
125 
126 
parseIntList(const char * src,std::vector<int> * dst)127 static void parseIntList (const char* src, std::vector<int>* dst)
128 {
129 	std::istringstream	str	(src);
130 	std::string			val;
131 
132 	while (std::getline(str, val, ','))
133 	{
134 		int intVal = 0;
135 		de::cmdline::parseType(val.c_str(), &intVal);
136 		dst->push_back(intVal);
137 	}
138 }
139 
registerOptions(de::cmdline::Parser & parser)140 void registerOptions (de::cmdline::Parser& parser)
141 {
142 	using de::cmdline::Option;
143 	using de::cmdline::NamedValue;
144 
145 	static const NamedValue<bool> s_enableNames[] =
146 	{
147 		{ "enable",		true	},
148 		{ "disable",	false	}
149 	};
150 	static const NamedValue<tcu::RunMode> s_runModes[] =
151 	{
152 		{ "execute",		RUNMODE_EXECUTE				  },
153 		{ "xml-caselist",	RUNMODE_DUMP_XML_CASELIST	  },
154 		{ "txt-caselist",	RUNMODE_DUMP_TEXT_CASELIST	  },
155 		{ "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST  },
156 		{ "amber-verify",   RUNMODE_VERIFY_AMBER_COHERENCY}
157 	};
158 	static const NamedValue<WindowVisibility> s_visibilites[] =
159 	{
160 		{ "windowed",		WINDOWVISIBILITY_WINDOWED	},
161 		{ "fullscreen",		WINDOWVISIBILITY_FULLSCREEN	},
162 		{ "hidden",			WINDOWVISIBILITY_HIDDEN		}
163 	};
164 	static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
165 	{
166 		{ "window",			SURFACETYPE_WINDOW				},
167 		{ "pixmap",			SURFACETYPE_OFFSCREEN_NATIVE	},
168 		{ "pbuffer",		SURFACETYPE_OFFSCREEN_GENERIC	},
169 		{ "fbo",			SURFACETYPE_FBO					}
170 	};
171 	static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
172 	{
173 		{ "unspecified",	SCREENROTATION_UNSPECIFIED	},
174 		{ "0",				SCREENROTATION_0			},
175 		{ "90",				SCREENROTATION_90			},
176 		{ "180",			SCREENROTATION_180			},
177 		{ "270",			SCREENROTATION_270			}
178 	};
179 	static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
180 	{
181 		{ "any",	tcu::RUNNERTYPE_ANY		},
182 		{ "none",	tcu::RUNNERTYPE_NONE	},
183 		{ "amber",	tcu::RUNNERTYPE_AMBER	},
184 	};
185 
186 	parser
187 		<< Option<CasePath>						("n",		"deqp-case",								"Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
188 		<< Option<CaseList>						(DE_NULL,	"deqp-caselist",							"Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
189 		<< Option<CaseListFile>					(DE_NULL,	"deqp-caselist-file",						"Read case list (in trie format) from given file")
190 		<< Option<CaseListResource>				(DE_NULL,	"deqp-caselist-resource",					"Read case list (in trie format) from given file located application's assets")
191 		<< Option<StdinCaseList>				(DE_NULL,	"deqp-stdin-caselist",						"Read case list (in trie format) from stdin")
192 		<< Option<LogFilename>					(DE_NULL,	"deqp-log-filename",						"Write test results to given file",					"TestResults.qpa")
193 		<< Option<RunMode>						(DE_NULL,	"deqp-runmode",								"Execute tests, write list of test cases into a file, or verify amber capability coherency",
194 																																							s_runModes,			"execute")
195 		<< Option<ExportFilenamePattern>		(DE_NULL,	"deqp-caselist-export-file",				"Set the target file name pattern for caselist export",					"${packageName}-cases.${typeExtension}")
196 		<< Option<WatchDog>						(DE_NULL,	"deqp-watchdog",							"Enable test watchdog",								s_enableNames,		"disable")
197 		<< Option<CrashHandler>					(DE_NULL,	"deqp-crashhandler",						"Enable crash handling",							s_enableNames,		"disable")
198 		<< Option<BaseSeed>						(DE_NULL,	"deqp-base-seed",							"Base seed for test cases that use randomization",						"0")
199 		<< Option<TestIterationCount>			(DE_NULL,	"deqp-test-iteration-count",				"Iteration count for cases that support variable number of iterations",	"0")
200 		<< Option<Visibility>					(DE_NULL,	"deqp-visibility",							"Default test window visibility",					s_visibilites,		"windowed")
201 		<< Option<SurfaceWidth>					(DE_NULL,	"deqp-surface-width",						"Use given surface width if possible",									"-1")
202 		<< Option<SurfaceHeight>				(DE_NULL,	"deqp-surface-height",						"Use given surface height if possible",									"-1")
203 		<< Option<SurfaceType>					(DE_NULL,	"deqp-surface-type",						"Use given surface type",							s_surfaceTypes,		"window")
204 		<< Option<ScreenRotation>				(DE_NULL,	"deqp-screen-rotation",						"Screen rotation for platforms that support it",	s_screenRotations,	"0")
205 		<< Option<GLContextType>				(DE_NULL,	"deqp-gl-context-type",						"OpenGL context type for platforms that support multiple")
206 		<< Option<GLConfigID>					(DE_NULL,	"deqp-gl-config-id",						"OpenGL (ES) render config ID (EGL config id on EGL platforms)",		"-1")
207 		<< Option<GLConfigName>					(DE_NULL,	"deqp-gl-config-name",						"Symbolic OpenGL (ES) render config name")
208 		<< Option<GLContextFlags>				(DE_NULL,	"deqp-gl-context-flags",					"OpenGL context flags (comma-separated, supports debug and robust)")
209 		<< Option<CLPlatformID>					(DE_NULL,	"deqp-cl-platform-id",						"Execute tests on given OpenCL platform (IDs start from 1)",			"1")
210 		<< Option<CLDeviceIDs>					(DE_NULL,	"deqp-cl-device-ids",						"Execute tests on given CL devices (comma-separated, IDs start from 1)",	parseIntList,	"")
211 		<< Option<CLBuildOptions>				(DE_NULL,	"deqp-cl-build-options",					"Extra build options for OpenCL compiler")
212 		<< Option<EGLDisplayType>				(DE_NULL,	"deqp-egl-display-type",					"EGL native display type")
213 		<< Option<EGLWindowType>				(DE_NULL,	"deqp-egl-window-type",						"EGL native window type")
214 		<< Option<EGLPixmapType>				(DE_NULL,	"deqp-egl-pixmap-type",						"EGL native pixmap type")
215 		<< Option<VKDeviceID>					(DE_NULL,	"deqp-vk-device-id",						"Vulkan device ID (IDs start from 1)",									"1")
216 		<< Option<VKDeviceGroupID>				(DE_NULL,	"deqp-vk-device-group-id",					"Vulkan device Group ID (IDs start from 1)",							"1")
217 		<< Option<LogImages>					(DE_NULL,	"deqp-log-images",							"Enable or disable logging of result images",		s_enableNames,		"enable")
218 		<< Option<LogShaderSources>				(DE_NULL,	"deqp-log-shader-sources",					"Enable or disable logging of shader sources",		s_enableNames,		"enable")
219 		<< Option<LogDecompiledSpirv>			(DE_NULL,	"deqp-log-decompiled-spirv",				"Enable or disable logging of decompiled spir-v",	s_enableNames,		"enable")
220 		<< Option<LogEmptyLoginfo>				(DE_NULL,	"deqp-log-empty-loginfo",					"Logging of empty shader compile/link log info",	s_enableNames,		"enable")
221 		<< Option<TestOOM>						(DE_NULL,	"deqp-test-oom",							"Run tests that exhaust memory on purpose",			s_enableNames,		TEST_OOM_DEFAULT)
222 		<< Option<ArchiveDir>					(DE_NULL,	"deqp-archive-dir",							"Path to test resource files",											".")
223 		<< Option<LogFlush>						(DE_NULL,	"deqp-log-flush",							"Enable or disable log file fflush",				s_enableNames,		"enable")
224 		<< Option<Validation>					(DE_NULL,	"deqp-validation",							"Enable or disable test case validation",			s_enableNames,		"disable")
225 		<< Option<PrintValidationErrors>		(DE_NULL,	"deqp-print-validation-errors",				"Print validation errors to standard error")
226 		<< Option<Optimization>					(DE_NULL,	"deqp-optimization-recipe",					"Shader optimization recipe (0=disabled, 1=performance, 2=size)",		"0")
227 		<< Option<OptimizeSpirv>				(DE_NULL,	"deqp-optimize-spirv",						"Apply optimization to spir-v shaders as well",		s_enableNames,		"disable")
228 		<< Option<ShaderCache>					(DE_NULL,	"deqp-shadercache",							"Enable or disable shader cache",					s_enableNames,		"enable")
229 		<< Option<ShaderCacheFilename>			(DE_NULL,	"deqp-shadercache-filename",				"Write shader cache to given file",										"shadercache.bin")
230 		<< Option<ShaderCacheTruncate>			(DE_NULL,	"deqp-shadercache-truncate",				"Truncate shader cache before running tests",		s_enableNames,		"enable")
231 		<< Option<ShaderCacheIPC>				(DE_NULL,	"deqp-shadercache-ipc",						"Should shader cache use inter process comms",		s_enableNames,		"disable")
232 		<< Option<RenderDoc>					(DE_NULL,	"deqp-renderdoc",							"Enable RenderDoc frame markers",					s_enableNames,		"disable")
233 		<< Option<CaseFraction>					(DE_NULL,	"deqp-fraction",							"Run a fraction of the test cases (e.g. N,M means run group%M==N)",	parseIntList,	"")
234 		<< Option<CaseFractionMandatoryTests>	(DE_NULL,	"deqp-fraction-mandatory-caselist-file",	"Case list file that must be run for each fraction",					"")
235 		<< Option<WaiverFile>					(DE_NULL,	"deqp-waiver-file",							"Read waived tests from given file",									"")
236 		<< Option<RunnerType>					(DE_NULL,	"deqp-runner-type",							"Filter test cases based on runner",				s_runnerTypes,		"any")
237 		<< Option<TerminateOnFail>				(DE_NULL,	"deqp-terminate-on-fail",					"Terminate the run on first failure",				s_enableNames,		"disable")
238 		<< Option<SubProcess>					(DE_NULL,	"deqp-subprocess",							"Inform app that it works as subprocess (Vulkan SC only, do not use manually)", s_enableNames, "disable")
239 		<< Option<SubprocessTestCount>			(DE_NULL,	"deqp-subprocess-test-count",				"Define default number of tests performed in subprocess for specific test cases(Vulkan SC only)",	"65536")
240 		<< Option<SubprocessConfigFile>			(DE_NULL,	"deqp-subprocess-cfg-file",					"Config file defining number of tests performed in subprocess for specific test branches (Vulkan SC only)", "")
241 		<< Option<ServerAddress>				(DE_NULL,	"deqp-server-address",						"Server address (host:port) responsible for shader compilation (Vulkan SC only)", "")
242 		<< Option<CommandPoolMinSize>			(DE_NULL,	"deqp-command-pool-min-size",				"Define minimum size of the command pool (in bytes) to use (Vulkan SC only)","0")
243 		<< Option<CommandBufferMinSize>			(DE_NULL,	"deqp-command-buffer-min-size",				"Define minimum size of the command buffer (in bytes) to use (Vulkan SC only)", "0")
244 		<< Option<CommandDefaultSize>			(DE_NULL,	"deqp-command-default-size",				"Define default single command size (in bytes) to use (Vulkan SC only)",	"256")
245 		<< Option<PipelineDefaultSize>			(DE_NULL,	"deqp-pipeline-default-size",				"Define default pipeline size (in bytes) to use (Vulkan SC only)",		"16384")
246 		<< Option<PipelineCompilerPath>			(DE_NULL,	"deqp-pipeline-compiler",					"Path to offline pipeline compiler (Vulkan SC only)", "")
247 		<< Option<PipelineCompilerDataDir>		(DE_NULL,	"deqp-pipeline-dir",						"Offline pipeline data directory (Vulkan SC only)", "")
248 		<< Option<PipelineCompilerArgs>			(DE_NULL,	"deqp-pipeline-args",						"Additional compiler parameters (Vulkan SC only)", "")
249 		<< Option<PipelineCompilerOutputFile>	(DE_NULL,	"deqp-pipeline-file",						"Output file with pipeline cache (Vulkan SC only, do not use manually)", "")
250 		<< Option<PipelineCompilerLogFile>		(DE_NULL,	"deqp-pipeline-logfile",					"Log file for pipeline compiler (Vulkan SC only, do not use manually)", "")
251 		<< Option<PipelineCompilerFilePrefix>	(DE_NULL,	"deqp-pipeline-prefix",						"Prefix for input pipeline compiler files (Vulkan SC only, do not use manually)", "")
252 		<< Option<VkLibraryPath>				(DE_NULL,	"deqp-vk-library-path",						"Path to Vulkan library (e.g. loader library vulkan-1.dll)", "")
253 		<< Option<ApplicationParametersInputFile>    (DE_NULL,       "deqp-app-params-input-file",				"File that provides a default set of application parameters");
254 }
255 
registerLegacyOptions(de::cmdline::Parser & parser)256 void registerLegacyOptions (de::cmdline::Parser& parser)
257 {
258 	using de::cmdline::Option;
259 
260 	parser
261 		<< Option<GLConfigID>			(DE_NULL,	"deqp-egl-config-id",			"Legacy name for --deqp-gl-config-id",	"-1")
262 		<< Option<GLConfigName>			(DE_NULL,	"deqp-egl-config-name",			"Legacy name for --deqp-gl-config-name");
263 }
264 
265 } // opt
266 
267 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
268 class DebugOutStreambuf : public std::streambuf
269 {
270 public:
271 						DebugOutStreambuf	(void);
272 						~DebugOutStreambuf	(void);
273 
274 protected:
275 	std::streamsize		xsputn				(const char* s, std::streamsize count);
276 	int					overflow			(int ch = -1);
277 
278 private:
279 	void				flushLine			(void);
280 
281 	std::ostringstream	m_curLine;
282 };
283 
DebugOutStreambuf(void)284 DebugOutStreambuf::DebugOutStreambuf (void)
285 {
286 }
287 
~DebugOutStreambuf(void)288 DebugOutStreambuf::~DebugOutStreambuf (void)
289 {
290 	if (m_curLine.tellp() != std::streampos(0))
291 		flushLine();
292 }
293 
xsputn(const char * s,std::streamsize count)294 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
295 {
296 	for (std::streamsize pos = 0; pos < count; pos++)
297 	{
298 		m_curLine.put(s[pos]);
299 
300 		if (s[pos] == '\n')
301 			flushLine();
302 	}
303 
304 	return count;
305 }
306 
overflow(int ch)307 int DebugOutStreambuf::overflow (int ch)
308 {
309 	if (ch == -1)
310 		return -1;
311 	else
312 	{
313 		DE_ASSERT((ch & 0xff) == ch);
314 		const char chVal = (char)(deUint8)(ch & 0xff);
315 		return xsputn(&chVal, 1) == 1 ? ch : -1;
316 	}
317 }
318 
flushLine(void)319 void DebugOutStreambuf::flushLine (void)
320 {
321 	qpPrint(m_curLine.str().c_str());
322 	m_curLine.str("");
323 }
324 
325 class CaseTreeNode
326 {
327 public:
CaseTreeNode(const std::string & name)328 										CaseTreeNode		(const std::string& name) : m_name(name) {}
329 										~CaseTreeNode		(void);
330 
getName(void) const331 	const std::string&					getName				(void) const { return m_name;				}
hasChildren(void) const332 	bool								hasChildren			(void) const { return !m_children.empty();	}
333 
334 	bool								hasChild			(const std::string& name) const;
335 	const CaseTreeNode*					getChild			(const std::string& name) const;
336 	CaseTreeNode*						getChild			(const std::string& name);
337 
addChild(CaseTreeNode * child)338 	void								addChild			(CaseTreeNode* child) { m_children.push_back(child); }
339 
340 private:
341 										CaseTreeNode		(const CaseTreeNode&);
342 	CaseTreeNode&						operator=			(const CaseTreeNode&);
343 
344 	enum { NOT_FOUND = -1 };
345 
346 	// \todo [2014-10-30 pyry] Speed up with hash / sorting
347 	int									findChildNdx		(const std::string& name) const;
348 
349 	std::string							m_name;
350 	std::vector<CaseTreeNode*>			m_children;
351 };
352 
~CaseTreeNode(void)353 CaseTreeNode::~CaseTreeNode (void)
354 {
355 	for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
356 		delete *i;
357 }
358 
findChildNdx(const std::string & name) const359 int CaseTreeNode::findChildNdx (const std::string& name) const
360 {
361 	for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
362 	{
363 		if (m_children[ndx]->getName() == name)
364 			return ndx;
365 	}
366 	return NOT_FOUND;
367 }
368 
hasChild(const std::string & name) const369 inline bool CaseTreeNode::hasChild (const std::string& name) const
370 {
371 	return findChildNdx(name) != NOT_FOUND;
372 }
373 
getChild(const std::string & name) const374 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
375 {
376 	const int ndx = findChildNdx(name);
377 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
378 }
379 
getChild(const std::string & name)380 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
381 {
382 	const int ndx = findChildNdx(name);
383 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
384 }
385 
getCurrentComponentLen(const char * path)386 static int getCurrentComponentLen (const char* path)
387 {
388 	int ndx = 0;
389 	for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
390 	return ndx;
391 }
392 
findNode(const CaseTreeNode * root,const char * path)393 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
394 {
395 	const CaseTreeNode*	curNode		= root;
396 	const char*			curPath		= path;
397 	int					curLen		= getCurrentComponentLen(curPath);
398 
399 	for (;;)
400 	{
401 		curNode = curNode->getChild(std::string(curPath, curPath+curLen));
402 
403 		if (!curNode)
404 			break;
405 
406 		curPath	+= curLen;
407 
408 		if (curPath[0] == 0)
409 			break;
410 		else
411 		{
412 			DE_ASSERT(curPath[0] == '.');
413 			curPath		+= 1;
414 			curLen		 = getCurrentComponentLen(curPath);
415 		}
416 	}
417 
418 	return curNode;
419 }
420 
parseCaseTrie(CaseTreeNode * root,std::istream & in)421 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
422 {
423 	vector<CaseTreeNode*>	nodeStack;
424 	string					curName;
425 	bool					expectNode		= true;
426 
427 	if (in.get() != '{')
428 		throw std::invalid_argument("Malformed case trie");
429 
430 	nodeStack.push_back(root);
431 
432 	while (!nodeStack.empty())
433 	{
434 		const int	curChr	= in.get();
435 
436 		if (curChr == std::char_traits<char>::eof() || curChr == 0)
437 			throw std::invalid_argument("Unterminated case tree");
438 
439 		if (curChr == '{' || curChr == ',' || curChr == '}')
440 		{
441 			if (!curName.empty() && expectNode)
442 			{
443 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
444 
445 				try
446 				{
447 					nodeStack.back()->addChild(newChild);
448 				}
449 				catch (...)
450 				{
451 					delete newChild;
452 					throw;
453 				}
454 
455 				if (curChr == '{')
456 					nodeStack.push_back(newChild);
457 
458 				curName.clear();
459 			}
460 			else if (curName.empty() == expectNode)
461 				throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
462 
463 			if (curChr == '}')
464 			{
465 				expectNode = false;
466 				nodeStack.pop_back();
467 
468 				// consume trailing new line
469 				if (nodeStack.empty())
470 				{
471 					if (in.peek() == '\r')
472 					  in.get();
473 					if (in.peek() == '\n')
474 					  in.get();
475 				}
476 			}
477 			else
478 				expectNode = true;
479 		}
480 		else if (isValidTestCaseNameChar((char)curChr))
481 			curName += (char)curChr;
482 		else
483 			throw std::invalid_argument("Illegal character in node name");
484 	}
485 }
486 
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates)487 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates)
488 {
489 	// \note Algorithm assumes that cases are sorted by groups, but will
490 	//		 function fine, albeit more slowly, if that is not the case.
491 	int		stackPos	= 0;
492 	string	curName;
493 
494 	for (;;)
495 	{
496 		const int curChr = in.get();
497 
498 		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
499 		{
500 			if (curName.empty())
501 				throw std::invalid_argument("Empty test case name");
502 
503 			if (!nodeStack[stackPos]->hasChild(curName))
504 			{
505 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
506 
507 				try
508 				{
509 					nodeStack[stackPos]->addChild(newChild);
510 				}
511 				catch (...)
512 				{
513 					delete newChild;
514 					throw;
515 				}
516 			}
517 			else if (reportDuplicates)
518 				throw std::invalid_argument("Duplicate test case");
519 
520 			curName.clear();
521 			stackPos = 0;
522 
523 			if (curChr == '\r' && in.peek() == '\n')
524 				in.get();
525 
526 			{
527 				const int nextChr = in.peek();
528 
529 				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
530 					break;
531 			}
532 		}
533 		else if (curChr == '.')
534 		{
535 			if (curName.empty())
536 				throw std::invalid_argument("Empty test group name");
537 
538 			if ((int)nodeStack.size() <= stackPos+1)
539 				nodeStack.resize(nodeStack.size()*2, DE_NULL);
540 
541 			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
542 			{
543 				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
544 
545 				if (!curGroup)
546 				{
547 					curGroup = new CaseTreeNode(curName);
548 
549 					try
550 					{
551 						nodeStack[stackPos]->addChild(curGroup);
552 					}
553 					catch (...)
554 					{
555 						delete curGroup;
556 						throw;
557 					}
558 				}
559 
560 				nodeStack[stackPos+1] = curGroup;
561 
562 				if ((int)nodeStack.size() > stackPos+2)
563 					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
564 			}
565 
566 			DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
567 
568 			curName.clear();
569 			stackPos += 1;
570 		}
571 		else if (isValidTestCaseNameChar((char)curChr))
572 			curName += (char)curChr;
573 		else
574 			throw std::invalid_argument("Illegal character in test case name");
575 	}
576 }
577 
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates)578 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates)
579 {
580 	vector<CaseTreeNode*> nodeStack(8, root);
581 	parseSimpleCaseList(nodeStack, in, reportDuplicates);
582 }
583 
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates)584 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates)
585 {
586 	// read whole file and remove all '\r'
587 	std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
588 	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
589 
590 	vector<CaseTreeNode*>	nodeStack(8, root);
591 	std::stringstream		namesStream(buffer);
592 	std::string				fileName;
593 
594 	while (std::getline(namesStream, fileName))
595 	{
596 		de::FilePath			groupPath		(fileName);
597 		de::UniquePtr<Resource>	groupResource	(archive.getResource(groupPath.normalize().getPath()));
598 		const int				groupBufferSize	(groupResource->getSize());
599 		std::vector<char>		groupBuffer		(static_cast<size_t>(groupBufferSize));
600 
601 		groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
602 		if (groupBuffer.empty())
603 			throw Exception("Empty case list resource");
604 
605 		std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
606 		parseSimpleCaseList(nodeStack, groupIn, reportDuplicates);
607 	}
608 }
609 
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)610 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
611 {
612 	CaseTreeNode* const root = new CaseTreeNode("");
613 	try
614 	{
615 		if (in.peek() == '{')
616 			parseCaseTrie(root, in);
617 		else
618 		{
619 			// if we are reading cases from file determine if we are
620 			// reading group file or plain list of cases; this is done by
621 			// reading single line and checking if it ends with ".txt"
622 			bool readGroupFile = false;
623 			if (path)
624 			{
625 				// read the first line and make sure it doesn't contain '\r'
626 				std::string line;
627 				std::getline(in, line);
628 				line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
629 
630 				const std::string ending = ".txt";
631 				readGroupFile = (line.length() > ending.length()) &&
632 								std::equal(ending.rbegin(), ending.rend(), line.rbegin());
633 
634 				// move to the beginning of the file to parse first line too
635 				in.seekg(0, in.beg);
636 			}
637 
638 			if (readGroupFile)
639 				parseGroupFile(root, in, archive, true);
640 			else
641 				parseCaseList(root, in, true);
642 		}
643 
644 		{
645 			const int curChr = in.get();
646 			if (curChr != std::char_traits<char>::eof() && curChr != 0)
647 				throw std::invalid_argument("Trailing characters at end of case list");
648 		}
649 
650 		return root;
651 	}
652 	catch (...)
653 	{
654 		delete root;
655 		throw;
656 	}
657 }
658 
659 class CasePaths
660 {
661 public:
662 	CasePaths(const string& pathList);
663 	CasePaths(const vector<string>& pathList);
664 	bool					matches(const string& caseName, bool allowPrefix = false) const;
665 
666 private:
667 	const vector<string>	m_casePatterns;
668 };
669 
CasePaths(const string & pathList)670 CasePaths::CasePaths (const string& pathList)
671 	: m_casePatterns(de::splitString(pathList, ','))
672 {
673 }
674 
CasePaths(const vector<string> & pathList)675 CasePaths::CasePaths(const vector<string>& pathList)
676 	: m_casePatterns(pathList)
677 {
678 }
679 
680 // Match a single path component against a pattern component that may contain *-wildcards.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)681 bool matchWildcards(string::const_iterator	patternStart,
682 					string::const_iterator	patternEnd,
683 					string::const_iterator	pathStart,
684 					string::const_iterator	pathEnd,
685 					bool					allowPrefix)
686 {
687 	string::const_iterator	pattern	= patternStart;
688 	string::const_iterator	path	= pathStart;
689 
690 	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
691 	{
692 		++pattern;
693 		++path;
694 	}
695 
696 	if (pattern == patternEnd)
697 		return (path == pathEnd);
698 	else if (*pattern == '*')
699 	{
700 		string::const_iterator patternNext = pattern + 1;
701 		if (patternNext != patternEnd) {
702 			for (; path != pathEnd; ++path)
703 			{
704 				if (*patternNext == *path)
705 					if (matchWildcards(patternNext, patternEnd, path, pathEnd, allowPrefix))
706 						return true;
707 			}
708 		}
709 
710 		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
711 			return true;
712 	}
713 	else if (path == pathEnd && allowPrefix)
714 		return true;
715 
716 	return false;
717 }
718 
719 #if defined(TCU_HIERARCHICAL_CASEPATHS)
720 // Match a list of pattern components to a list of path components. A pattern
721 // component may contain *-wildcards. A pattern component "**" matches zero or
722 // more whole path components.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)723 static bool patternMatches(vector<string>::const_iterator	patternStart,
724 						   vector<string>::const_iterator	patternEnd,
725 						   vector<string>::const_iterator	pathStart,
726 						   vector<string>::const_iterator	pathEnd,
727 						   bool								allowPrefix)
728 {
729 	vector<string>::const_iterator	pattern	= patternStart;
730 	vector<string>::const_iterator	path	= pathStart;
731 
732 	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
733 		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
734 												path->begin(), path->end(), false)))
735 	{
736 		++pattern;
737 		++path;
738 	}
739 
740 	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
741 		return true;
742 	else if (pattern != patternEnd && *pattern == "**")
743 	{
744 		for (; path != pathEnd; ++path)
745 			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
746 				return true;
747 		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
748 			return true;
749 	}
750 
751 	return false;
752 }
753 #endif
754 
matches(const string & caseName,bool allowPrefix) const755 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
756 {
757 #if defined(TCU_HIERARCHICAL_CASEPATHS)
758 	const vector<string> components = de::splitString(caseName, '.');
759 #endif
760 
761 	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
762 	{
763 #if defined(TCU_HIERARCHICAL_CASEPATHS)
764 		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
765 
766 		if (patternMatches(patternComponents.begin(), patternComponents.end(),
767 						   components.begin(), components.end(), allowPrefix))
768 			return true;
769 #else
770 		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
771 						   caseName.begin(), caseName.end(), allowPrefix))
772 			return true;
773 #endif
774 	}
775 
776 	return false;
777 }
778 
779 /*--------------------------------------------------------------------*//*!
780  * \brief Construct command line
781  * \note CommandLine is not fully initialized until parse() has been called.
782  *//*--------------------------------------------------------------------*/
CommandLine(void)783 CommandLine::CommandLine (void)
784 	: m_appName(), m_logFlags(0)
785 {
786 }
787 
788 /*--------------------------------------------------------------------*//*!
789  * \brief Construct command line from standard argc, argv pair.
790  *
791  * Calls parse() with given arguments
792  * \param archive application's assets
793  * \param argc Number of arguments
794  * \param argv Command line arguments
795  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)796 CommandLine::CommandLine (int argc, const char* const* argv)
797 	: m_appName(argv[0]), m_logFlags (0)
798 {
799 	if (argc > 1)
800 	{
801 		int loop = 1;		// skip application name
802 		while (true)
803 		{
804 			m_initialCmdLine += std::string(argv[loop++]);
805 			if (loop >= argc)
806 				break;
807 			m_initialCmdLine += " ";
808 		}
809 	}
810 
811 	if (!parse(argc, argv))
812 		throw Exception("Failed to parse command line");
813 }
814 
815 /*--------------------------------------------------------------------*//*!
816  * \brief Construct command line from string.
817  *
818  * Calls parse() with given argument.
819  * \param archive application's assets
820  * \param cmdLine Full command line string.
821  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)822 CommandLine::CommandLine (const std::string& cmdLine)
823 	: m_appName(), m_initialCmdLine	(cmdLine)
824 {
825 	if (!parse(cmdLine))
826 		throw Exception("Failed to parse command line");
827 }
828 
~CommandLine(void)829 CommandLine::~CommandLine (void)
830 {
831 }
832 
clear(void)833 void CommandLine::clear (void)
834 {
835 	m_cmdLine.clear();
836 	m_logFlags = 0;
837 }
838 
getCommandLine(void) const839 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
840 {
841 	return m_cmdLine;
842 }
843 
getApplicationName(void) const844 const std::string& CommandLine::getApplicationName(void) const
845 {
846 	return m_appName;
847 }
848 
getInitialCmdLine(void) const849 const std::string& CommandLine::getInitialCmdLine(void) const
850 {
851 	return m_initialCmdLine;
852 }
853 
registerExtendedOptions(de::cmdline::Parser & parser)854 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
855 {
856 	DE_UNREF(parser);
857 }
858 
859 /*--------------------------------------------------------------------*//*!
860  * \brief Parse command line from standard argc, argv pair.
861  * \note parse() must be called exactly once.
862  * \param argc Number of arguments
863  * \param argv Command line arguments
864  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)865 bool CommandLine::parse (int argc, const char* const* argv)
866 {
867 	DebugOutStreambuf	sbuf;
868 	std::ostream		debugOut	(&sbuf);
869 	de::cmdline::Parser	parser;
870 
871 	opt::registerOptions(parser);
872 	opt::registerLegacyOptions(parser);
873 	registerExtendedOptions(parser);
874 
875 	clear();
876 
877 	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
878 	{
879 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
880 		parser.help(debugOut);
881 
882 		clear();
883 		return false;
884 	}
885 
886 	if (!m_cmdLine.getOption<opt::LogImages>())
887 		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
888 
889 	if (!m_cmdLine.getOption<opt::LogShaderSources>())
890 		m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
891 
892 	if (!m_cmdLine.getOption<opt::LogFlush>())
893 		m_logFlags |= QP_TEST_LOG_NO_FLUSH;
894 
895 	if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
896 		m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
897 
898 	if (m_cmdLine.getOption<opt::SubProcess>())
899 		m_logFlags |= QP_TEST_LOG_NO_INITIAL_OUTPUT;
900 
901 	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
902 		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
903 		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
904 		(m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
905 		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
906 	{
907 		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
908 		clear();
909 		return false;
910 	}
911 
912 	if (m_cmdLine.getArgs().size() > 0)
913 	{
914 		debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
915 
916 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
917 		parser.help(debugOut);
918 
919 		clear();
920 		return false;
921 	}
922 
923 	return true;
924 }
925 
926 /*--------------------------------------------------------------------*//*!
927  * \brief Parse command line from string.
928  * \note parse() must be called exactly once.
929  * \param cmdLine Full command line string.
930  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)931 bool CommandLine::parse (const std::string& cmdLine)
932 {
933 	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
934 	if (!parsedCmdLine)
935 		throw std::bad_alloc();
936 
937 	bool isOk = false;
938 	try
939 	{
940 		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
941 	}
942 	catch (...)
943 	{
944 		deCommandLine_destroy(parsedCmdLine);
945 		throw;
946 	}
947 
948 	deCommandLine_destroy(parsedCmdLine);
949 	return isOk;
950 }
951 
getLogFileName(void) const952 const char*				CommandLine::getLogFileName					(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();					}
getLogFlags(void) const953 deUint32				CommandLine::getLogFlags					(void) const	{ return m_logFlags;														}
getRunMode(void) const954 RunMode					CommandLine::getRunMode						(void) const	{ return m_cmdLine.getOption<opt::RunMode>();								}
getCaseListExportFile(void) const955 const char*				CommandLine::getCaseListExportFile			(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();			}
getVisibility(void) const956 WindowVisibility		CommandLine::getVisibility					(void) const	{ return m_cmdLine.getOption<opt::Visibility>();							}
isWatchDogEnabled(void) const957 bool					CommandLine::isWatchDogEnabled				(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();								}
isCrashHandlingEnabled(void) const958 bool					CommandLine::isCrashHandlingEnabled			(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();							}
getBaseSeed(void) const959 int						CommandLine::getBaseSeed					(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();								}
getTestIterationCount(void) const960 int						CommandLine::getTestIterationCount			(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();					}
getSurfaceWidth(void) const961 int						CommandLine::getSurfaceWidth				(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();							}
getSurfaceHeight(void) const962 int						CommandLine::getSurfaceHeight				(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();							}
getSurfaceType(void) const963 SurfaceType				CommandLine::getSurfaceType					(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();							}
getScreenRotation(void) const964 ScreenRotation			CommandLine::getScreenRotation				(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();						}
getGLConfigId(void) const965 int						CommandLine::getGLConfigId					(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();							}
getCLPlatformId(void) const966 int						CommandLine::getCLPlatformId				(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();							}
getCLDeviceIds(void) const967 const std::vector<int>&	CommandLine::getCLDeviceIds					(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();							}
getVKDeviceId(void) const968 int						CommandLine::getVKDeviceId					(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();							}
getVKDeviceGroupId(void) const969 int						CommandLine::getVKDeviceGroupId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();						}
isValidationEnabled(void) const970 bool					CommandLine::isValidationEnabled			(void) const	{ return m_cmdLine.getOption<opt::Validation>();							}
printValidationErrors(void) const971 bool					CommandLine::printValidationErrors			(void) const	{ return m_cmdLine.getOption<opt::PrintValidationErrors>();					}
isLogDecompiledSpirvEnabled(void) const972 bool					CommandLine::isLogDecompiledSpirvEnabled	(void) const	{ return m_cmdLine.getOption<opt::LogDecompiledSpirv>();					}
isOutOfMemoryTestEnabled(void) const973 bool					CommandLine::isOutOfMemoryTestEnabled		(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();								}
isShadercacheEnabled(void) const974 bool					CommandLine::isShadercacheEnabled			(void) const	{ return m_cmdLine.getOption<opt::ShaderCache>();							}
getShaderCacheFilename(void) const975 const char*				CommandLine::getShaderCacheFilename			(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();			}
isShaderCacheTruncateEnabled(void) const976 bool					CommandLine::isShaderCacheTruncateEnabled	(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheTruncate>();					}
isShaderCacheIPCEnabled(void) const977 bool					CommandLine::isShaderCacheIPCEnabled		(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheIPC>();						}
getOptimizationRecipe(void) const978 int						CommandLine::getOptimizationRecipe			(void) const	{ return m_cmdLine.getOption<opt::Optimization>();							}
isSpirvOptimizationEnabled(void) const979 bool					CommandLine::isSpirvOptimizationEnabled		(void) const	{ return m_cmdLine.getOption<opt::OptimizeSpirv>();							}
isRenderDocEnabled(void) const980 bool					CommandLine::isRenderDocEnabled				(void) const	{ return m_cmdLine.getOption<opt::RenderDoc>();								}
getWaiverFileName(void) const981 const char*				CommandLine::getWaiverFileName				(void) const	{ return m_cmdLine.getOption<opt::WaiverFile>().c_str();					}
getCaseFraction(void) const982 const std::vector<int>&	CommandLine::getCaseFraction				(void) const	{ return m_cmdLine.getOption<opt::CaseFraction>();							}
getCaseFractionMandatoryTests(void) const983 const char*				CommandLine::getCaseFractionMandatoryTests	(void) const	{ return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str();	}
getArchiveDir(void) const984 const char*				CommandLine::getArchiveDir					(void) const	{ return m_cmdLine.getOption<opt::ArchiveDir>().c_str();					}
getRunnerType(void) const985 tcu::TestRunnerType		CommandLine::getRunnerType					(void) const	{ return m_cmdLine.getOption<opt::RunnerType>();							}
isTerminateOnFailEnabled(void) const986 bool					CommandLine::isTerminateOnFailEnabled		(void) const	{ return m_cmdLine.getOption<opt::TerminateOnFail>();						}
isSubProcess(void) const987 bool					CommandLine::isSubProcess					(void) const	{ return m_cmdLine.getOption<opt::SubProcess>();							}
getSubprocessTestCount(void) const988 int						CommandLine::getSubprocessTestCount			(void) const	{ return m_cmdLine.getOption<opt::SubprocessTestCount>();					}
getCommandPoolMinSize(void) const989 int						CommandLine::getCommandPoolMinSize			(void) const	{ return m_cmdLine.getOption<opt::CommandPoolMinSize>();					}
getCommandBufferMinSize(void) const990 int						CommandLine::getCommandBufferMinSize		(void) const	{ return m_cmdLine.getOption<opt::CommandBufferMinSize>();					}
getCommandDefaultSize(void) const991 int						CommandLine::getCommandDefaultSize			(void) const	{ return m_cmdLine.getOption<opt::CommandDefaultSize>();					}
getPipelineDefaultSize(void) const992 int						CommandLine::getPipelineDefaultSize			(void) const	{ return m_cmdLine.getOption<opt::PipelineDefaultSize>();					}
993 
getGLContextType(void) const994 const char* CommandLine::getGLContextType (void) const
995 {
996 	if (m_cmdLine.hasOption<opt::GLContextType>())
997 		return m_cmdLine.getOption<opt::GLContextType>().c_str();
998 	else
999 		return DE_NULL;
1000 }
getGLConfigName(void) const1001 const char* CommandLine::getGLConfigName (void) const
1002 {
1003 	if (m_cmdLine.hasOption<opt::GLConfigName>())
1004 		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
1005 	else
1006 		return DE_NULL;
1007 }
1008 
getGLContextFlags(void) const1009 const char* CommandLine::getGLContextFlags (void) const
1010 {
1011 	if (m_cmdLine.hasOption<opt::GLContextFlags>())
1012 		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
1013 	else
1014 		return DE_NULL;
1015 }
1016 
getCLBuildOptions(void) const1017 const char* CommandLine::getCLBuildOptions (void) const
1018 {
1019 	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
1020 		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
1021 	else
1022 		return DE_NULL;
1023 }
1024 
getEGLDisplayType(void) const1025 const char* CommandLine::getEGLDisplayType (void) const
1026 {
1027 	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
1028 		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
1029 	else
1030 		return DE_NULL;
1031 }
1032 
getEGLWindowType(void) const1033 const char* CommandLine::getEGLWindowType (void) const
1034 {
1035 	if (m_cmdLine.hasOption<opt::EGLWindowType>())
1036 		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
1037 	else
1038 		return DE_NULL;
1039 }
1040 
getEGLPixmapType(void) const1041 const char* CommandLine::getEGLPixmapType (void) const
1042 {
1043 	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
1044 		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
1045 	else
1046 		return DE_NULL;
1047 }
1048 
getSubprocessConfigFile(void) const1049 const char* CommandLine::getSubprocessConfigFile (void) const
1050 {
1051 	if (m_cmdLine.hasOption<opt::SubprocessConfigFile>())
1052 		return m_cmdLine.getOption<opt::SubprocessConfigFile>().c_str();
1053 	else
1054 		return DE_NULL;
1055 }
1056 
1057 
getServerAddress(void) const1058 const char* CommandLine::getServerAddress (void) const
1059 {
1060 	if (m_cmdLine.hasOption<opt::ServerAddress>())
1061 		return m_cmdLine.getOption<opt::ServerAddress>().c_str();
1062 	else
1063 		return DE_NULL;
1064 }
1065 
getPipelineCompilerPath(void) const1066 const char* CommandLine::getPipelineCompilerPath(void) const
1067 {
1068 	if (m_cmdLine.hasOption<opt::PipelineCompilerPath>())
1069 		return m_cmdLine.getOption<opt::PipelineCompilerPath>().c_str();
1070 	else
1071 		return DE_NULL;
1072 }
1073 
getPipelineCompilerDataDir(void) const1074 const char* CommandLine::getPipelineCompilerDataDir(void) const
1075 {
1076 	if (m_cmdLine.hasOption<opt::PipelineCompilerDataDir>())
1077 		return m_cmdLine.getOption<opt::PipelineCompilerDataDir>().c_str();
1078 	else
1079 		return DE_NULL;
1080 }
1081 
getPipelineCompilerArgs(void) const1082 const char* CommandLine::getPipelineCompilerArgs(void) const
1083 {
1084 	if (m_cmdLine.hasOption<opt::PipelineCompilerArgs>())
1085 		return m_cmdLine.getOption<opt::PipelineCompilerArgs>().c_str();
1086 	else
1087 		return DE_NULL;
1088 }
1089 
getPipelineCompilerOutputFile(void) const1090 const char* CommandLine::getPipelineCompilerOutputFile(void) const
1091 {
1092 	if (m_cmdLine.hasOption<opt::PipelineCompilerOutputFile>())
1093 		return m_cmdLine.getOption<opt::PipelineCompilerOutputFile>().c_str();
1094 	else
1095 		return DE_NULL;
1096 }
1097 
getPipelineCompilerLogFile(void) const1098 const char* CommandLine::getPipelineCompilerLogFile(void) const
1099 {
1100 	if (m_cmdLine.hasOption<opt::PipelineCompilerLogFile>())
1101 		return m_cmdLine.getOption<opt::PipelineCompilerLogFile>().c_str();
1102 	else
1103 		return DE_NULL;
1104 }
1105 
getPipelineCompilerFilePrefix(void) const1106 const char* CommandLine::getPipelineCompilerFilePrefix(void) const
1107 {
1108 	if (m_cmdLine.hasOption<opt::PipelineCompilerFilePrefix>())
1109 		return m_cmdLine.getOption<opt::PipelineCompilerFilePrefix>().c_str();
1110 	else
1111 		return DE_NULL;
1112 }
1113 
getVkLibraryPath(void) const1114 const char* CommandLine::getVkLibraryPath(void) const
1115 {
1116 	if (m_cmdLine.hasOption<opt::VkLibraryPath>())
1117 		return (m_cmdLine.getOption<opt::VkLibraryPath>() != "") ? m_cmdLine.getOption<opt::VkLibraryPath>().c_str() : DE_NULL;
1118     else
1119 		return DE_NULL;
1120 }
1121 
getAppParamsInputFilePath(void) const1122 const char* CommandLine::getAppParamsInputFilePath(void) const
1123 {
1124 	if (m_cmdLine.hasOption<opt::ApplicationParametersInputFile>())
1125 		return m_cmdLine.getOption<opt::ApplicationParametersInputFile>().c_str();
1126 	else
1127 		return DE_NULL;
1128 }
1129 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)1130 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
1131 {
1132 	const CaseTreeNode* node = findNode(root, groupPath);
1133 	return node && node->hasChildren();
1134 }
1135 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)1136 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1137 {
1138 	const CaseTreeNode* node = findNode(root, casePath);
1139 	return node && !node->hasChildren();
1140 }
1141 
createCaseListFilter(const tcu::Archive & archive) const1142 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1143 {
1144 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1145 }
1146 
checkTestGroupName(const char * groupName) const1147 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1148 {
1149 	bool result = false;
1150 	if (m_casePaths)
1151 		result = m_casePaths->matches(groupName, true);
1152 	else if (m_caseTree)
1153 		result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1154 	else
1155 		return true;
1156 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1157 		result = m_caseFractionMandatoryTests->matches(groupName, true);
1158 	return result;
1159 }
1160 
checkTestCaseName(const char * caseName) const1161 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1162 {
1163 	bool result = false;
1164 	if (m_casePaths)
1165 		result = m_casePaths->matches(caseName, false);
1166 	else if (m_caseTree)
1167 		result = tcu::checkTestCaseName(m_caseTree, caseName);
1168 	else
1169 		return true;
1170 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1171 		result = m_caseFractionMandatoryTests->matches(caseName, false);
1172 	return result;
1173 }
1174 
checkCaseFraction(int i,const std::string & testCaseName) const1175 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1176 {
1177 	return	m_caseFraction.size() != 2 ||
1178 		((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1179 		(m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1180 }
1181 
CaseListFilter(void)1182 CaseListFilter::CaseListFilter (void)
1183 	: m_caseTree	(DE_NULL)
1184 	, m_runnerType	(tcu::RUNNERTYPE_ANY)
1185 {
1186 }
1187 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1188 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1189 	: m_caseTree	(DE_NULL)
1190 {
1191 	if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1192 	{
1193 		m_runnerType = RUNNERTYPE_AMBER;
1194 	}
1195 	else
1196 	{
1197 		m_runnerType = cmdLine.getOption<opt::RunnerType>();
1198 	}
1199 
1200 	if (cmdLine.hasOption<opt::CaseList>())
1201 	{
1202 		std::istringstream str(cmdLine.getOption<opt::CaseList>());
1203 
1204 		m_caseTree = parseCaseList(str, archive);
1205 	}
1206 	else if (cmdLine.hasOption<opt::CaseListFile>())
1207 	{
1208 		std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1209 		std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1210 
1211 		if (!in.is_open() || !in.good())
1212 			throw Exception("Failed to open case list file '" + caseListFile + "'");
1213 
1214 		m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1215 	}
1216 	else if (cmdLine.hasOption<opt::CaseListResource>())
1217 	{
1218 		// \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1219 		//						   istream adaptor for tcu::Resource.
1220 		de::UniquePtr<Resource>	caseListResource	(archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1221 		const int				bufferSize			= caseListResource->getSize();
1222 		std::vector<char>		buffer				((size_t)bufferSize);
1223 
1224 		if (buffer.empty())
1225 			throw Exception("Empty case list resource");
1226 
1227 		caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1228 
1229 		{
1230 			std::istringstream	in	(std::string(&buffer[0], (size_t)bufferSize));
1231 
1232 			m_caseTree = parseCaseList(in, archive);
1233 		}
1234 	}
1235 	else if (cmdLine.getOption<opt::StdinCaseList>())
1236 	{
1237 		m_caseTree = parseCaseList(std::cin, archive);
1238 	}
1239 	else if (cmdLine.hasOption<opt::CasePath>())
1240 		m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1241 
1242 	if (!cmdLine.getOption<opt::SubProcess>())
1243 		m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1244 
1245 	if (m_caseFraction.size() == 2 &&
1246 		(m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1247 		throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1248 
1249 	if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1250 		throw Exception("Invalid case fraction. Must have two components.");
1251 
1252 	if (m_caseFraction.size() == 2)
1253 	{
1254 		std::string					caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1255 
1256 		if (!caseFractionMandatoryTestsFilename.empty())
1257 		{
1258 			std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1259 			if (!fileStream.is_open() || !fileStream.good())
1260 				throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1261 
1262 			std::vector<std::string>	cfPaths;
1263 			std::string					line;
1264 
1265 			while (std::getline(fileStream, line))
1266 			{
1267 				line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1268 				cfPaths.push_back(line);
1269 			}
1270 			if (!cfPaths.empty())
1271 			{
1272 				m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1273 				if (m_caseTree != DE_NULL)
1274 				{
1275 					fileStream.clear();
1276 					fileStream.seekg(0, fileStream.beg);
1277 					parseCaseList(m_caseTree, fileStream, false);
1278 				}
1279 			}
1280 		}
1281 	}
1282 }
1283 
~CaseListFilter(void)1284 CaseListFilter::~CaseListFilter (void)
1285 {
1286 	delete m_caseTree;
1287 }
1288 
1289 } // tcu
1290