• 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 #include <unordered_map>
43 
44 using std::string;
45 using std::vector;
46 
47 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
48 #if (DE_OS == DE_OS_WIN32)
49 #	define TEST_OOM_DEFAULT		"enable"
50 #else
51 #	define TEST_OOM_DEFAULT		"disable"
52 #endif
53 
54 namespace tcu
55 {
56 
57 namespace opt
58 {
59 
60 DE_DECLARE_COMMAND_LINE_OPT(CasePath,					std::string);
61 DE_DECLARE_COMMAND_LINE_OPT(CaseList,					std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile,				std::string);
63 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource,			std::string);
64 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList,				bool);
65 DE_DECLARE_COMMAND_LINE_OPT(LogFilename,				std::string);
66 DE_DECLARE_COMMAND_LINE_OPT(RunMode,					tcu::RunMode);
67 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern,		std::string);
68 DE_DECLARE_COMMAND_LINE_OPT(WatchDog,					bool);
69 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler,				bool);
70 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed,					int);
71 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount,			int);
72 DE_DECLARE_COMMAND_LINE_OPT(Visibility,					WindowVisibility);
73 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth,				int);
74 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight,				int);
75 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType,				tcu::SurfaceType);
76 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation,				tcu::ScreenRotation);
77 DE_DECLARE_COMMAND_LINE_OPT(GLContextType,				std::string);
78 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID,					int);
79 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName,				std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags,				std::string);
81 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID,				int);
82 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs,				std::vector<int>);
83 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions,				std::string);
84 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType,				std::string);
85 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType,				std::string);
86 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType,				std::string);
87 DE_DECLARE_COMMAND_LINE_OPT(LogImages,					bool);
88 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources,			bool);
89 DE_DECLARE_COMMAND_LINE_OPT(LogDecompiledSpirv,			bool);
90 DE_DECLARE_COMMAND_LINE_OPT(LogEmptyLoginfo,			bool);
91 DE_DECLARE_COMMAND_LINE_OPT(TestOOM,					bool);
92 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir,					std::string);
93 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID,					int);
94 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID,			int);
95 DE_DECLARE_COMMAND_LINE_OPT(LogFlush,					bool);
96 DE_DECLARE_COMMAND_LINE_OPT(LogCompact,					bool);
97 DE_DECLARE_COMMAND_LINE_OPT(Validation,					bool);
98 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors,		bool);
99 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,				bool);
100 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,		std::string);
101 DE_DECLARE_COMMAND_LINE_OPT(Optimization,				int);
102 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,				bool);
103 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,		bool);
104 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheIPC,				bool);
105 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,					bool);
106 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction,				std::vector<int>);
107 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests,	std::string);
108 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile,					std::string);
109 DE_DECLARE_COMMAND_LINE_OPT(RunnerType,					tcu::TestRunnerType);
110 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail,			bool);
111 DE_DECLARE_COMMAND_LINE_OPT(SubProcess,					bool);
112 DE_DECLARE_COMMAND_LINE_OPT(SubprocessTestCount,		int);
113 DE_DECLARE_COMMAND_LINE_OPT(SubprocessConfigFile,		std::string);
114 DE_DECLARE_COMMAND_LINE_OPT(ServerAddress,				std::string);
115 DE_DECLARE_COMMAND_LINE_OPT(CommandPoolMinSize,			int);
116 DE_DECLARE_COMMAND_LINE_OPT(CommandBufferMinSize,		int);
117 DE_DECLARE_COMMAND_LINE_OPT(CommandDefaultSize,			int);
118 DE_DECLARE_COMMAND_LINE_OPT(PipelineDefaultSize,		int);
119 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath,		std::string);
120 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir,	std::string);
121 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs,		std::string);
122 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile,	std::string);
123 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile,	std::string);
124 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerFilePrefix,	std::string);
125 DE_DECLARE_COMMAND_LINE_OPT(VkLibraryPath,				std::string);
126 DE_DECLARE_COMMAND_LINE_OPT(ApplicationParametersInputFile,	std::string);
127 
128 
parseIntList(const char * src,std::vector<int> * dst)129 static void parseIntList (const char* src, std::vector<int>* dst)
130 {
131 	std::istringstream	str	(src);
132 	std::string			val;
133 
134 	while (std::getline(str, val, ','))
135 	{
136 		int intVal = 0;
137 		de::cmdline::parseType(val.c_str(), &intVal);
138 		dst->push_back(intVal);
139 	}
140 }
141 
registerOptions(de::cmdline::Parser & parser)142 void registerOptions (de::cmdline::Parser& parser)
143 {
144 	using de::cmdline::Option;
145 	using de::cmdline::NamedValue;
146 
147 	static const NamedValue<bool> s_enableNames[] =
148 	{
149 		{ "enable",		true	},
150 		{ "disable",	false	}
151 	};
152 	static const NamedValue<tcu::RunMode> s_runModes[] =
153 	{
154 		{ "execute",		RUNMODE_EXECUTE				  },
155 		{ "xml-caselist",	RUNMODE_DUMP_XML_CASELIST	  },
156 		{ "txt-caselist",	RUNMODE_DUMP_TEXT_CASELIST	  },
157 		{ "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST  },
158 		{ "amber-verify",   RUNMODE_VERIFY_AMBER_COHERENCY}
159 	};
160 	static const NamedValue<WindowVisibility> s_visibilites[] =
161 	{
162 		{ "windowed",		WINDOWVISIBILITY_WINDOWED	},
163 		{ "fullscreen",		WINDOWVISIBILITY_FULLSCREEN	},
164 		{ "hidden",			WINDOWVISIBILITY_HIDDEN		}
165 	};
166 	static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
167 	{
168 		{ "window",			SURFACETYPE_WINDOW				},
169 		{ "pixmap",			SURFACETYPE_OFFSCREEN_NATIVE	},
170 		{ "pbuffer",		SURFACETYPE_OFFSCREEN_GENERIC	},
171 		{ "fbo",			SURFACETYPE_FBO					}
172 	};
173 	static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
174 	{
175 		{ "unspecified",	SCREENROTATION_UNSPECIFIED	},
176 		{ "0",				SCREENROTATION_0			},
177 		{ "90",				SCREENROTATION_90			},
178 		{ "180",			SCREENROTATION_180			},
179 		{ "270",			SCREENROTATION_270			}
180 	};
181 	static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
182 	{
183 		{ "any",	tcu::RUNNERTYPE_ANY		},
184 		{ "none",	tcu::RUNNERTYPE_NONE	},
185 		{ "amber",	tcu::RUNNERTYPE_AMBER	},
186 	};
187 
188 	parser
189 		<< Option<CasePath>						("n",		"deqp-case",								"Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
190 		<< Option<CaseList>						(DE_NULL,	"deqp-caselist",							"Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
191 		<< Option<CaseListFile>					(DE_NULL,	"deqp-caselist-file",						"Read case list (in trie format) from given file")
192 		<< Option<CaseListResource>				(DE_NULL,	"deqp-caselist-resource",					"Read case list (in trie format) from given file located application's assets")
193 		<< Option<StdinCaseList>				(DE_NULL,	"deqp-stdin-caselist",						"Read case list (in trie format) from stdin")
194 		<< Option<LogFilename>					(DE_NULL,	"deqp-log-filename",						"Write test results to given file",					"TestResults.qpa")
195 		<< Option<RunMode>						(DE_NULL,	"deqp-runmode",								"Execute tests, write list of test cases into a file, or verify amber capability coherency",
196 																																							s_runModes,			"execute")
197 		<< Option<ExportFilenamePattern>		(DE_NULL,	"deqp-caselist-export-file",				"Set the target file name pattern for caselist export",					"${packageName}-cases.${typeExtension}")
198 		<< Option<WatchDog>						(DE_NULL,	"deqp-watchdog",							"Enable test watchdog",								s_enableNames,		"disable")
199 		<< Option<CrashHandler>					(DE_NULL,	"deqp-crashhandler",						"Enable crash handling",							s_enableNames,		"disable")
200 		<< Option<BaseSeed>						(DE_NULL,	"deqp-base-seed",							"Base seed for test cases that use randomization",						"0")
201 		<< Option<TestIterationCount>			(DE_NULL,	"deqp-test-iteration-count",				"Iteration count for cases that support variable number of iterations",	"0")
202 		<< Option<Visibility>					(DE_NULL,	"deqp-visibility",							"Default test window visibility",					s_visibilites,		"windowed")
203 		<< Option<SurfaceWidth>					(DE_NULL,	"deqp-surface-width",						"Use given surface width if possible",									"-1")
204 		<< Option<SurfaceHeight>				(DE_NULL,	"deqp-surface-height",						"Use given surface height if possible",									"-1")
205 		<< Option<SurfaceType>					(DE_NULL,	"deqp-surface-type",						"Use given surface type",							s_surfaceTypes,		"window")
206 		<< Option<ScreenRotation>				(DE_NULL,	"deqp-screen-rotation",						"Screen rotation for platforms that support it",	s_screenRotations,	"0")
207 		<< Option<GLContextType>				(DE_NULL,	"deqp-gl-context-type",						"OpenGL context type for platforms that support multiple")
208 		<< Option<GLConfigID>					(DE_NULL,	"deqp-gl-config-id",						"OpenGL (ES) render config ID (EGL config id on EGL platforms)",		"-1")
209 		<< Option<GLConfigName>					(DE_NULL,	"deqp-gl-config-name",						"Symbolic OpenGL (ES) render config name")
210 		<< Option<GLContextFlags>				(DE_NULL,	"deqp-gl-context-flags",					"OpenGL context flags (comma-separated, supports debug and robust)")
211 		<< Option<CLPlatformID>					(DE_NULL,	"deqp-cl-platform-id",						"Execute tests on given OpenCL platform (IDs start from 1)",			"1")
212 		<< Option<CLDeviceIDs>					(DE_NULL,	"deqp-cl-device-ids",						"Execute tests on given CL devices (comma-separated, IDs start from 1)",	parseIntList,	"")
213 		<< Option<CLBuildOptions>				(DE_NULL,	"deqp-cl-build-options",					"Extra build options for OpenCL compiler")
214 		<< Option<EGLDisplayType>				(DE_NULL,	"deqp-egl-display-type",					"EGL native display type")
215 		<< Option<EGLWindowType>				(DE_NULL,	"deqp-egl-window-type",						"EGL native window type")
216 		<< Option<EGLPixmapType>				(DE_NULL,	"deqp-egl-pixmap-type",						"EGL native pixmap type")
217 		<< Option<VKDeviceID>					(DE_NULL,	"deqp-vk-device-id",						"Vulkan device ID (IDs start from 1)",									"1")
218 		<< Option<VKDeviceGroupID>				(DE_NULL,	"deqp-vk-device-group-id",					"Vulkan device Group ID (IDs start from 1)",							"1")
219 		<< Option<LogImages>					(DE_NULL,	"deqp-log-images",							"Enable or disable logging of result images",		s_enableNames,		"enable")
220 		<< Option<LogShaderSources>				(DE_NULL,	"deqp-log-shader-sources",					"Enable or disable logging of shader sources",		s_enableNames,		"enable")
221 		<< Option<LogDecompiledSpirv>			(DE_NULL,	"deqp-log-decompiled-spirv",				"Enable or disable logging of decompiled spir-v",	s_enableNames,		"enable")
222 		<< Option<LogEmptyLoginfo>				(DE_NULL,	"deqp-log-empty-loginfo",					"Logging of empty shader compile/link log info",	s_enableNames,		"enable")
223 		<< Option<TestOOM>						(DE_NULL,	"deqp-test-oom",							"Run tests that exhaust memory on purpose",			s_enableNames,		TEST_OOM_DEFAULT)
224 		<< Option<ArchiveDir>					(DE_NULL,	"deqp-archive-dir",							"Path to test resource files",											".")
225 		<< Option<LogFlush>						(DE_NULL,	"deqp-log-flush",							"Enable or disable log file fflush",				s_enableNames,		"enable")
226 		<< Option<LogCompact>					(DE_NULL,	"deqp-log-compact",							"Enable or disable the compact version of the log",								s_enableNames,		"disable")
227 		<< Option<Validation>					(DE_NULL,	"deqp-validation",							"Enable or disable test case validation",			s_enableNames,		"disable")
228 		<< Option<PrintValidationErrors>		(DE_NULL,	"deqp-print-validation-errors",				"Print validation errors to standard error")
229 		<< Option<Optimization>					(DE_NULL,	"deqp-optimization-recipe",					"Shader optimization recipe (0=disabled, 1=performance, 2=size)",		"0")
230 		<< Option<OptimizeSpirv>				(DE_NULL,	"deqp-optimize-spirv",						"Apply optimization to spir-v shaders as well",		s_enableNames,		"disable")
231 		<< Option<ShaderCache>					(DE_NULL,	"deqp-shadercache",							"Enable or disable shader cache",					s_enableNames,		"enable")
232 		<< Option<ShaderCacheFilename>			(DE_NULL,	"deqp-shadercache-filename",				"Write shader cache to given file",										"shadercache.bin")
233 		<< Option<ShaderCacheTruncate>			(DE_NULL,	"deqp-shadercache-truncate",				"Truncate shader cache before running tests",		s_enableNames,		"enable")
234 		<< Option<ShaderCacheIPC>				(DE_NULL,	"deqp-shadercache-ipc",						"Should shader cache use inter process comms",		s_enableNames,		"disable")
235 		<< Option<RenderDoc>					(DE_NULL,	"deqp-renderdoc",							"Enable RenderDoc frame markers",					s_enableNames,		"disable")
236 		<< Option<CaseFraction>					(DE_NULL,	"deqp-fraction",							"Run a fraction of the test cases (e.g. N,M means run group%M==N)",	parseIntList,	"")
237 		<< Option<CaseFractionMandatoryTests>	(DE_NULL,	"deqp-fraction-mandatory-caselist-file",	"Case list file that must be run for each fraction",					"")
238 		<< Option<WaiverFile>					(DE_NULL,	"deqp-waiver-file",							"Read waived tests from given file",									"")
239 		<< Option<RunnerType>					(DE_NULL,	"deqp-runner-type",							"Filter test cases based on runner",				s_runnerTypes,		"any")
240 		<< Option<TerminateOnFail>				(DE_NULL,	"deqp-terminate-on-fail",					"Terminate the run on first failure",				s_enableNames,		"disable")
241 		<< Option<SubProcess>					(DE_NULL,	"deqp-subprocess",							"Inform app that it works as subprocess (Vulkan SC only, do not use manually)", s_enableNames, "disable")
242 		<< Option<SubprocessTestCount>			(DE_NULL,	"deqp-subprocess-test-count",				"Define default number of tests performed in subprocess for specific test cases(Vulkan SC only)",	"65536")
243 		<< Option<SubprocessConfigFile>			(DE_NULL,	"deqp-subprocess-cfg-file",					"Config file defining number of tests performed in subprocess for specific test branches (Vulkan SC only)", "")
244 		<< Option<ServerAddress>				(DE_NULL,	"deqp-server-address",						"Server address (host:port) responsible for shader compilation (Vulkan SC only)", "")
245 		<< Option<CommandPoolMinSize>			(DE_NULL,	"deqp-command-pool-min-size",				"Define minimum size of the command pool (in bytes) to use (Vulkan SC only)","0")
246 		<< Option<CommandBufferMinSize>			(DE_NULL,	"deqp-command-buffer-min-size",				"Define minimum size of the command buffer (in bytes) to use (Vulkan SC only)", "0")
247 		<< Option<CommandDefaultSize>			(DE_NULL,	"deqp-command-default-size",				"Define default single command size (in bytes) to use (Vulkan SC only)",	"256")
248 		<< Option<PipelineDefaultSize>			(DE_NULL,	"deqp-pipeline-default-size",				"Define default pipeline size (in bytes) to use (Vulkan SC only)",		"16384")
249 		<< Option<PipelineCompilerPath>			(DE_NULL,	"deqp-pipeline-compiler",					"Path to offline pipeline compiler (Vulkan SC only)", "")
250 		<< Option<PipelineCompilerDataDir>		(DE_NULL,	"deqp-pipeline-dir",						"Offline pipeline data directory (Vulkan SC only)", "")
251 		<< Option<PipelineCompilerArgs>			(DE_NULL,	"deqp-pipeline-args",						"Additional compiler parameters (Vulkan SC only)", "")
252 		<< Option<PipelineCompilerOutputFile>	(DE_NULL,	"deqp-pipeline-file",						"Output file with pipeline cache (Vulkan SC only, do not use manually)", "")
253 		<< Option<PipelineCompilerLogFile>		(DE_NULL,	"deqp-pipeline-logfile",					"Log file for pipeline compiler (Vulkan SC only, do not use manually)", "")
254 		<< Option<PipelineCompilerFilePrefix>	(DE_NULL,	"deqp-pipeline-prefix",						"Prefix for input pipeline compiler files (Vulkan SC only, do not use manually)", "")
255 		<< Option<VkLibraryPath>				(DE_NULL,	"deqp-vk-library-path",						"Path to Vulkan library (e.g. loader library vulkan-1.dll)", "")
256 		<< Option<ApplicationParametersInputFile>    (DE_NULL,       "deqp-app-params-input-file",				"File that provides a default set of application parameters");
257 }
258 
registerLegacyOptions(de::cmdline::Parser & parser)259 void registerLegacyOptions (de::cmdline::Parser& parser)
260 {
261 	using de::cmdline::Option;
262 
263 	parser
264 		<< Option<GLConfigID>			(DE_NULL,	"deqp-egl-config-id",			"Legacy name for --deqp-gl-config-id",	"-1")
265 		<< Option<GLConfigName>			(DE_NULL,	"deqp-egl-config-name",			"Legacy name for --deqp-gl-config-name");
266 }
267 
268 } // opt
269 
270 
271 // Used to store hashes of test case names
272 typedef uint64_t test_case_hash_t;
273 
274 // Source: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp
275 // MurmurHash2, 64-bit versions, by Austin Appleby
MurmurHash64B(const void * key,int len,uint64_t seed)276 static uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed )
277 {
278 	const uint32_t m = 0x5bd1e995;
279 	const int r = 24;
280 
281 	uint32_t h1 = uint32_t(seed) ^ len;
282 	uint32_t h2 = uint32_t(seed >> 32);
283 
284 	// Ensure that unaligned accesses to data are allowed.
285 #ifdef WIN32
286 	typedef __declspec(align(1)) uint32_t uint32_t_unaligned;
287 #else
288 	typedef __attribute__((aligned(1))) uint32_t uint32_t_unaligned;
289 #endif
290 	const uint32_t_unaligned * data = (const uint32_t_unaligned *)key;
291 
292 	while(len >= 8)
293 	{
294 		uint32_t k1 = *data++;
295 		k1 *= m; k1 ^= k1 >> r; k1 *= m;
296 		h1 *= m; h1 ^= k1;
297 		len -= 4;
298 
299 		uint32_t k2 = *data++;
300 		k2 *= m; k2 ^= k2 >> r; k2 *= m;
301 		h2 *= m; h2 ^= k2;
302 		len -= 4;
303 	}
304 
305 	if(len >= 4)
306 	{
307 		uint32_t k1 = *data++;
308 		k1 *= m; k1 ^= k1 >> r; k1 *= m;
309 		h1 *= m; h1 ^= k1;
310 		len -= 4;
311 	}
312 
313 	switch(len)
314 	{
315 		case 3: h2 ^= ((unsigned char*)data)[2] << 16;
316 			// fall through
317 		case 2: h2 ^= ((unsigned char*)data)[1] << 8;
318 			// fall through
319 		case 1: h2 ^= ((unsigned char*)data)[0];
320 			h2 *= m;
321 	};
322 
323 	h1 ^= h2 >> 18; h1 *= m;
324 	h2 ^= h1 >> 22; h2 *= m;
325 	h1 ^= h2 >> 17; h1 *= m;
326 	h2 ^= h1 >> 19; h2 *= m;
327 
328 	uint64_t h = h1;
329 
330 	h = (h << 32) | h2;
331 
332 	return h;
333 }
334 
335 /*--------------------------------------------------------------------*//*!
336  * \brief Generates an hash for the test case name part provided.
337  * If a hashCollisionDetectionMap is passed, will detect hash
338  * collisions using that map. hashCollisionDetectionMap can be NULL.
339  * As an example, the standard std::hash<std::string> on a 32-bit
340  * machine will collide with 'random_298' and 'subgroupand_int16_t_mesh_requiredsubgroupsize'
341  *//*--------------------------------------------------------------------*/
hashTestNodeName(const std::string & name,std::unordered_map<test_case_hash_t,std::string> * hashCollisionDetectionMap)342 static test_case_hash_t hashTestNodeName (const std::string &name, std::unordered_map<test_case_hash_t, std::string> *hashCollisionDetectionMap)
343 {
344 	test_case_hash_t hash = MurmurHash64B(name.c_str(), (int)name.length(), 1);
345 	if(hashCollisionDetectionMap != nullptr) {
346 		auto search = hashCollisionDetectionMap->find(hash);
347 		if (search != hashCollisionDetectionMap->end()) {
348 			if (search->second != name) {
349 				print("There was an hash collision between '%s' and '%s'\n", search->second.c_str(), name.c_str());
350 				throw std::runtime_error("Hash collision detected!");
351 			}
352 		}
353 		hashCollisionDetectionMap->insert({hash, name});
354 	}
355 	return hash;
356 }
357 
358 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
359 class DebugOutStreambuf : public std::streambuf
360 {
361 public:
362 						DebugOutStreambuf	(void);
363 						~DebugOutStreambuf	(void);
364 
365 protected:
366 	std::streamsize		xsputn				(const char* s, std::streamsize count);
367 	int					overflow			(int ch = -1);
368 
369 private:
370 	void				flushLine			(void);
371 
372 	std::ostringstream	m_curLine;
373 };
374 
DebugOutStreambuf(void)375 DebugOutStreambuf::DebugOutStreambuf (void)
376 {
377 }
378 
~DebugOutStreambuf(void)379 DebugOutStreambuf::~DebugOutStreambuf (void)
380 {
381 	if (m_curLine.tellp() != std::streampos(0))
382 		flushLine();
383 }
384 
xsputn(const char * s,std::streamsize count)385 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
386 {
387 	for (std::streamsize pos = 0; pos < count; pos++)
388 	{
389 		m_curLine.put(s[pos]);
390 
391 		if (s[pos] == '\n')
392 			flushLine();
393 	}
394 
395 	return count;
396 }
397 
overflow(int ch)398 int DebugOutStreambuf::overflow (int ch)
399 {
400 	if (ch == -1)
401 		return -1;
402 	else
403 	{
404 		DE_ASSERT((ch & 0xff) == ch);
405 		const char chVal = (char)(deUint8)(ch & 0xff);
406 		return xsputn(&chVal, 1) == 1 ? ch : -1;
407 	}
408 }
409 
flushLine(void)410 void DebugOutStreambuf::flushLine (void)
411 {
412 	qpPrint(m_curLine.str().c_str());
413 	m_curLine.str("");
414 }
415 
416 class CaseTreeNode
417 {
418 public:
CaseTreeNode(const test_case_hash_t hash)419 										CaseTreeNode		(const test_case_hash_t hash) : m_hash(hash) {}
420 										~CaseTreeNode		(void);
421 
getHash(void) const422 	test_case_hash_t					getHash				(void) const { return m_hash;				}
hasChildren(void) const423 	bool								hasChildren			(void) const { return !m_children.empty();	}
424 
425 	bool								hasChild			(test_case_hash_t hash) const;
426 	CaseTreeNode*						getChild			(test_case_hash_t hash) const;
427 
addChild(CaseTreeNode * child)428 	void								addChild			(CaseTreeNode* child) { m_children.push_back(child); }
429 
430 private:
431 										CaseTreeNode		(const CaseTreeNode&);
432 	CaseTreeNode&						operator=			(const CaseTreeNode&);
433 
434 	enum { NOT_FOUND = -1 };
435 
436 	int									findChildNdx		(test_case_hash_t hash) const;
437 
438 	test_case_hash_t					m_hash;
439 	std::vector<CaseTreeNode*>			m_children;
440 };
441 
~CaseTreeNode(void)442 CaseTreeNode::~CaseTreeNode (void)
443 {
444 	for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
445 		delete *i;
446 }
447 
findChildNdx(test_case_hash_t hash) const448 int CaseTreeNode::findChildNdx (test_case_hash_t hash) const
449 {
450 	for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
451 	{
452 		if (m_children[ndx]->getHash() == hash)
453 			return ndx;
454 	}
455 	return NOT_FOUND;
456 }
457 
hasChild(test_case_hash_t hash) const458 inline bool CaseTreeNode::hasChild (test_case_hash_t hash) const
459 {
460 	return findChildNdx(hash) != NOT_FOUND;
461 }
462 
getChild(test_case_hash_t hash) const463 inline CaseTreeNode* CaseTreeNode::getChild (test_case_hash_t hash) const
464 {
465 	const int ndx = findChildNdx(hash);
466 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
467 }
468 
getCurrentComponentLen(const char * path)469 static int getCurrentComponentLen (const char* path)
470 {
471 	int ndx = 0;
472 	for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
473 	return ndx;
474 }
475 
findNode(const CaseTreeNode * root,const char * path)476 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
477 {
478 	const CaseTreeNode*	curNode		= root;
479 	const char*			curPath		= path;
480 	int					curLen		= getCurrentComponentLen(curPath);
481 
482 	for (;;)
483 	{
484 		test_case_hash_t hash = hashTestNodeName(std::string(curPath, curPath + curLen), nullptr);
485 		curNode = curNode->getChild(hash);
486 
487 		if (!curNode)
488 			break;
489 
490 		curPath	+= curLen;
491 
492 		if (curPath[0] == 0)
493 			break;
494 		else
495 		{
496 			DE_ASSERT(curPath[0] == '.');
497 			curPath		+= 1;
498 			curLen		 = getCurrentComponentLen(curPath);
499 		}
500 	}
501 
502 	return curNode;
503 }
504 
parseCaseTrie(CaseTreeNode * root,std::istream & in,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)505 static void parseCaseTrie (CaseTreeNode* root, std::istream& in, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
506 {
507 	vector<CaseTreeNode*>	nodeStack;
508 	string					curName;
509 	bool					expectNode		= true;
510 
511 	if (in.get() != '{')
512 		throw std::invalid_argument("Malformed case trie");
513 
514 	nodeStack.push_back(root);
515 
516 	while (!nodeStack.empty())
517 	{
518 		const int	curChr	= in.get();
519 
520 		if (curChr == std::char_traits<char>::eof() || curChr == 0)
521 			throw std::invalid_argument("Unterminated case tree");
522 
523 		if (curChr == '{' || curChr == ',' || curChr == '}')
524 		{
525 			if (!curName.empty() && expectNode)
526 			{
527 				test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
528 				CaseTreeNode* const newChild = new CaseTreeNode(hash);
529 
530 				try
531 				{
532 					nodeStack.back()->addChild(newChild);
533 				}
534 				catch (...)
535 				{
536 					delete newChild;
537 					throw;
538 				}
539 
540 				if (curChr == '{')
541 					nodeStack.push_back(newChild);
542 
543 				curName.clear();
544 			}
545 			else if (curName.empty() == expectNode)
546 				throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
547 
548 			if (curChr == '}')
549 			{
550 				expectNode = false;
551 				nodeStack.pop_back();
552 
553 				// consume trailing new line
554 				if (nodeStack.empty())
555 				{
556 					if (in.peek() == '\r')
557 					  in.get();
558 					if (in.peek() == '\n')
559 					  in.get();
560 				}
561 			}
562 			else
563 				expectNode = true;
564 		}
565 		else if (isValidTestCaseNameChar((char)curChr))
566 			curName += (char)curChr;
567 		else
568 			throw std::invalid_argument("Illegal character in node name");
569 	}
570 }
571 
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)572 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
573 {
574 	// \note Algorithm assumes that cases are sorted by groups, but will
575 	//		 function fine, albeit more slowly, if that is not the case.
576 	int		stackPos	= 0;
577 	string	curName;
578 
579 	for (;;)
580 	{
581 		const int curChr = in.get();
582 
583 		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
584 		{
585 			if (curName.empty())
586 				throw std::invalid_argument("Empty test case name");
587 
588 			test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
589 			if (!nodeStack[stackPos]->hasChild(hash))
590 			{
591 				CaseTreeNode* const newChild = new CaseTreeNode(hash);
592 
593 				try
594 				{
595 					nodeStack[stackPos]->addChild(newChild);
596 				}
597 				catch (...)
598 				{
599 					delete newChild;
600 					throw;
601 				}
602 			}
603 			else if (reportDuplicates)
604 				throw std::invalid_argument("Duplicate test case");
605 
606 			curName.clear();
607 			stackPos = 0;
608 
609 			if (curChr == '\r' && in.peek() == '\n')
610 				in.get();
611 
612 			{
613 				const int nextChr = in.peek();
614 
615 				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
616 					break;
617 			}
618 		}
619 		else if (curChr == '.')
620 		{
621 			if (curName.empty())
622 				throw std::invalid_argument("Empty test group name");
623 
624 			if ((int)nodeStack.size() <= stackPos+1)
625 				nodeStack.resize(nodeStack.size()*2, DE_NULL);
626 
627 			test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
628 			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getHash() != hash)
629 			{
630 				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(hash);
631 
632 				if (!curGroup)
633 				{
634 					curGroup = new CaseTreeNode(hash);
635 
636 					try
637 					{
638 						nodeStack[stackPos]->addChild(curGroup);
639 					}
640 					catch (...)
641 					{
642 						delete curGroup;
643 						throw;
644 					}
645 				}
646 
647 				nodeStack[stackPos+1] = curGroup;
648 
649 				if ((int)nodeStack.size() > stackPos+2)
650 					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
651 			}
652 
653 			DE_ASSERT(nodeStack[stackPos+1]->getHash() == hash);
654 
655 			curName.clear();
656 			stackPos += 1;
657 		}
658 		else if (isValidTestCaseNameChar((char)curChr))
659 			curName += (char)curChr;
660 		else
661 			throw std::invalid_argument("Illegal character in test case name");
662 	}
663 }
664 
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)665 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
666 {
667 	vector<CaseTreeNode*> nodeStack(8, root);
668 	parseSimpleCaseList(nodeStack, in, reportDuplicates, hashCollisionDetectionMap);
669 }
670 
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)671 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
672 {
673 	// read whole file and remove all '\r'
674 	std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
675 	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
676 
677 	vector<CaseTreeNode*>	nodeStack(8, root);
678 	std::stringstream		namesStream(buffer);
679 	std::string				fileName;
680 
681 	while (std::getline(namesStream, fileName))
682 	{
683 		de::FilePath			groupPath		(fileName);
684 		de::UniquePtr<Resource>	groupResource	(archive.getResource(groupPath.normalize().getPath()));
685 		const int				groupBufferSize	(groupResource->getSize());
686 		std::vector<char>		groupBuffer		(static_cast<size_t>(groupBufferSize));
687 
688 		groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
689 		if (groupBuffer.empty())
690 			throw Exception("Empty case list resource");
691 
692 		std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
693 		parseSimpleCaseList(nodeStack, groupIn, reportDuplicates, hashCollisionDetectionMap);
694 	}
695 }
696 
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)697 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
698 {
699 	std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
700 	auto rootName = "";
701 	test_case_hash_t hash = hashTestNodeName(rootName, &hashCollisionDetectionMap);
702 	CaseTreeNode* const root = new CaseTreeNode(hash);
703 	try
704 	{
705 		if (in.peek() == '{')
706 			parseCaseTrie(root, in, hashCollisionDetectionMap);
707 		else
708 		{
709 			// if we are reading cases from file determine if we are
710 			// reading group file or plain list of cases; this is done by
711 			// reading single line and checking if it ends with ".txt"
712 			bool readGroupFile = false;
713 			if (path)
714 			{
715 				// read the first line and make sure it doesn't contain '\r'
716 				std::string line;
717 				std::getline(in, line);
718 				line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
719 
720 				const std::string ending = ".txt";
721 				readGroupFile = (line.length() > ending.length()) &&
722 								std::equal(ending.rbegin(), ending.rend(), line.rbegin());
723 
724 				// move to the beginning of the file to parse first line too
725 				in.seekg(0, in.beg);
726 			}
727 
728 			if (readGroupFile)
729 				parseGroupFile(root, in, archive, true, hashCollisionDetectionMap);
730 			else
731 				parseCaseList(root, in, true, hashCollisionDetectionMap);
732 		}
733 
734 		{
735 			const int curChr = in.get();
736 			if (curChr != std::char_traits<char>::eof() && curChr != 0)
737 				throw std::invalid_argument("Trailing characters at end of case list");
738 		}
739 
740 		return root;
741 	}
742 	catch (...)
743 	{
744 		delete root;
745 		throw;
746 	}
747 }
748 
749 class CasePaths
750 {
751 public:
752 	CasePaths(const string& pathList);
753 	CasePaths(const vector<string>& pathList);
754 	bool					matches(const string& caseName, bool allowPrefix = false) const;
755 
756 private:
757 	const vector<string>	m_casePatterns;
758 };
759 
CasePaths(const string & pathList)760 CasePaths::CasePaths (const string& pathList)
761 	: m_casePatterns(de::splitString(pathList, ','))
762 {
763 }
764 
CasePaths(const vector<string> & pathList)765 CasePaths::CasePaths(const vector<string>& pathList)
766 	: m_casePatterns(pathList)
767 {
768 }
769 
770 // 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)771 bool matchWildcards(string::const_iterator	patternStart,
772 					string::const_iterator	patternEnd,
773 					string::const_iterator	pathStart,
774 					string::const_iterator	pathEnd,
775 					bool					allowPrefix)
776 {
777 	string::const_iterator	pattern	= patternStart;
778 	string::const_iterator	path	= pathStart;
779 
780 	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
781 	{
782 		++pattern;
783 		++path;
784 	}
785 
786 	if (pattern == patternEnd)
787 		return (path == pathEnd);
788 	else if (*pattern == '*')
789 	{
790 		string::const_iterator patternNext = pattern + 1;
791 		if (patternNext != patternEnd) {
792 			for (; path != pathEnd; ++path)
793 			{
794 				if (*patternNext == *path)
795 					if (matchWildcards(patternNext, patternEnd, path, pathEnd, allowPrefix))
796 						return true;
797 			}
798 		}
799 
800 		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
801 			return true;
802 	}
803 	else if (path == pathEnd && allowPrefix)
804 		return true;
805 
806 	return false;
807 }
808 
809 #if defined(TCU_HIERARCHICAL_CASEPATHS)
810 // Match a list of pattern components to a list of path components. A pattern
811 // component may contain *-wildcards. A pattern component "**" matches zero or
812 // 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)813 static bool patternMatches(vector<string>::const_iterator	patternStart,
814 						   vector<string>::const_iterator	patternEnd,
815 						   vector<string>::const_iterator	pathStart,
816 						   vector<string>::const_iterator	pathEnd,
817 						   bool								allowPrefix)
818 {
819 	vector<string>::const_iterator	pattern	= patternStart;
820 	vector<string>::const_iterator	path	= pathStart;
821 
822 	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
823 		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
824 												path->begin(), path->end(), false)))
825 	{
826 		++pattern;
827 		++path;
828 	}
829 
830 	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
831 		return true;
832 	else if (pattern != patternEnd && *pattern == "**")
833 	{
834 		for (; path != pathEnd; ++path)
835 			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
836 				return true;
837 		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
838 			return true;
839 	}
840 
841 	return false;
842 }
843 #endif
844 
matches(const string & caseName,bool allowPrefix) const845 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
846 {
847 #if defined(TCU_HIERARCHICAL_CASEPATHS)
848 	const vector<string> components = de::splitString(caseName, '.');
849 #endif
850 
851 	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
852 	{
853 #if defined(TCU_HIERARCHICAL_CASEPATHS)
854 		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
855 
856 		if (patternMatches(patternComponents.begin(), patternComponents.end(),
857 						   components.begin(), components.end(), allowPrefix))
858 			return true;
859 #else
860 		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
861 						   caseName.begin(), caseName.end(), allowPrefix))
862 			return true;
863 #endif
864 	}
865 
866 	return false;
867 }
868 
869 /*--------------------------------------------------------------------*//*!
870  * \brief Construct command line
871  * \note CommandLine is not fully initialized until parse() has been called.
872  *//*--------------------------------------------------------------------*/
CommandLine(void)873 CommandLine::CommandLine (void)
874 	: m_appName(), m_logFlags(0), m_hadHelpSpecified(false)
875 {
876 }
877 
878 /*--------------------------------------------------------------------*//*!
879  * \brief Construct command line from standard argc, argv pair.
880  *
881  * Calls parse() with given arguments
882  * \param archive application's assets
883  * \param argc Number of arguments
884  * \param argv Command line arguments
885  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)886 CommandLine::CommandLine (int argc, const char* const* argv)
887 	: m_appName(argv[0]), m_logFlags (0), m_hadHelpSpecified(false)
888 {
889 	if (argc > 1)
890 	{
891 		int loop = 1;		// skip application name
892 		while (true)
893 		{
894 			m_initialCmdLine += std::string(argv[loop++]);
895 			if (loop >= argc)
896 				break;
897 			m_initialCmdLine += " ";
898 		}
899 	}
900 
901 	if (!parse(argc, argv))
902 	{
903 		if (m_hadHelpSpecified)
904 			exit(EXIT_SUCCESS);
905 		else
906 			throw Exception("Failed to parse command line");
907 	}
908 }
909 
910 /*--------------------------------------------------------------------*//*!
911  * \brief Construct command line from string.
912  *
913  * Calls parse() with given argument.
914  * \param archive application's assets
915  * \param cmdLine Full command line string.
916  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)917 CommandLine::CommandLine (const std::string& cmdLine)
918 	: m_appName(), m_initialCmdLine	(cmdLine), m_hadHelpSpecified(false)
919 {
920 	if (!parse(cmdLine))
921 		throw Exception("Failed to parse command line");
922 }
923 
~CommandLine(void)924 CommandLine::~CommandLine (void)
925 {
926 }
927 
clear(void)928 void CommandLine::clear (void)
929 {
930 	m_cmdLine.clear();
931 	m_logFlags = 0;
932 }
933 
getCommandLine(void) const934 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
935 {
936 	return m_cmdLine;
937 }
938 
getApplicationName(void) const939 const std::string& CommandLine::getApplicationName(void) const
940 {
941 	return m_appName;
942 }
943 
getInitialCmdLine(void) const944 const std::string& CommandLine::getInitialCmdLine(void) const
945 {
946 	return m_initialCmdLine;
947 }
948 
registerExtendedOptions(de::cmdline::Parser & parser)949 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
950 {
951 	DE_UNREF(parser);
952 }
953 
954 /*--------------------------------------------------------------------*//*!
955  * \brief Parse command line from standard argc, argv pair.
956  * \note parse() must be called exactly once.
957  * \param argc Number of arguments
958  * \param argv Command line arguments
959  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)960 bool CommandLine::parse (int argc, const char* const* argv)
961 {
962 	DebugOutStreambuf	sbuf;
963 	std::ostream		debugOut	(&sbuf);
964 	de::cmdline::Parser	parser;
965 
966 	opt::registerOptions(parser);
967 	opt::registerLegacyOptions(parser);
968 	registerExtendedOptions(parser);
969 
970 	clear();
971 
972 	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
973 	{
974 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
975 		parser.help(debugOut);
976 
977 		// We need to save this to avoid exiting with error later, and before the clear() call that wipes its value.
978 		m_hadHelpSpecified = m_cmdLine.helpSpecified();
979 
980 		clear();
981 		return false;
982 	}
983 
984 	if (!m_cmdLine.getOption<opt::LogImages>())
985 		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
986 
987 	if (!m_cmdLine.getOption<opt::LogShaderSources>())
988 		m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
989 
990 	if (!m_cmdLine.getOption<opt::LogFlush>())
991 		m_logFlags |= QP_TEST_LOG_NO_FLUSH;
992 
993 	if (m_cmdLine.getOption<opt::LogCompact>())
994 		m_logFlags |= QP_TEST_LOG_COMPACT;
995 
996 	if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
997 		m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
998 
999 	if (m_cmdLine.getOption<opt::SubProcess>())
1000 		m_logFlags |= QP_TEST_LOG_NO_INITIAL_OUTPUT;
1001 
1002 	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
1003 		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
1004 		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
1005 		(m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
1006 		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
1007 	{
1008 		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
1009 		clear();
1010 		return false;
1011 	}
1012 
1013 	if (m_cmdLine.getArgs().size() > 0)
1014 	{
1015 		debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
1016 
1017 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
1018 		parser.help(debugOut);
1019 
1020 		clear();
1021 		return false;
1022 	}
1023 
1024 	return true;
1025 }
1026 
1027 /*--------------------------------------------------------------------*//*!
1028  * \brief Parse command line from string.
1029  * \note parse() must be called exactly once.
1030  * \param cmdLine Full command line string.
1031  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)1032 bool CommandLine::parse (const std::string& cmdLine)
1033 {
1034 	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
1035 	if (!parsedCmdLine)
1036 		throw std::bad_alloc();
1037 
1038 	bool isOk = false;
1039 	try
1040 	{
1041 		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
1042 	}
1043 	catch (...)
1044 	{
1045 		deCommandLine_destroy(parsedCmdLine);
1046 		throw;
1047 	}
1048 
1049 	deCommandLine_destroy(parsedCmdLine);
1050 	return isOk;
1051 }
1052 
getLogFileName(void) const1053 const char*				CommandLine::getLogFileName					(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();					}
getLogFlags(void) const1054 deUint32				CommandLine::getLogFlags					(void) const	{ return m_logFlags;														}
getRunMode(void) const1055 RunMode					CommandLine::getRunMode						(void) const	{ return m_cmdLine.getOption<opt::RunMode>();								}
getCaseListExportFile(void) const1056 const char*				CommandLine::getCaseListExportFile			(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();			}
getVisibility(void) const1057 WindowVisibility		CommandLine::getVisibility					(void) const	{ return m_cmdLine.getOption<opt::Visibility>();							}
isWatchDogEnabled(void) const1058 bool					CommandLine::isWatchDogEnabled				(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();								}
isCrashHandlingEnabled(void) const1059 bool					CommandLine::isCrashHandlingEnabled			(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();							}
getBaseSeed(void) const1060 int						CommandLine::getBaseSeed					(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();								}
getTestIterationCount(void) const1061 int						CommandLine::getTestIterationCount			(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();					}
getSurfaceWidth(void) const1062 int						CommandLine::getSurfaceWidth				(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();							}
getSurfaceHeight(void) const1063 int						CommandLine::getSurfaceHeight				(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();							}
getSurfaceType(void) const1064 SurfaceType				CommandLine::getSurfaceType					(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();							}
getScreenRotation(void) const1065 ScreenRotation			CommandLine::getScreenRotation				(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();						}
getGLConfigId(void) const1066 int						CommandLine::getGLConfigId					(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();							}
getCLPlatformId(void) const1067 int						CommandLine::getCLPlatformId				(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();							}
getCLDeviceIds(void) const1068 const std::vector<int>&	CommandLine::getCLDeviceIds					(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();							}
getVKDeviceId(void) const1069 int						CommandLine::getVKDeviceId					(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();							}
getVKDeviceGroupId(void) const1070 int						CommandLine::getVKDeviceGroupId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();						}
isValidationEnabled(void) const1071 bool					CommandLine::isValidationEnabled			(void) const	{ return m_cmdLine.getOption<opt::Validation>();							}
printValidationErrors(void) const1072 bool					CommandLine::printValidationErrors			(void) const	{ return m_cmdLine.getOption<opt::PrintValidationErrors>();					}
isLogDecompiledSpirvEnabled(void) const1073 bool					CommandLine::isLogDecompiledSpirvEnabled	(void) const	{ return m_cmdLine.getOption<opt::LogDecompiledSpirv>();					}
isOutOfMemoryTestEnabled(void) const1074 bool					CommandLine::isOutOfMemoryTestEnabled		(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();								}
isShadercacheEnabled(void) const1075 bool					CommandLine::isShadercacheEnabled			(void) const	{ return m_cmdLine.getOption<opt::ShaderCache>();							}
getShaderCacheFilename(void) const1076 const char*				CommandLine::getShaderCacheFilename			(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();			}
isShaderCacheTruncateEnabled(void) const1077 bool					CommandLine::isShaderCacheTruncateEnabled	(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheTruncate>();					}
isShaderCacheIPCEnabled(void) const1078 bool					CommandLine::isShaderCacheIPCEnabled		(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheIPC>();						}
getOptimizationRecipe(void) const1079 int						CommandLine::getOptimizationRecipe			(void) const	{ return m_cmdLine.getOption<opt::Optimization>();							}
isSpirvOptimizationEnabled(void) const1080 bool					CommandLine::isSpirvOptimizationEnabled		(void) const	{ return m_cmdLine.getOption<opt::OptimizeSpirv>();							}
isRenderDocEnabled(void) const1081 bool					CommandLine::isRenderDocEnabled				(void) const	{ return m_cmdLine.getOption<opt::RenderDoc>();								}
getWaiverFileName(void) const1082 const char*				CommandLine::getWaiverFileName				(void) const	{ return m_cmdLine.getOption<opt::WaiverFile>().c_str();					}
getCaseFraction(void) const1083 const std::vector<int>&	CommandLine::getCaseFraction				(void) const	{ return m_cmdLine.getOption<opt::CaseFraction>();							}
getCaseFractionMandatoryTests(void) const1084 const char*				CommandLine::getCaseFractionMandatoryTests	(void) const	{ return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str();	}
getArchiveDir(void) const1085 const char*				CommandLine::getArchiveDir					(void) const	{ return m_cmdLine.getOption<opt::ArchiveDir>().c_str();					}
getRunnerType(void) const1086 tcu::TestRunnerType		CommandLine::getRunnerType					(void) const	{ return m_cmdLine.getOption<opt::RunnerType>();							}
isTerminateOnFailEnabled(void) const1087 bool					CommandLine::isTerminateOnFailEnabled		(void) const	{ return m_cmdLine.getOption<opt::TerminateOnFail>();						}
isSubProcess(void) const1088 bool					CommandLine::isSubProcess					(void) const	{ return m_cmdLine.getOption<opt::SubProcess>();							}
getSubprocessTestCount(void) const1089 int						CommandLine::getSubprocessTestCount			(void) const	{ return m_cmdLine.getOption<opt::SubprocessTestCount>();					}
getCommandPoolMinSize(void) const1090 int						CommandLine::getCommandPoolMinSize			(void) const	{ return m_cmdLine.getOption<opt::CommandPoolMinSize>();					}
getCommandBufferMinSize(void) const1091 int						CommandLine::getCommandBufferMinSize		(void) const	{ return m_cmdLine.getOption<opt::CommandBufferMinSize>();					}
getCommandDefaultSize(void) const1092 int						CommandLine::getCommandDefaultSize			(void) const	{ return m_cmdLine.getOption<opt::CommandDefaultSize>();					}
getPipelineDefaultSize(void) const1093 int						CommandLine::getPipelineDefaultSize			(void) const	{ return m_cmdLine.getOption<opt::PipelineDefaultSize>();					}
1094 
getGLContextType(void) const1095 const char* CommandLine::getGLContextType (void) const
1096 {
1097 	if (m_cmdLine.hasOption<opt::GLContextType>())
1098 		return m_cmdLine.getOption<opt::GLContextType>().c_str();
1099 	else
1100 		return DE_NULL;
1101 }
getGLConfigName(void) const1102 const char* CommandLine::getGLConfigName (void) const
1103 {
1104 	if (m_cmdLine.hasOption<opt::GLConfigName>())
1105 		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
1106 	else
1107 		return DE_NULL;
1108 }
1109 
getGLContextFlags(void) const1110 const char* CommandLine::getGLContextFlags (void) const
1111 {
1112 	if (m_cmdLine.hasOption<opt::GLContextFlags>())
1113 		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
1114 	else
1115 		return DE_NULL;
1116 }
1117 
getCLBuildOptions(void) const1118 const char* CommandLine::getCLBuildOptions (void) const
1119 {
1120 	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
1121 		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
1122 	else
1123 		return DE_NULL;
1124 }
1125 
getEGLDisplayType(void) const1126 const char* CommandLine::getEGLDisplayType (void) const
1127 {
1128 	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
1129 		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
1130 	else
1131 		return DE_NULL;
1132 }
1133 
getEGLWindowType(void) const1134 const char* CommandLine::getEGLWindowType (void) const
1135 {
1136 	if (m_cmdLine.hasOption<opt::EGLWindowType>())
1137 		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
1138 	else
1139 		return DE_NULL;
1140 }
1141 
getEGLPixmapType(void) const1142 const char* CommandLine::getEGLPixmapType (void) const
1143 {
1144 	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
1145 		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
1146 	else
1147 		return DE_NULL;
1148 }
1149 
getSubprocessConfigFile(void) const1150 const char* CommandLine::getSubprocessConfigFile (void) const
1151 {
1152 	if (m_cmdLine.hasOption<opt::SubprocessConfigFile>())
1153 		return m_cmdLine.getOption<opt::SubprocessConfigFile>().c_str();
1154 	else
1155 		return DE_NULL;
1156 }
1157 
1158 
getServerAddress(void) const1159 const char* CommandLine::getServerAddress (void) const
1160 {
1161 	if (m_cmdLine.hasOption<opt::ServerAddress>())
1162 		return m_cmdLine.getOption<opt::ServerAddress>().c_str();
1163 	else
1164 		return DE_NULL;
1165 }
1166 
getPipelineCompilerPath(void) const1167 const char* CommandLine::getPipelineCompilerPath(void) const
1168 {
1169 	if (m_cmdLine.hasOption<opt::PipelineCompilerPath>())
1170 		return m_cmdLine.getOption<opt::PipelineCompilerPath>().c_str();
1171 	else
1172 		return DE_NULL;
1173 }
1174 
getPipelineCompilerDataDir(void) const1175 const char* CommandLine::getPipelineCompilerDataDir(void) const
1176 {
1177 	if (m_cmdLine.hasOption<opt::PipelineCompilerDataDir>())
1178 		return m_cmdLine.getOption<opt::PipelineCompilerDataDir>().c_str();
1179 	else
1180 		return DE_NULL;
1181 }
1182 
getPipelineCompilerArgs(void) const1183 const char* CommandLine::getPipelineCompilerArgs(void) const
1184 {
1185 	if (m_cmdLine.hasOption<opt::PipelineCompilerArgs>())
1186 		return m_cmdLine.getOption<opt::PipelineCompilerArgs>().c_str();
1187 	else
1188 		return DE_NULL;
1189 }
1190 
getPipelineCompilerOutputFile(void) const1191 const char* CommandLine::getPipelineCompilerOutputFile(void) const
1192 {
1193 	if (m_cmdLine.hasOption<opt::PipelineCompilerOutputFile>())
1194 		return m_cmdLine.getOption<opt::PipelineCompilerOutputFile>().c_str();
1195 	else
1196 		return DE_NULL;
1197 }
1198 
getPipelineCompilerLogFile(void) const1199 const char* CommandLine::getPipelineCompilerLogFile(void) const
1200 {
1201 	if (m_cmdLine.hasOption<opt::PipelineCompilerLogFile>())
1202 		return m_cmdLine.getOption<opt::PipelineCompilerLogFile>().c_str();
1203 	else
1204 		return DE_NULL;
1205 }
1206 
getPipelineCompilerFilePrefix(void) const1207 const char* CommandLine::getPipelineCompilerFilePrefix(void) const
1208 {
1209 	if (m_cmdLine.hasOption<opt::PipelineCompilerFilePrefix>())
1210 		return m_cmdLine.getOption<opt::PipelineCompilerFilePrefix>().c_str();
1211 	else
1212 		return DE_NULL;
1213 }
1214 
getVkLibraryPath(void) const1215 const char* CommandLine::getVkLibraryPath(void) const
1216 {
1217 	if (m_cmdLine.hasOption<opt::VkLibraryPath>())
1218 		return (m_cmdLine.getOption<opt::VkLibraryPath>() != "") ? m_cmdLine.getOption<opt::VkLibraryPath>().c_str() : DE_NULL;
1219 	else
1220 		return DE_NULL;
1221 }
1222 
getAppParamsInputFilePath(void) const1223 const char* CommandLine::getAppParamsInputFilePath(void) const
1224 {
1225 	if (m_cmdLine.hasOption<opt::ApplicationParametersInputFile>())
1226 		return m_cmdLine.getOption<opt::ApplicationParametersInputFile>().c_str();
1227 	else
1228 		return DE_NULL;
1229 }
1230 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)1231 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
1232 {
1233 	const CaseTreeNode* node = findNode(root, groupPath);
1234 	return node && node->hasChildren();
1235 }
1236 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)1237 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1238 {
1239 	const CaseTreeNode* node = findNode(root, casePath);
1240 	return node && !node->hasChildren();
1241 }
1242 
createCaseListFilter(const tcu::Archive & archive) const1243 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1244 {
1245 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1246 }
1247 
checkTestGroupName(const char * groupName) const1248 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1249 {
1250 	bool result = false;
1251 	if (m_casePaths)
1252 		result = m_casePaths->matches(groupName, true);
1253 	else if (m_caseTree)
1254 		result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1255 	else
1256 		return true;
1257 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1258 		result = m_caseFractionMandatoryTests->matches(groupName, true);
1259 	return result;
1260 }
1261 
checkTestCaseName(const char * caseName) const1262 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1263 {
1264 	bool result = false;
1265 	if (m_casePaths)
1266 		result = m_casePaths->matches(caseName, false);
1267 	else if (m_caseTree)
1268 		result = tcu::checkTestCaseName(m_caseTree, caseName);
1269 	else
1270 		return true;
1271 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1272 		result = m_caseFractionMandatoryTests->matches(caseName, false);
1273 	return result;
1274 }
1275 
checkCaseFraction(int i,const std::string & testCaseName) const1276 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1277 {
1278 	return	m_caseFraction.size() != 2 ||
1279 		((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1280 		(m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1281 }
1282 
CaseListFilter(void)1283 CaseListFilter::CaseListFilter (void)
1284 	: m_caseTree	(DE_NULL)
1285 	, m_runnerType	(tcu::RUNNERTYPE_ANY)
1286 {
1287 }
1288 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1289 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1290 	: m_caseTree	(DE_NULL)
1291 {
1292 	if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1293 	{
1294 		m_runnerType = RUNNERTYPE_AMBER;
1295 	}
1296 	else
1297 	{
1298 		m_runnerType = cmdLine.getOption<opt::RunnerType>();
1299 	}
1300 
1301 	if (cmdLine.hasOption<opt::CaseList>())
1302 	{
1303 		std::istringstream str(cmdLine.getOption<opt::CaseList>());
1304 
1305 		m_caseTree = parseCaseList(str, archive);
1306 	}
1307 	else if (cmdLine.hasOption<opt::CaseListFile>())
1308 	{
1309 		std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1310 		std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1311 
1312 		if (!in.is_open() || !in.good())
1313 			throw Exception("Failed to open case list file '" + caseListFile + "'");
1314 
1315 		m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1316 	}
1317 	else if (cmdLine.hasOption<opt::CaseListResource>())
1318 	{
1319 		// \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1320 		//						   istream adaptor for tcu::Resource.
1321 		de::UniquePtr<Resource>	caseListResource	(archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1322 		const int				bufferSize			= caseListResource->getSize();
1323 		std::vector<char>		buffer				((size_t)bufferSize);
1324 
1325 		if (buffer.empty())
1326 			throw Exception("Empty case list resource");
1327 
1328 		caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1329 
1330 		{
1331 			std::istringstream	in	(std::string(&buffer[0], (size_t)bufferSize));
1332 
1333 			m_caseTree = parseCaseList(in, archive);
1334 		}
1335 	}
1336 	else if (cmdLine.getOption<opt::StdinCaseList>())
1337 	{
1338 		m_caseTree = parseCaseList(std::cin, archive);
1339 	}
1340 	else if (cmdLine.hasOption<opt::CasePath>())
1341 		m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1342 
1343 	if (!cmdLine.getOption<opt::SubProcess>())
1344 		m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1345 
1346 	if (m_caseFraction.size() == 2 &&
1347 		(m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1348 		throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1349 
1350 	if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1351 		throw Exception("Invalid case fraction. Must have two components.");
1352 
1353 	if (m_caseFraction.size() == 2)
1354 	{
1355 		std::string					caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1356 
1357 		if (!caseFractionMandatoryTestsFilename.empty())
1358 		{
1359 			std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1360 			if (!fileStream.is_open() || !fileStream.good())
1361 				throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1362 
1363 			std::vector<std::string>	cfPaths;
1364 			std::string					line;
1365 
1366 			while (std::getline(fileStream, line))
1367 			{
1368 				line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1369 				cfPaths.push_back(line);
1370 			}
1371 			if (!cfPaths.empty())
1372 			{
1373 				m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1374 				if (m_caseTree != DE_NULL)
1375 				{
1376 					fileStream.clear();
1377 					fileStream.seekg(0, fileStream.beg);
1378 					std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
1379 					parseCaseList(m_caseTree, fileStream, false, hashCollisionDetectionMap);
1380 				}
1381 			}
1382 		}
1383 	}
1384 }
1385 
~CaseListFilter(void)1386 CaseListFilter::~CaseListFilter (void)
1387 {
1388 	delete m_caseTree;
1389 }
1390 
1391 } // tcu
1392