• 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(RenderDoc,					bool);
103 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction,				std::vector<int>);
104 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests,	std::string);
105 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile,					std::string);
106 DE_DECLARE_COMMAND_LINE_OPT(RunnerType,					tcu::TestRunnerType);
107 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail,			bool);
108 
parseIntList(const char * src,std::vector<int> * dst)109 static void parseIntList (const char* src, std::vector<int>* dst)
110 {
111 	std::istringstream	str	(src);
112 	std::string			val;
113 
114 	while (std::getline(str, val, ','))
115 	{
116 		int intVal = 0;
117 		de::cmdline::parseType(val.c_str(), &intVal);
118 		dst->push_back(intVal);
119 	}
120 }
121 
registerOptions(de::cmdline::Parser & parser)122 void registerOptions (de::cmdline::Parser& parser)
123 {
124 	using de::cmdline::Option;
125 	using de::cmdline::NamedValue;
126 
127 	static const NamedValue<bool> s_enableNames[] =
128 	{
129 		{ "enable",		true	},
130 		{ "disable",	false	}
131 	};
132 	static const NamedValue<tcu::RunMode> s_runModes[] =
133 	{
134 		{ "execute",		RUNMODE_EXECUTE				  },
135 		{ "xml-caselist",	RUNMODE_DUMP_XML_CASELIST	  },
136 		{ "txt-caselist",	RUNMODE_DUMP_TEXT_CASELIST	  },
137 		{ "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST  },
138 		{ "amber-verify",   RUNMODE_VERIFY_AMBER_COHERENCY}
139 	};
140 	static const NamedValue<WindowVisibility> s_visibilites[] =
141 	{
142 		{ "windowed",		WINDOWVISIBILITY_WINDOWED	},
143 		{ "fullscreen",		WINDOWVISIBILITY_FULLSCREEN	},
144 		{ "hidden",			WINDOWVISIBILITY_HIDDEN		}
145 	};
146 	static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
147 	{
148 		{ "window",			SURFACETYPE_WINDOW				},
149 		{ "pixmap",			SURFACETYPE_OFFSCREEN_NATIVE	},
150 		{ "pbuffer",		SURFACETYPE_OFFSCREEN_GENERIC	},
151 		{ "fbo",			SURFACETYPE_FBO					}
152 	};
153 	static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
154 	{
155 		{ "unspecified",	SCREENROTATION_UNSPECIFIED	},
156 		{ "0",				SCREENROTATION_0			},
157 		{ "90",				SCREENROTATION_90			},
158 		{ "180",			SCREENROTATION_180			},
159 		{ "270",			SCREENROTATION_270			}
160 	};
161 	static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
162 	{
163 		{ "any",	tcu::RUNNERTYPE_ANY		},
164 		{ "none",	tcu::RUNNERTYPE_NONE	},
165 		{ "amber",	tcu::RUNNERTYPE_AMBER	},
166 	};
167 
168 	parser
169 		<< Option<CasePath>						("n",		"deqp-case",								"Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
170 		<< Option<CaseList>						(DE_NULL,	"deqp-caselist",							"Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
171 		<< Option<CaseListFile>					(DE_NULL,	"deqp-caselist-file",						"Read case list (in trie format) from given file")
172 		<< Option<CaseListResource>				(DE_NULL,	"deqp-caselist-resource",					"Read case list (in trie format) from given file located application's assets")
173 		<< Option<StdinCaseList>				(DE_NULL,	"deqp-stdin-caselist",						"Read case list (in trie format) from stdin")
174 		<< Option<LogFilename>					(DE_NULL,	"deqp-log-filename",						"Write test results to given file",					"TestResults.qpa")
175 		<< Option<RunMode>						(DE_NULL,	"deqp-runmode",								"Execute tests, write list of test cases into a file, or verify amber capability coherency",
176 																																							s_runModes,			"execute")
177 		<< Option<ExportFilenamePattern>		(DE_NULL,	"deqp-caselist-export-file",				"Set the target file name pattern for caselist export",					"${packageName}-cases.${typeExtension}")
178 		<< Option<WatchDog>						(DE_NULL,	"deqp-watchdog",							"Enable test watchdog",								s_enableNames,		"disable")
179 		<< Option<CrashHandler>					(DE_NULL,	"deqp-crashhandler",						"Enable crash handling",							s_enableNames,		"disable")
180 		<< Option<BaseSeed>						(DE_NULL,	"deqp-base-seed",							"Base seed for test cases that use randomization",						"0")
181 		<< Option<TestIterationCount>			(DE_NULL,	"deqp-test-iteration-count",				"Iteration count for cases that support variable number of iterations",	"0")
182 		<< Option<Visibility>					(DE_NULL,	"deqp-visibility",							"Default test window visibility",					s_visibilites,		"windowed")
183 		<< Option<SurfaceWidth>					(DE_NULL,	"deqp-surface-width",						"Use given surface width if possible",									"-1")
184 		<< Option<SurfaceHeight>				(DE_NULL,	"deqp-surface-height",						"Use given surface height if possible",									"-1")
185 		<< Option<SurfaceType>					(DE_NULL,	"deqp-surface-type",						"Use given surface type",							s_surfaceTypes,		"window")
186 		<< Option<ScreenRotation>				(DE_NULL,	"deqp-screen-rotation",						"Screen rotation for platforms that support it",	s_screenRotations,	"0")
187 		<< Option<GLContextType>				(DE_NULL,	"deqp-gl-context-type",						"OpenGL context type for platforms that support multiple")
188 		<< Option<GLConfigID>					(DE_NULL,	"deqp-gl-config-id",						"OpenGL (ES) render config ID (EGL config id on EGL platforms)",		"-1")
189 		<< Option<GLConfigName>					(DE_NULL,	"deqp-gl-config-name",						"Symbolic OpenGL (ES) render config name")
190 		<< Option<GLContextFlags>				(DE_NULL,	"deqp-gl-context-flags",					"OpenGL context flags (comma-separated, supports debug and robust)")
191 		<< Option<CLPlatformID>					(DE_NULL,	"deqp-cl-platform-id",						"Execute tests on given OpenCL platform (IDs start from 1)",			"1")
192 		<< Option<CLDeviceIDs>					(DE_NULL,	"deqp-cl-device-ids",						"Execute tests on given CL devices (comma-separated, IDs start from 1)",	parseIntList,	"")
193 		<< Option<CLBuildOptions>				(DE_NULL,	"deqp-cl-build-options",					"Extra build options for OpenCL compiler")
194 		<< Option<EGLDisplayType>				(DE_NULL,	"deqp-egl-display-type",					"EGL native display type")
195 		<< Option<EGLWindowType>				(DE_NULL,	"deqp-egl-window-type",						"EGL native window type")
196 		<< Option<EGLPixmapType>				(DE_NULL,	"deqp-egl-pixmap-type",						"EGL native pixmap type")
197 		<< Option<VKDeviceID>					(DE_NULL,	"deqp-vk-device-id",						"Vulkan device ID (IDs start from 1)",									"1")
198 		<< Option<VKDeviceGroupID>				(DE_NULL,	"deqp-vk-device-group-id",					"Vulkan device Group ID (IDs start from 1)",							"1")
199 		<< Option<LogImages>					(DE_NULL,	"deqp-log-images",							"Enable or disable logging of result images",		s_enableNames,		"enable")
200 		<< Option<LogShaderSources>				(DE_NULL,	"deqp-log-shader-sources",					"Enable or disable logging of shader sources",		s_enableNames,		"enable")
201 		<< Option<LogDecompiledSpirv>			(DE_NULL,	"deqp-log-decompiled-spirv",				"Enable or disable logging of decompiled spir-v",	s_enableNames,		"enable")
202 		<< Option<LogEmptyLoginfo>				(DE_NULL,	"deqp-log-empty-loginfo",					"Logging of empty shader compile/link log info",	s_enableNames,		"enable")
203 		<< Option<TestOOM>						(DE_NULL,	"deqp-test-oom",							"Run tests that exhaust memory on purpose",			s_enableNames,		TEST_OOM_DEFAULT)
204 		<< Option<ArchiveDir>					(DE_NULL,	"deqp-archive-dir",							"Path to test resource files",											".")
205 		<< Option<LogFlush>						(DE_NULL,	"deqp-log-flush",							"Enable or disable log file fflush",				s_enableNames,		"enable")
206 		<< Option<Validation>					(DE_NULL,	"deqp-validation",							"Enable or disable test case validation",			s_enableNames,		"disable")
207 		<< Option<PrintValidationErrors>		(DE_NULL,	"deqp-print-validation-errors",				"Print validation errors to standard error")
208 		<< Option<Optimization>					(DE_NULL,	"deqp-optimization-recipe",					"Shader optimization recipe (0=disabled, 1=performance, 2=size)",		"0")
209 		<< Option<OptimizeSpirv>				(DE_NULL,	"deqp-optimize-spirv",						"Apply optimization to spir-v shaders as well",		s_enableNames,		"disable")
210 		<< Option<ShaderCache>					(DE_NULL,	"deqp-shadercache",							"Enable or disable shader cache",					s_enableNames,		"enable")
211 		<< Option<ShaderCacheFilename>			(DE_NULL,	"deqp-shadercache-filename",				"Write shader cache to given file",										"shadercache.bin")
212 		<< Option<ShaderCacheTruncate>			(DE_NULL,	"deqp-shadercache-truncate",				"Truncate shader cache before running tests",		s_enableNames,		"enable")
213 		<< Option<RenderDoc>					(DE_NULL,	"deqp-renderdoc",							"Enable RenderDoc frame markers",					s_enableNames,		"disable")
214 		<< Option<CaseFraction>					(DE_NULL,	"deqp-fraction",							"Run a fraction of the test cases (e.g. N,M means run group%M==N)",	parseIntList,	"")
215 		<< Option<CaseFractionMandatoryTests>	(DE_NULL,	"deqp-fraction-mandatory-caselist-file",	"Case list file that must be run for each fraction",					"")
216 		<< Option<WaiverFile>					(DE_NULL,	"deqp-waiver-file",							"Read waived tests from given file",									"")
217 		<< Option<RunnerType>					(DE_NULL,	"deqp-runner-type",							"Filter test cases based on runner",				s_runnerTypes,		"any")
218 		<< Option<TerminateOnFail>				(DE_NULL,	"deqp-terminate-on-fail",					"Terminate the run on first failure",				s_enableNames,		"disable");
219 }
220 
registerLegacyOptions(de::cmdline::Parser & parser)221 void registerLegacyOptions (de::cmdline::Parser& parser)
222 {
223 	using de::cmdline::Option;
224 
225 	parser
226 		<< Option<GLConfigID>			(DE_NULL,	"deqp-egl-config-id",			"Legacy name for --deqp-gl-config-id",	"-1")
227 		<< Option<GLConfigName>			(DE_NULL,	"deqp-egl-config-name",			"Legacy name for --deqp-gl-config-name");
228 }
229 
230 } // opt
231 
232 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
233 class DebugOutStreambuf : public std::streambuf
234 {
235 public:
236 						DebugOutStreambuf	(void);
237 						~DebugOutStreambuf	(void);
238 
239 protected:
240 	std::streamsize		xsputn				(const char* s, std::streamsize count);
241 	int					overflow			(int ch = -1);
242 
243 private:
244 	void				flushLine			(void);
245 
246 	std::ostringstream	m_curLine;
247 };
248 
DebugOutStreambuf(void)249 DebugOutStreambuf::DebugOutStreambuf (void)
250 {
251 }
252 
~DebugOutStreambuf(void)253 DebugOutStreambuf::~DebugOutStreambuf (void)
254 {
255 	if (m_curLine.tellp() != std::streampos(0))
256 		flushLine();
257 }
258 
xsputn(const char * s,std::streamsize count)259 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
260 {
261 	for (std::streamsize pos = 0; pos < count; pos++)
262 	{
263 		m_curLine.put(s[pos]);
264 
265 		if (s[pos] == '\n')
266 			flushLine();
267 	}
268 
269 	return count;
270 }
271 
overflow(int ch)272 int DebugOutStreambuf::overflow (int ch)
273 {
274 	if (ch == -1)
275 		return -1;
276 	else
277 	{
278 		DE_ASSERT((ch & 0xff) == ch);
279 		const char chVal = (char)(deUint8)(ch & 0xff);
280 		return xsputn(&chVal, 1) == 1 ? ch : -1;
281 	}
282 }
283 
flushLine(void)284 void DebugOutStreambuf::flushLine (void)
285 {
286 	qpPrint(m_curLine.str().c_str());
287 	m_curLine.str("");
288 }
289 
290 class CaseTreeNode
291 {
292 public:
CaseTreeNode(const std::string & name)293 										CaseTreeNode		(const std::string& name) : m_name(name) {}
294 										~CaseTreeNode		(void);
295 
getName(void) const296 	const std::string&					getName				(void) const { return m_name;				}
hasChildren(void) const297 	bool								hasChildren			(void) const { return !m_children.empty();	}
298 
299 	bool								hasChild			(const std::string& name) const;
300 	const CaseTreeNode*					getChild			(const std::string& name) const;
301 	CaseTreeNode*						getChild			(const std::string& name);
302 
addChild(CaseTreeNode * child)303 	void								addChild			(CaseTreeNode* child) { m_children.push_back(child); }
304 
305 private:
306 										CaseTreeNode		(const CaseTreeNode&);
307 	CaseTreeNode&						operator=			(const CaseTreeNode&);
308 
309 	enum { NOT_FOUND = -1 };
310 
311 	// \todo [2014-10-30 pyry] Speed up with hash / sorting
312 	int									findChildNdx		(const std::string& name) const;
313 
314 	std::string							m_name;
315 	std::vector<CaseTreeNode*>			m_children;
316 };
317 
~CaseTreeNode(void)318 CaseTreeNode::~CaseTreeNode (void)
319 {
320 	for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
321 		delete *i;
322 }
323 
findChildNdx(const std::string & name) const324 int CaseTreeNode::findChildNdx (const std::string& name) const
325 {
326 	for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
327 	{
328 		if (m_children[ndx]->getName() == name)
329 			return ndx;
330 	}
331 	return NOT_FOUND;
332 }
333 
hasChild(const std::string & name) const334 inline bool CaseTreeNode::hasChild (const std::string& name) const
335 {
336 	return findChildNdx(name) != NOT_FOUND;
337 }
338 
getChild(const std::string & name) const339 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
340 {
341 	const int ndx = findChildNdx(name);
342 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
343 }
344 
getChild(const std::string & name)345 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
346 {
347 	const int ndx = findChildNdx(name);
348 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
349 }
350 
getCurrentComponentLen(const char * path)351 static int getCurrentComponentLen (const char* path)
352 {
353 	int ndx = 0;
354 	for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
355 	return ndx;
356 }
357 
findNode(const CaseTreeNode * root,const char * path)358 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
359 {
360 	const CaseTreeNode*	curNode		= root;
361 	const char*			curPath		= path;
362 	int					curLen		= getCurrentComponentLen(curPath);
363 
364 	for (;;)
365 	{
366 		curNode = curNode->getChild(std::string(curPath, curPath+curLen));
367 
368 		if (!curNode)
369 			break;
370 
371 		curPath	+= curLen;
372 
373 		if (curPath[0] == 0)
374 			break;
375 		else
376 		{
377 			DE_ASSERT(curPath[0] == '.');
378 			curPath		+= 1;
379 			curLen		 = getCurrentComponentLen(curPath);
380 		}
381 	}
382 
383 	return curNode;
384 }
385 
parseCaseTrie(CaseTreeNode * root,std::istream & in)386 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
387 {
388 	vector<CaseTreeNode*>	nodeStack;
389 	string					curName;
390 	bool					expectNode		= true;
391 
392 	if (in.get() != '{')
393 		throw std::invalid_argument("Malformed case trie");
394 
395 	nodeStack.push_back(root);
396 
397 	while (!nodeStack.empty())
398 	{
399 		const int	curChr	= in.get();
400 
401 		if (curChr == std::char_traits<char>::eof() || curChr == 0)
402 			throw std::invalid_argument("Unterminated case tree");
403 
404 		if (curChr == '{' || curChr == ',' || curChr == '}')
405 		{
406 			if (!curName.empty() && expectNode)
407 			{
408 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
409 
410 				try
411 				{
412 					nodeStack.back()->addChild(newChild);
413 				}
414 				catch (...)
415 				{
416 					delete newChild;
417 					throw;
418 				}
419 
420 				if (curChr == '{')
421 					nodeStack.push_back(newChild);
422 
423 				curName.clear();
424 			}
425 			else if (curName.empty() == expectNode)
426 				throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
427 
428 			if (curChr == '}')
429 			{
430 				expectNode = false;
431 				nodeStack.pop_back();
432 
433 				// consume trailing new line
434 				if (nodeStack.empty())
435 				{
436 					if (in.peek() == '\r')
437 					  in.get();
438 					if (in.peek() == '\n')
439 					  in.get();
440 				}
441 			}
442 			else
443 				expectNode = true;
444 		}
445 		else if (isValidTestCaseNameChar((char)curChr))
446 			curName += (char)curChr;
447 		else
448 			throw std::invalid_argument("Illegal character in node name");
449 	}
450 }
451 
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates)452 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates)
453 {
454 	// \note Algorithm assumes that cases are sorted by groups, but will
455 	//		 function fine, albeit more slowly, if that is not the case.
456 	int		stackPos	= 0;
457 	string	curName;
458 
459 	for (;;)
460 	{
461 		const int curChr = in.get();
462 
463 		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
464 		{
465 			if (curName.empty())
466 				throw std::invalid_argument("Empty test case name");
467 
468 			if (!nodeStack[stackPos]->hasChild(curName))
469 			{
470 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
471 
472 				try
473 				{
474 					nodeStack[stackPos]->addChild(newChild);
475 				}
476 				catch (...)
477 				{
478 					delete newChild;
479 					throw;
480 				}
481 			}
482 			else if (reportDuplicates)
483 				throw std::invalid_argument("Duplicate test case");
484 
485 			curName.clear();
486 			stackPos = 0;
487 
488 			if (curChr == '\r' && in.peek() == '\n')
489 				in.get();
490 
491 			{
492 				const int nextChr = in.peek();
493 
494 				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
495 					break;
496 			}
497 		}
498 		else if (curChr == '.')
499 		{
500 			if (curName.empty())
501 				throw std::invalid_argument("Empty test group name");
502 
503 			if ((int)nodeStack.size() <= stackPos+1)
504 				nodeStack.resize(nodeStack.size()*2, DE_NULL);
505 
506 			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
507 			{
508 				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
509 
510 				if (!curGroup)
511 				{
512 					curGroup = new CaseTreeNode(curName);
513 
514 					try
515 					{
516 						nodeStack[stackPos]->addChild(curGroup);
517 					}
518 					catch (...)
519 					{
520 						delete curGroup;
521 						throw;
522 					}
523 				}
524 
525 				nodeStack[stackPos+1] = curGroup;
526 
527 				if ((int)nodeStack.size() > stackPos+2)
528 					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
529 			}
530 
531 			DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
532 
533 			curName.clear();
534 			stackPos += 1;
535 		}
536 		else if (isValidTestCaseNameChar((char)curChr))
537 			curName += (char)curChr;
538 		else
539 			throw std::invalid_argument("Illegal character in test case name");
540 	}
541 }
542 
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates)543 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates)
544 {
545 	vector<CaseTreeNode*> nodeStack(8, root);
546 	parseSimpleCaseList(nodeStack, in, reportDuplicates);
547 }
548 
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates)549 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates)
550 {
551 	// read whole file and remove all '\r'
552 	std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
553 	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
554 
555 	vector<CaseTreeNode*>	nodeStack(8, root);
556 	std::stringstream		namesStream(buffer);
557 	std::string				fileName;
558 
559 	while (std::getline(namesStream, fileName))
560 	{
561 		de::FilePath			groupPath		(fileName);
562 		de::UniquePtr<Resource>	groupResource	(archive.getResource(groupPath.normalize().getPath()));
563 		const int				groupBufferSize	(groupResource->getSize());
564 		std::vector<char>		groupBuffer		(static_cast<size_t>(groupBufferSize));
565 
566 		groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
567 		if (groupBuffer.empty())
568 			throw Exception("Empty case list resource");
569 
570 		std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
571 		parseSimpleCaseList(nodeStack, groupIn, reportDuplicates);
572 	}
573 }
574 
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)575 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
576 {
577 	CaseTreeNode* const root = new CaseTreeNode("");
578 	try
579 	{
580 		if (in.peek() == '{')
581 			parseCaseTrie(root, in);
582 		else
583 		{
584 			// if we are reading cases from file determine if we are
585 			// reading group file or plain list of cases; this is done by
586 			// reading single line and checking if it ends with ".txt"
587 			bool readGroupFile = false;
588 			if (path)
589 			{
590 				// read the first line and make sure it doesn't contain '\r'
591 				std::string line;
592 				std::getline(in, line);
593 				line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
594 
595 				const std::string ending = ".txt";
596 				readGroupFile = (line.length() > ending.length()) &&
597 								std::equal(ending.rbegin(), ending.rend(), line.rbegin());
598 
599 				// move to the beginning of the file to parse first line too
600 				in.seekg(0, in.beg);
601 			}
602 
603 			if (readGroupFile)
604 				parseGroupFile(root, in, archive, true);
605 			else
606 				parseCaseList(root, in, true);
607 		}
608 
609 		{
610 			const int curChr = in.get();
611 			if (curChr != std::char_traits<char>::eof() && curChr != 0)
612 				throw std::invalid_argument("Trailing characters at end of case list");
613 		}
614 
615 		return root;
616 	}
617 	catch (...)
618 	{
619 		delete root;
620 		throw;
621 	}
622 }
623 
624 class CasePaths
625 {
626 public:
627 	CasePaths(const string& pathList);
628 	CasePaths(const vector<string>& pathList);
629 	bool					matches(const string& caseName, bool allowPrefix = false) const;
630 
631 private:
632 	const vector<string>	m_casePatterns;
633 };
634 
CasePaths(const string & pathList)635 CasePaths::CasePaths (const string& pathList)
636 	: m_casePatterns(de::splitString(pathList, ','))
637 {
638 }
639 
CasePaths(const vector<string> & pathList)640 CasePaths::CasePaths(const vector<string>& pathList)
641 	: m_casePatterns(pathList)
642 {
643 }
644 
645 // 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)646 bool matchWildcards(string::const_iterator	patternStart,
647 					string::const_iterator	patternEnd,
648 					string::const_iterator	pathStart,
649 					string::const_iterator	pathEnd,
650 					bool					allowPrefix)
651 {
652 	string::const_iterator	pattern	= patternStart;
653 	string::const_iterator	path	= pathStart;
654 
655 	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
656 	{
657 		++pattern;
658 		++path;
659 	}
660 
661 	if (pattern == patternEnd)
662 		return (path == pathEnd);
663 	else if (*pattern == '*')
664 	{
665 		for (; path != pathEnd; ++path)
666 		{
667 			if (matchWildcards(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
668 				return true;
669 		}
670 
671 		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
672 			return true;
673 	}
674 	else if (path == pathEnd && allowPrefix)
675 		return true;
676 
677 	return false;
678 }
679 
680 #if defined(TCU_HIERARCHICAL_CASEPATHS)
681 // Match a list of pattern components to a list of path components. A pattern
682 // component may contain *-wildcards. A pattern component "**" matches zero or
683 // 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)684 static bool patternMatches(vector<string>::const_iterator	patternStart,
685 						   vector<string>::const_iterator	patternEnd,
686 						   vector<string>::const_iterator	pathStart,
687 						   vector<string>::const_iterator	pathEnd,
688 						   bool								allowPrefix)
689 {
690 	vector<string>::const_iterator	pattern	= patternStart;
691 	vector<string>::const_iterator	path	= pathStart;
692 
693 	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
694 		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
695 												path->begin(), path->end(), false)))
696 	{
697 		++pattern;
698 		++path;
699 	}
700 
701 	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
702 		return true;
703 	else if (pattern != patternEnd && *pattern == "**")
704 	{
705 		for (; path != pathEnd; ++path)
706 			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
707 				return true;
708 		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
709 			return true;
710 	}
711 
712 	return false;
713 }
714 #endif
715 
matches(const string & caseName,bool allowPrefix) const716 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
717 {
718 	const vector<string> components = de::splitString(caseName, '.');
719 
720 	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
721 	{
722 #if defined(TCU_HIERARCHICAL_CASEPATHS)
723 		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
724 
725 		if (patternMatches(patternComponents.begin(), patternComponents.end(),
726 						   components.begin(), components.end(), allowPrefix))
727 			return true;
728 #else
729 		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
730 						   caseName.begin(), caseName.end(), allowPrefix))
731 			return true;
732 #endif
733 	}
734 
735 	return false;
736 }
737 
738 /*--------------------------------------------------------------------*//*!
739  * \brief Construct command line
740  * \note CommandLine is not fully initialized until parse() has been called.
741  *//*--------------------------------------------------------------------*/
CommandLine(void)742 CommandLine::CommandLine (void)
743 	: m_logFlags	(0)
744 {
745 }
746 
747 /*--------------------------------------------------------------------*//*!
748  * \brief Construct command line from standard argc, argv pair.
749  *
750  * Calls parse() with given arguments
751  * \param archive application's assets
752  * \param argc Number of arguments
753  * \param argv Command line arguments
754  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)755 CommandLine::CommandLine (int argc, const char* const* argv)
756 	: m_logFlags	(0)
757 {
758 	if (argc > 1)
759 	{
760 		int loop = 1;		// skip application name
761 		while (true)
762 		{
763 			m_initialCmdLine += std::string(argv[loop++]);
764 			if (loop >= argc)
765 				break;
766 			m_initialCmdLine += " ";
767 		}
768 	}
769 
770 	if (!parse(argc, argv))
771 		throw Exception("Failed to parse command line");
772 }
773 
774 /*--------------------------------------------------------------------*//*!
775  * \brief Construct command line from string.
776  *
777  * Calls parse() with given argument.
778  * \param archive application's assets
779  * \param cmdLine Full command line string.
780  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)781 CommandLine::CommandLine (const std::string& cmdLine)
782 	: m_initialCmdLine	(cmdLine)
783 {
784 	if (!parse(cmdLine))
785 		throw Exception("Failed to parse command line");
786 }
787 
~CommandLine(void)788 CommandLine::~CommandLine (void)
789 {
790 }
791 
clear(void)792 void CommandLine::clear (void)
793 {
794 	m_cmdLine.clear();
795 	m_logFlags = 0;
796 }
797 
getCommandLine(void) const798 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
799 {
800 	return m_cmdLine;
801 }
802 
getInitialCmdLine(void) const803 const std::string& CommandLine::getInitialCmdLine(void) const
804 {
805 	return m_initialCmdLine;
806 }
807 
registerExtendedOptions(de::cmdline::Parser & parser)808 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
809 {
810 	DE_UNREF(parser);
811 }
812 
813 /*--------------------------------------------------------------------*//*!
814  * \brief Parse command line from standard argc, argv pair.
815  * \note parse() must be called exactly once.
816  * \param argc Number of arguments
817  * \param argv Command line arguments
818  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)819 bool CommandLine::parse (int argc, const char* const* argv)
820 {
821 	DebugOutStreambuf	sbuf;
822 	std::ostream		debugOut	(&sbuf);
823 	de::cmdline::Parser	parser;
824 
825 	opt::registerOptions(parser);
826 	opt::registerLegacyOptions(parser);
827 	registerExtendedOptions(parser);
828 
829 	clear();
830 
831 	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
832 	{
833 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
834 		parser.help(debugOut);
835 
836 		clear();
837 		return false;
838 	}
839 
840 	if (!m_cmdLine.getOption<opt::LogImages>())
841 		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
842 
843 	if (!m_cmdLine.getOption<opt::LogShaderSources>())
844 		m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
845 
846 	if (!m_cmdLine.getOption<opt::LogFlush>())
847 		m_logFlags |= QP_TEST_LOG_NO_FLUSH;
848 
849 	if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
850 		m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
851 
852 	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
853 		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
854 		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
855 		(m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
856 		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
857 	{
858 		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
859 		clear();
860 		return false;
861 	}
862 
863 	if (m_cmdLine.getArgs().size() > 0)
864 	{
865 		debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
866 
867 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
868 		parser.help(debugOut);
869 
870 		clear();
871 		return false;
872 	}
873 
874 	return true;
875 }
876 
877 /*--------------------------------------------------------------------*//*!
878  * \brief Parse command line from string.
879  * \note parse() must be called exactly once.
880  * \param cmdLine Full command line string.
881  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)882 bool CommandLine::parse (const std::string& cmdLine)
883 {
884 	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
885 	if (!parsedCmdLine)
886 		throw std::bad_alloc();
887 
888 	bool isOk = false;
889 	try
890 	{
891 		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
892 	}
893 	catch (...)
894 	{
895 		deCommandLine_destroy(parsedCmdLine);
896 		throw;
897 	}
898 
899 	deCommandLine_destroy(parsedCmdLine);
900 	return isOk;
901 }
902 
getLogFileName(void) const903 const char*				CommandLine::getLogFileName					(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();					}
getLogFlags(void) const904 deUint32				CommandLine::getLogFlags					(void) const	{ return m_logFlags;														}
getRunMode(void) const905 RunMode					CommandLine::getRunMode						(void) const	{ return m_cmdLine.getOption<opt::RunMode>();								}
getCaseListExportFile(void) const906 const char*				CommandLine::getCaseListExportFile			(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();			}
getVisibility(void) const907 WindowVisibility		CommandLine::getVisibility					(void) const	{ return m_cmdLine.getOption<opt::Visibility>();							}
isWatchDogEnabled(void) const908 bool					CommandLine::isWatchDogEnabled				(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();								}
isCrashHandlingEnabled(void) const909 bool					CommandLine::isCrashHandlingEnabled			(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();							}
getBaseSeed(void) const910 int						CommandLine::getBaseSeed					(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();								}
getTestIterationCount(void) const911 int						CommandLine::getTestIterationCount			(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();					}
getSurfaceWidth(void) const912 int						CommandLine::getSurfaceWidth				(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();							}
getSurfaceHeight(void) const913 int						CommandLine::getSurfaceHeight				(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();							}
getSurfaceType(void) const914 SurfaceType				CommandLine::getSurfaceType					(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();							}
getScreenRotation(void) const915 ScreenRotation			CommandLine::getScreenRotation				(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();						}
getGLConfigId(void) const916 int						CommandLine::getGLConfigId					(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();							}
getCLPlatformId(void) const917 int						CommandLine::getCLPlatformId				(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();							}
getCLDeviceIds(void) const918 const std::vector<int>&	CommandLine::getCLDeviceIds					(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();							}
getVKDeviceId(void) const919 int						CommandLine::getVKDeviceId					(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();							}
getVKDeviceGroupId(void) const920 int						CommandLine::getVKDeviceGroupId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();						}
isValidationEnabled(void) const921 bool					CommandLine::isValidationEnabled			(void) const	{ return m_cmdLine.getOption<opt::Validation>();							}
printValidationErrors(void) const922 bool					CommandLine::printValidationErrors			(void) const	{ return m_cmdLine.getOption<opt::PrintValidationErrors>();					}
isLogDecompiledSpirvEnabled(void) const923 bool					CommandLine::isLogDecompiledSpirvEnabled	(void) const	{ return m_cmdLine.getOption<opt::LogDecompiledSpirv>();					}
isOutOfMemoryTestEnabled(void) const924 bool					CommandLine::isOutOfMemoryTestEnabled		(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();								}
isShadercacheEnabled(void) const925 bool					CommandLine::isShadercacheEnabled			(void) const	{ return m_cmdLine.getOption<opt::ShaderCache>();							}
getShaderCacheFilename(void) const926 const char*				CommandLine::getShaderCacheFilename			(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();			}
isShaderCacheTruncateEnabled(void) const927 bool					CommandLine::isShaderCacheTruncateEnabled	(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheTruncate>();					}
getOptimizationRecipe(void) const928 int						CommandLine::getOptimizationRecipe			(void) const	{ return m_cmdLine.getOption<opt::Optimization>();							}
isSpirvOptimizationEnabled(void) const929 bool					CommandLine::isSpirvOptimizationEnabled		(void) const	{ return m_cmdLine.getOption<opt::OptimizeSpirv>();							}
isRenderDocEnabled(void) const930 bool					CommandLine::isRenderDocEnabled				(void) const	{ return m_cmdLine.getOption<opt::RenderDoc>();								}
getWaiverFileName(void) const931 const char*				CommandLine::getWaiverFileName				(void) const	{ return m_cmdLine.getOption<opt::WaiverFile>().c_str();					}
getCaseFraction(void) const932 const std::vector<int>&	CommandLine::getCaseFraction				(void) const	{ return m_cmdLine.getOption<opt::CaseFraction>();							}
getCaseFractionMandatoryTests(void) const933 const char*				CommandLine::getCaseFractionMandatoryTests	(void) const	{ return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str();	}
getArchiveDir(void) const934 const char*				CommandLine::getArchiveDir					(void) const	{ return m_cmdLine.getOption<opt::ArchiveDir>().c_str();					}
getRunnerType(void) const935 tcu::TestRunnerType		CommandLine::getRunnerType					(void) const	{ return m_cmdLine.getOption<opt::RunnerType>();							}
isTerminateOnFailEnabled(void) const936 bool					CommandLine::isTerminateOnFailEnabled		(void) const	{ return m_cmdLine.getOption<opt::TerminateOnFail>();						}
937 
getGLContextType(void) const938 const char* CommandLine::getGLContextType (void) const
939 {
940 	if (m_cmdLine.hasOption<opt::GLContextType>())
941 		return m_cmdLine.getOption<opt::GLContextType>().c_str();
942 	else
943 		return DE_NULL;
944 }
getGLConfigName(void) const945 const char* CommandLine::getGLConfigName (void) const
946 {
947 	if (m_cmdLine.hasOption<opt::GLConfigName>())
948 		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
949 	else
950 		return DE_NULL;
951 }
952 
getGLContextFlags(void) const953 const char* CommandLine::getGLContextFlags (void) const
954 {
955 	if (m_cmdLine.hasOption<opt::GLContextFlags>())
956 		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
957 	else
958 		return DE_NULL;
959 }
960 
getCLBuildOptions(void) const961 const char* CommandLine::getCLBuildOptions (void) const
962 {
963 	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
964 		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
965 	else
966 		return DE_NULL;
967 }
968 
getEGLDisplayType(void) const969 const char* CommandLine::getEGLDisplayType (void) const
970 {
971 	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
972 		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
973 	else
974 		return DE_NULL;
975 }
976 
getEGLWindowType(void) const977 const char* CommandLine::getEGLWindowType (void) const
978 {
979 	if (m_cmdLine.hasOption<opt::EGLWindowType>())
980 		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
981 	else
982 		return DE_NULL;
983 }
984 
getEGLPixmapType(void) const985 const char* CommandLine::getEGLPixmapType (void) const
986 {
987 	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
988 		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
989 	else
990 		return DE_NULL;
991 }
992 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)993 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
994 {
995 	const CaseTreeNode* node = findNode(root, groupPath);
996 	return node && node->hasChildren();
997 }
998 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)999 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1000 {
1001 	const CaseTreeNode* node = findNode(root, casePath);
1002 	return node && !node->hasChildren();
1003 }
1004 
createCaseListFilter(const tcu::Archive & archive) const1005 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1006 {
1007 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1008 }
1009 
checkTestGroupName(const char * groupName) const1010 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1011 {
1012 	bool result = false;
1013 	if (m_casePaths)
1014 		result = m_casePaths->matches(groupName, true);
1015 	else if (m_caseTree)
1016 		result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1017 	else
1018 		return true;
1019 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1020 		result = m_caseFractionMandatoryTests->matches(groupName, true);
1021 	return result;
1022 }
1023 
checkTestCaseName(const char * caseName) const1024 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1025 {
1026 	bool result = false;
1027 	if (m_casePaths)
1028 		result = m_casePaths->matches(caseName, false);
1029 	else if (m_caseTree)
1030 		result = tcu::checkTestCaseName(m_caseTree, caseName);
1031 	else
1032 		return true;
1033 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1034 		result = m_caseFractionMandatoryTests->matches(caseName, false);
1035 	return result;
1036 }
1037 
checkCaseFraction(int i,const std::string & testCaseName) const1038 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1039 {
1040 	return	m_caseFraction.size() != 2 ||
1041 		((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1042 		(m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1043 }
1044 
CaseListFilter(void)1045 CaseListFilter::CaseListFilter (void)
1046 	: m_caseTree	(DE_NULL)
1047 	, m_runnerType	(tcu::RUNNERTYPE_ANY)
1048 {
1049 }
1050 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1051 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1052 	: m_caseTree	(DE_NULL)
1053 {
1054 	if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1055 	{
1056 		m_runnerType = RUNNERTYPE_AMBER;
1057 	}
1058 	else
1059 	{
1060 		m_runnerType = cmdLine.getOption<opt::RunnerType>();
1061 	}
1062 
1063 	if (cmdLine.hasOption<opt::CaseList>())
1064 	{
1065 		std::istringstream str(cmdLine.getOption<opt::CaseList>());
1066 
1067 		m_caseTree = parseCaseList(str, archive);
1068 	}
1069 	else if (cmdLine.hasOption<opt::CaseListFile>())
1070 	{
1071 		std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1072 		std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1073 
1074 		if (!in.is_open() || !in.good())
1075 			throw Exception("Failed to open case list file '" + caseListFile + "'");
1076 
1077 		m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1078 	}
1079 	else if (cmdLine.hasOption<opt::CaseListResource>())
1080 	{
1081 		// \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1082 		//						   istream adaptor for tcu::Resource.
1083 		de::UniquePtr<Resource>	caseListResource	(archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1084 		const int				bufferSize			= caseListResource->getSize();
1085 		std::vector<char>		buffer				((size_t)bufferSize);
1086 
1087 		if (buffer.empty())
1088 			throw Exception("Empty case list resource");
1089 
1090 		caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1091 
1092 		{
1093 			std::istringstream	in	(std::string(&buffer[0], (size_t)bufferSize));
1094 
1095 			m_caseTree = parseCaseList(in, archive);
1096 		}
1097 	}
1098 	else if (cmdLine.getOption<opt::StdinCaseList>())
1099 	{
1100 		m_caseTree = parseCaseList(std::cin, archive);
1101 	}
1102 	else if (cmdLine.hasOption<opt::CasePath>())
1103 		m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1104 
1105 	m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1106 
1107 	if (m_caseFraction.size() == 2 &&
1108 		(m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1109 		throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1110 
1111 	if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1112 		throw Exception("Invalid case fraction. Must have two components.");
1113 
1114 	if (m_caseFraction.size() == 2)
1115 	{
1116 		std::string					caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1117 
1118 		if (!caseFractionMandatoryTestsFilename.empty())
1119 		{
1120 			std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1121 			if (!fileStream.is_open() || !fileStream.good())
1122 				throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1123 
1124 			std::vector<std::string>	cfPaths;
1125 			std::string					line;
1126 
1127 			while (std::getline(fileStream, line))
1128 			{
1129 				line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1130 				cfPaths.push_back(line);
1131 			}
1132 			if (!cfPaths.empty())
1133 			{
1134 				m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1135 				if (m_caseTree != DE_NULL)
1136 				{
1137 					fileStream.clear();
1138 					fileStream.seekg(0, fileStream.beg);
1139 					parseCaseList(m_caseTree, fileStream, false);
1140 				}
1141 			}
1142 		}
1143 	}
1144 }
1145 
~CaseListFilter(void)1146 CaseListFilter::~CaseListFilter (void)
1147 {
1148 	delete m_caseTree;
1149 }
1150 
1151 } // tcu
1152