• 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 
42 using std::string;
43 using std::vector;
44 
45 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
46 #if (DE_OS == DE_OS_WIN32)
47 #	define TEST_OOM_DEFAULT		"enable"
48 #else
49 #	define TEST_OOM_DEFAULT		"disable"
50 #endif
51 
52 namespace tcu
53 {
54 
55 namespace opt
56 {
57 
58 DE_DECLARE_COMMAND_LINE_OPT(CasePath,					std::string);
59 DE_DECLARE_COMMAND_LINE_OPT(CaseList,					std::string);
60 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile,				std::string);
61 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource,			std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList,				bool);
63 DE_DECLARE_COMMAND_LINE_OPT(LogFilename,				std::string);
64 DE_DECLARE_COMMAND_LINE_OPT(RunMode,					tcu::RunMode);
65 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern,		std::string);
66 DE_DECLARE_COMMAND_LINE_OPT(WatchDog,					bool);
67 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler,				bool);
68 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed,					int);
69 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount,			int);
70 DE_DECLARE_COMMAND_LINE_OPT(Visibility,					WindowVisibility);
71 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth,				int);
72 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight,				int);
73 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType,				tcu::SurfaceType);
74 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation,				tcu::ScreenRotation);
75 DE_DECLARE_COMMAND_LINE_OPT(GLContextType,				std::string);
76 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID,					int);
77 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName,				std::string);
78 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags,				std::string);
79 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID,				int);
80 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs,				std::vector<int>);
81 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions,				std::string);
82 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType,				std::string);
83 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType,				std::string);
84 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType,				std::string);
85 DE_DECLARE_COMMAND_LINE_OPT(LogImages,					bool);
86 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources,			bool);
87 DE_DECLARE_COMMAND_LINE_OPT(TestOOM,					bool);
88 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID,					int);
89 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID,			int);
90 DE_DECLARE_COMMAND_LINE_OPT(LogFlush,					bool);
91 DE_DECLARE_COMMAND_LINE_OPT(Validation,					bool);
92 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,				bool);
93 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,		std::string);
94 DE_DECLARE_COMMAND_LINE_OPT(Optimization,				int);
95 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,				bool);
96 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,		bool);
97 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,					bool);
98 
parseIntList(const char * src,std::vector<int> * dst)99 static void parseIntList (const char* src, std::vector<int>* dst)
100 {
101 	std::istringstream	str	(src);
102 	std::string			val;
103 
104 	while (std::getline(str, val, ','))
105 	{
106 		int intVal = 0;
107 		de::cmdline::parseType(val.c_str(), &intVal);
108 		dst->push_back(intVal);
109 	}
110 }
111 
registerOptions(de::cmdline::Parser & parser)112 void registerOptions (de::cmdline::Parser& parser)
113 {
114 	using de::cmdline::Option;
115 	using de::cmdline::NamedValue;
116 
117 	static const NamedValue<bool> s_enableNames[] =
118 	{
119 		{ "enable",		true	},
120 		{ "disable",	false	}
121 	};
122 	static const NamedValue<tcu::RunMode> s_runModes[] =
123 	{
124 		{ "execute",		RUNMODE_EXECUTE				},
125 		{ "xml-caselist",	RUNMODE_DUMP_XML_CASELIST	},
126 		{ "txt-caselist",	RUNMODE_DUMP_TEXT_CASELIST	},
127 		{ "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST}
128 	};
129 	static const NamedValue<WindowVisibility> s_visibilites[] =
130 	{
131 		{ "windowed",		WINDOWVISIBILITY_WINDOWED	},
132 		{ "fullscreen",		WINDOWVISIBILITY_FULLSCREEN	},
133 		{ "hidden",			WINDOWVISIBILITY_HIDDEN		}
134 	};
135 	static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
136 	{
137 		{ "window",			SURFACETYPE_WINDOW				},
138 		{ "pixmap",			SURFACETYPE_OFFSCREEN_NATIVE	},
139 		{ "pbuffer",		SURFACETYPE_OFFSCREEN_GENERIC	},
140 		{ "fbo",			SURFACETYPE_FBO					}
141 	};
142 	static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
143 	{
144 		{ "unspecified",	SCREENROTATION_UNSPECIFIED	},
145 		{ "0",				SCREENROTATION_0			},
146 		{ "90",				SCREENROTATION_90			},
147 		{ "180",			SCREENROTATION_180			},
148 		{ "270",			SCREENROTATION_270			}
149 	};
150 
151 	parser
152 		<< Option<CasePath>				("n",		"deqp-case",					"Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
153 		<< Option<CaseList>				(DE_NULL,	"deqp-caselist",				"Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
154 		<< Option<CaseListFile>			(DE_NULL,	"deqp-caselist-file",			"Read case list (in trie format) from given file")
155 		<< Option<CaseListResource>		(DE_NULL,	"deqp-caselist-resource",		"Read case list (in trie format) from given file located application's assets")
156 		<< Option<StdinCaseList>		(DE_NULL,	"deqp-stdin-caselist",			"Read case list (in trie format) from stdin")
157 		<< Option<LogFilename>			(DE_NULL,	"deqp-log-filename",			"Write test results to given file",					"TestResults.qpa")
158 		<< Option<RunMode>				(DE_NULL,	"deqp-runmode",					"Execute tests, or write list of test cases into a file",
159 																																		s_runModes,			"execute")
160 		<< Option<ExportFilenamePattern>(DE_NULL,	"deqp-caselist-export-file",	"Set the target file name pattern for caselist export",					"${packageName}-cases.${typeExtension}")
161 		<< Option<WatchDog>				(DE_NULL,	"deqp-watchdog",				"Enable test watchdog",								s_enableNames,		"disable")
162 		<< Option<CrashHandler>			(DE_NULL,	"deqp-crashhandler",			"Enable crash handling",							s_enableNames,		"disable")
163 		<< Option<BaseSeed>				(DE_NULL,	"deqp-base-seed",				"Base seed for test cases that use randomization",						"0")
164 		<< Option<TestIterationCount>	(DE_NULL,	"deqp-test-iteration-count",	"Iteration count for cases that support variable number of iterations",	"0")
165 		<< Option<Visibility>			(DE_NULL,	"deqp-visibility",				"Default test window visibility",					s_visibilites,		"windowed")
166 		<< Option<SurfaceWidth>			(DE_NULL,	"deqp-surface-width",			"Use given surface width if possible",									"-1")
167 		<< Option<SurfaceHeight>		(DE_NULL,	"deqp-surface-height",			"Use given surface height if possible",									"-1")
168 		<< Option<SurfaceType>			(DE_NULL,	"deqp-surface-type",			"Use given surface type",							s_surfaceTypes,		"window")
169 		<< Option<ScreenRotation>		(DE_NULL,	"deqp-screen-rotation",			"Screen rotation for platforms that support it",	s_screenRotations,	"0")
170 		<< Option<GLContextType>		(DE_NULL,	"deqp-gl-context-type",			"OpenGL context type for platforms that support multiple")
171 		<< Option<GLConfigID>			(DE_NULL,	"deqp-gl-config-id",			"OpenGL (ES) render config ID (EGL config id on EGL platforms)",		"-1")
172 		<< Option<GLConfigName>			(DE_NULL,	"deqp-gl-config-name",			"Symbolic OpenGL (ES) render config name")
173 		<< Option<GLContextFlags>		(DE_NULL,	"deqp-gl-context-flags",		"OpenGL context flags (comma-separated, supports debug and robust)")
174 		<< Option<CLPlatformID>			(DE_NULL,	"deqp-cl-platform-id",			"Execute tests on given OpenCL platform (IDs start from 1)",			"1")
175 		<< Option<CLDeviceIDs>			(DE_NULL,	"deqp-cl-device-ids",			"Execute tests on given CL devices (comma-separated, IDs start from 1)",	parseIntList,	"")
176 		<< Option<CLBuildOptions>		(DE_NULL,	"deqp-cl-build-options",		"Extra build options for OpenCL compiler")
177 		<< Option<EGLDisplayType>		(DE_NULL,	"deqp-egl-display-type",		"EGL native display type")
178 		<< Option<EGLWindowType>		(DE_NULL,	"deqp-egl-window-type",			"EGL native window type")
179 		<< Option<EGLPixmapType>		(DE_NULL,	"deqp-egl-pixmap-type",			"EGL native pixmap type")
180 		<< Option<VKDeviceID>			(DE_NULL,	"deqp-vk-device-id",			"Vulkan device ID (IDs start from 1)",									"1")
181 		<< Option<VKDeviceGroupID>		(DE_NULL,	"deqp-vk-device-group-id",		"Vulkan device Group ID (IDs start from 1)",							"1")
182 		<< Option<LogImages>			(DE_NULL,	"deqp-log-images",				"Enable or disable logging of result images",		s_enableNames,		"enable")
183 		<< Option<LogShaderSources>		(DE_NULL,	"deqp-log-shader-sources",		"Enable or disable logging of shader sources",		s_enableNames,		"enable")
184 		<< Option<TestOOM>				(DE_NULL,	"deqp-test-oom",				"Run tests that exhaust memory on purpose",			s_enableNames,		TEST_OOM_DEFAULT)
185 		<< Option<LogFlush>				(DE_NULL,	"deqp-log-flush",				"Enable or disable log file fflush",				s_enableNames,		"enable")
186 		<< Option<Validation>			(DE_NULL,	"deqp-validation",				"Enable or disable test case validation",			s_enableNames,		"disable")
187 		<< Option<Optimization>			(DE_NULL,	"deqp-optimization-recipe",		"Shader optimization recipe (0=disabled)",								"0")
188 		<< Option<OptimizeSpirv>		(DE_NULL,	"deqp-optimize-spirv",			"Apply optimization to spir-v shaders as well",		s_enableNames,		"disable")
189 		<< Option<ShaderCache>			(DE_NULL,	"deqp-shadercache",				"Enable or disable shader cache",					s_enableNames,		"enable")
190 		<< Option<ShaderCacheFilename>	(DE_NULL,	"deqp-shadercache-filename",	"Write shader cache to given file",										"shadercache.bin")
191 		<< Option<ShaderCacheTruncate>	(DE_NULL,	"deqp-shadercache-truncate",	"Truncate shader cache before running tests",		s_enableNames,		"enable")
192 		<< Option<RenderDoc>			(DE_NULL,	"deqp-renderdoc",				"Enable RenderDoc frame markers",					s_enableNames,		"disable");
193 }
194 
registerLegacyOptions(de::cmdline::Parser & parser)195 void registerLegacyOptions (de::cmdline::Parser& parser)
196 {
197 	using de::cmdline::Option;
198 
199 	parser
200 		<< Option<GLConfigID>			(DE_NULL,	"deqp-egl-config-id",			"Legacy name for --deqp-gl-config-id",	"-1")
201 		<< Option<GLConfigName>			(DE_NULL,	"deqp-egl-config-name",			"Legacy name for --deqp-gl-config-name");
202 }
203 
204 } // opt
205 
206 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
207 class DebugOutStreambuf : public std::streambuf
208 {
209 public:
210 						DebugOutStreambuf	(void);
211 						~DebugOutStreambuf	(void);
212 
213 protected:
214 	std::streamsize		xsputn				(const char* s, std::streamsize count);
215 	int					overflow			(int ch = -1);
216 
217 private:
218 	void				flushLine			(void);
219 
220 	std::ostringstream	m_curLine;
221 };
222 
DebugOutStreambuf(void)223 DebugOutStreambuf::DebugOutStreambuf (void)
224 {
225 }
226 
~DebugOutStreambuf(void)227 DebugOutStreambuf::~DebugOutStreambuf (void)
228 {
229 	if (m_curLine.tellp() != std::streampos(0))
230 		flushLine();
231 }
232 
xsputn(const char * s,std::streamsize count)233 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
234 {
235 	for (std::streamsize pos = 0; pos < count; pos++)
236 	{
237 		m_curLine.put(s[pos]);
238 
239 		if (s[pos] == '\n')
240 			flushLine();
241 	}
242 
243 	return count;
244 }
245 
overflow(int ch)246 int DebugOutStreambuf::overflow (int ch)
247 {
248 	if (ch == -1)
249 		return -1;
250 	else
251 	{
252 		DE_ASSERT((ch & 0xff) == ch);
253 		const char chVal = (char)(deUint8)(ch & 0xff);
254 		return xsputn(&chVal, 1) == 1 ? ch : -1;
255 	}
256 }
257 
flushLine(void)258 void DebugOutStreambuf::flushLine (void)
259 {
260 	qpPrint(m_curLine.str().c_str());
261 	m_curLine.str("");
262 }
263 
264 class CaseTreeNode
265 {
266 public:
CaseTreeNode(const std::string & name)267 										CaseTreeNode		(const std::string& name) : m_name(name) {}
268 										~CaseTreeNode		(void);
269 
getName(void) const270 	const std::string&					getName				(void) const { return m_name;				}
hasChildren(void) const271 	bool								hasChildren			(void) const { return !m_children.empty();	}
272 
273 	bool								hasChild			(const std::string& name) const;
274 	const CaseTreeNode*					getChild			(const std::string& name) const;
275 	CaseTreeNode*						getChild			(const std::string& name);
276 
addChild(CaseTreeNode * child)277 	void								addChild			(CaseTreeNode* child) { m_children.push_back(child); }
278 
279 private:
280 										CaseTreeNode		(const CaseTreeNode&);
281 	CaseTreeNode&						operator=			(const CaseTreeNode&);
282 
283 	enum { NOT_FOUND = -1 };
284 
285 	// \todo [2014-10-30 pyry] Speed up with hash / sorting
286 	int									findChildNdx		(const std::string& name) const;
287 
288 	std::string							m_name;
289 	std::vector<CaseTreeNode*>			m_children;
290 };
291 
~CaseTreeNode(void)292 CaseTreeNode::~CaseTreeNode (void)
293 {
294 	for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
295 		delete *i;
296 }
297 
findChildNdx(const std::string & name) const298 int CaseTreeNode::findChildNdx (const std::string& name) const
299 {
300 	for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
301 	{
302 		if (m_children[ndx]->getName() == name)
303 			return ndx;
304 	}
305 	return NOT_FOUND;
306 }
307 
hasChild(const std::string & name) const308 inline bool CaseTreeNode::hasChild (const std::string& name) const
309 {
310 	return findChildNdx(name) != NOT_FOUND;
311 }
312 
getChild(const std::string & name) const313 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
314 {
315 	const int ndx = findChildNdx(name);
316 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
317 }
318 
getChild(const std::string & name)319 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
320 {
321 	const int ndx = findChildNdx(name);
322 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
323 }
324 
getCurrentComponentLen(const char * path)325 static int getCurrentComponentLen (const char* path)
326 {
327 	int ndx = 0;
328 	for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
329 	return ndx;
330 }
331 
findNode(const CaseTreeNode * root,const char * path)332 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
333 {
334 	const CaseTreeNode*	curNode		= root;
335 	const char*			curPath		= path;
336 	int					curLen		= getCurrentComponentLen(curPath);
337 
338 	for (;;)
339 	{
340 		curNode = curNode->getChild(std::string(curPath, curPath+curLen));
341 
342 		if (!curNode)
343 			break;
344 
345 		curPath	+= curLen;
346 
347 		if (curPath[0] == 0)
348 			break;
349 		else
350 		{
351 			DE_ASSERT(curPath[0] == '.');
352 			curPath		+= 1;
353 			curLen		 = getCurrentComponentLen(curPath);
354 		}
355 	}
356 
357 	return curNode;
358 }
359 
parseCaseTrie(CaseTreeNode * root,std::istream & in)360 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
361 {
362 	vector<CaseTreeNode*>	nodeStack;
363 	string					curName;
364 	bool					expectNode		= true;
365 
366 	if (in.get() != '{')
367 		throw std::invalid_argument("Malformed case trie");
368 
369 	nodeStack.push_back(root);
370 
371 	while (!nodeStack.empty())
372 	{
373 		const int	curChr	= in.get();
374 
375 		if (curChr == std::char_traits<char>::eof() || curChr == 0)
376 			throw std::invalid_argument("Unterminated case tree");
377 
378 		if (curChr == '{' || curChr == ',' || curChr == '}')
379 		{
380 			if (!curName.empty() && expectNode)
381 			{
382 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
383 
384 				try
385 				{
386 					nodeStack.back()->addChild(newChild);
387 				}
388 				catch (...)
389 				{
390 					delete newChild;
391 					throw;
392 				}
393 
394 				if (curChr == '{')
395 					nodeStack.push_back(newChild);
396 
397 				curName.clear();
398 			}
399 			else if (curName.empty() == expectNode)
400 				throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
401 
402 			if (curChr == '}')
403 			{
404 				expectNode = false;
405 				nodeStack.pop_back();
406 
407 				// consume trailing new line
408 				if (nodeStack.empty())
409 				{
410 					if (in.peek() == '\r')
411 					  in.get();
412 					if (in.peek() == '\n')
413 					  in.get();
414 				}
415 			}
416 			else
417 				expectNode = true;
418 		}
419 		else if (isValidTestCaseNameChar((char)curChr))
420 			curName += (char)curChr;
421 		else
422 			throw std::invalid_argument("Illegal character in node name");
423 	}
424 }
425 
parseCaseList(CaseTreeNode * root,std::istream & in)426 static void parseCaseList (CaseTreeNode* root, std::istream& in)
427 {
428 	// \note Algorithm assumes that cases are sorted by groups, but will
429 	//		 function fine, albeit more slowly, if that is not the case.
430 	vector<CaseTreeNode*>	nodeStack;
431 	int						stackPos	= 0;
432 	string					curName;
433 
434 	nodeStack.resize(8, DE_NULL);
435 
436 	nodeStack[0] = root;
437 
438 	for (;;)
439 	{
440 		const int	curChr	= in.get();
441 
442 		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
443 		{
444 			if (curName.empty())
445 				throw std::invalid_argument("Empty test case name");
446 
447 			if (nodeStack[stackPos]->hasChild(curName))
448 				throw std::invalid_argument("Duplicate test case");
449 
450 			CaseTreeNode* const newChild = new CaseTreeNode(curName);
451 
452 			try
453 			{
454 				nodeStack[stackPos]->addChild(newChild);
455 			}
456 			catch (...)
457 			{
458 				delete newChild;
459 				throw;
460 			}
461 
462 			curName.clear();
463 			stackPos = 0;
464 
465 			if (curChr == '\r' && in.peek() == '\n')
466 				in.get();
467 
468 			{
469 				const int nextChr = in.peek();
470 
471 				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
472 					break;
473 			}
474 		}
475 		else if (curChr == '.')
476 		{
477 			if (curName.empty())
478 				throw std::invalid_argument("Empty test group name");
479 
480 			if ((int)nodeStack.size() <= stackPos+1)
481 				nodeStack.resize(nodeStack.size()*2, DE_NULL);
482 
483 			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
484 			{
485 				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
486 
487 				if (!curGroup)
488 				{
489 					curGroup = new CaseTreeNode(curName);
490 
491 					try
492 					{
493 						nodeStack[stackPos]->addChild(curGroup);
494 					}
495 					catch (...)
496 					{
497 						delete curGroup;
498 						throw;
499 					}
500 				}
501 
502 				nodeStack[stackPos+1] = curGroup;
503 
504 				if ((int)nodeStack.size() > stackPos+2)
505 					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
506 			}
507 
508 			DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
509 
510 			curName.clear();
511 			stackPos += 1;
512 		}
513 		else if (isValidTestCaseNameChar((char)curChr))
514 			curName += (char)curChr;
515 		else
516 			throw std::invalid_argument("Illegal character in test case name");
517 	}
518 }
519 
parseCaseList(std::istream & in)520 static CaseTreeNode* parseCaseList (std::istream& in)
521 {
522 	CaseTreeNode* const root = new CaseTreeNode("");
523 	try
524 	{
525 		if (in.peek() == '{')
526 			parseCaseTrie(root, in);
527 		else
528 			parseCaseList(root, in);
529 
530 		{
531 			const int curChr = in.get();
532 			if (curChr != std::char_traits<char>::eof() && curChr != 0)
533 				throw std::invalid_argument("Trailing characters at end of case list");
534 		}
535 
536 		return root;
537 	}
538 	catch (...)
539 	{
540 		delete root;
541 		throw;
542 	}
543 }
544 
545 class CasePaths
546 {
547 public:
548 							CasePaths	(const string& pathList);
549 	bool					matches		(const string& caseName, bool allowPrefix=false) const;
550 
551 private:
552 	const vector<string>	m_casePatterns;
553 };
554 
CasePaths(const string & pathList)555 CasePaths::CasePaths (const string& pathList)
556 	: m_casePatterns(de::splitString(pathList, ','))
557 {
558 }
559 
560 // 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)561 static bool matchWildcards(string::const_iterator	patternStart,
562 						   string::const_iterator	patternEnd,
563 						   string::const_iterator	pathStart,
564 						   string::const_iterator	pathEnd,
565 						   bool						allowPrefix)
566 {
567 	string::const_iterator	pattern	= patternStart;
568 	string::const_iterator	path	= pathStart;
569 
570 	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
571 	{
572 		++pattern;
573 		++path;
574 	}
575 
576 	if (pattern == patternEnd)
577 		return (path == pathEnd);
578 	else if (*pattern == '*')
579 	{
580 		for (; path != pathEnd; ++path)
581 		{
582 			if (matchWildcards(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
583 				return true;
584 		}
585 
586 		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
587 			return true;
588 	}
589 	else if (path == pathEnd && allowPrefix)
590 		return true;
591 
592 	return false;
593 }
594 
595 #if defined(TCU_HIERARCHICAL_CASEPATHS)
596 // Match a list of pattern components to a list of path components. A pattern
597 // component may contain *-wildcards. A pattern component "**" matches zero or
598 // 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)599 static bool patternMatches(vector<string>::const_iterator	patternStart,
600 						   vector<string>::const_iterator	patternEnd,
601 						   vector<string>::const_iterator	pathStart,
602 						   vector<string>::const_iterator	pathEnd,
603 						   bool								allowPrefix)
604 {
605 	vector<string>::const_iterator	pattern	= patternStart;
606 	vector<string>::const_iterator	path	= pathStart;
607 
608 	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
609 		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
610 												path->begin(), path->end(), false)))
611 	{
612 		++pattern;
613 		++path;
614 	}
615 
616 	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
617 		return true;
618 	else if (pattern != patternEnd && *pattern == "**")
619 	{
620 		for (; path != pathEnd; ++path)
621 			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
622 				return true;
623 		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
624 			return true;
625 	}
626 
627 	return false;
628 }
629 #endif
630 
matches(const string & caseName,bool allowPrefix) const631 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
632 {
633 	const vector<string> components = de::splitString(caseName, '.');
634 
635 	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
636 	{
637 #if defined(TCU_HIERARCHICAL_CASEPATHS)
638 		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
639 
640 		if (patternMatches(patternComponents.begin(), patternComponents.end(),
641 						   components.begin(), components.end(), allowPrefix))
642 			return true;
643 #else
644 		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
645 						   caseName.begin(), caseName.end(), allowPrefix))
646 			return true;
647 #endif
648 	}
649 
650 	return false;
651 }
652 
653 /*--------------------------------------------------------------------*//*!
654  * \brief Construct command line
655  * \note CommandLine is not fully initialized until parse() has been called.
656  *//*--------------------------------------------------------------------*/
CommandLine(void)657 CommandLine::CommandLine (void)
658 	: m_logFlags	(0)
659 {
660 }
661 
662 /*--------------------------------------------------------------------*//*!
663  * \brief Construct command line from standard argc, argv pair.
664  *
665  * Calls parse() with given arguments
666  * \param archive application's assets
667  * \param argc Number of arguments
668  * \param argv Command line arguments
669  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)670 CommandLine::CommandLine (int argc, const char* const* argv)
671 	: m_logFlags	(0)
672 {
673 	if (!parse(argc, argv))
674 		throw Exception("Failed to parse command line");
675 }
676 
677 /*--------------------------------------------------------------------*//*!
678  * \brief Construct command line from string.
679  *
680  * Calls parse() with given argument.
681  * \param archive application's assets
682  * \param cmdLine Full command line string.
683  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)684 CommandLine::CommandLine (const std::string& cmdLine)
685 {
686 	if (!parse(cmdLine))
687 		throw Exception("Failed to parse command line");
688 }
689 
~CommandLine(void)690 CommandLine::~CommandLine (void)
691 {
692 }
693 
clear(void)694 void CommandLine::clear (void)
695 {
696 	m_cmdLine.clear();
697 	m_logFlags = 0;
698 }
699 
getCommandLine(void) const700 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
701 {
702 	return m_cmdLine;
703 }
704 
registerExtendedOptions(de::cmdline::Parser & parser)705 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
706 {
707 	DE_UNREF(parser);
708 }
709 
710 /*--------------------------------------------------------------------*//*!
711  * \brief Parse command line from standard argc, argv pair.
712  * \note parse() must be called exactly once.
713  * \param argc Number of arguments
714  * \param argv Command line arguments
715  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)716 bool CommandLine::parse (int argc, const char* const* argv)
717 {
718 	DebugOutStreambuf	sbuf;
719 	std::ostream		debugOut	(&sbuf);
720 	de::cmdline::Parser	parser;
721 
722 	opt::registerOptions(parser);
723 	opt::registerLegacyOptions(parser);
724 	registerExtendedOptions(parser);
725 
726 	clear();
727 
728 	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
729 	{
730 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
731 		parser.help(debugOut);
732 
733 		clear();
734 		return false;
735 	}
736 
737 	if (!m_cmdLine.getOption<opt::LogImages>())
738 		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
739 
740 	if (!m_cmdLine.getOption<opt::LogShaderSources>())
741 		m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
742 
743 	if (!m_cmdLine.getOption<opt::LogFlush>())
744 		m_logFlags |= QP_TEST_LOG_NO_FLUSH;
745 
746 	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
747 		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
748 		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
749 		(m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
750 		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
751 	{
752 		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
753 		clear();
754 		return false;
755 	}
756 
757 	return true;
758 }
759 
760 /*--------------------------------------------------------------------*//*!
761  * \brief Parse command line from string.
762  * \note parse() must be called exactly once.
763  * \param cmdLine Full command line string.
764  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)765 bool CommandLine::parse (const std::string& cmdLine)
766 {
767 	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
768 	if (!parsedCmdLine)
769 		throw std::bad_alloc();
770 
771 	bool isOk = false;
772 	try
773 	{
774 		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
775 	}
776 	catch (...)
777 	{
778 		deCommandLine_destroy(parsedCmdLine);
779 		throw;
780 	}
781 
782 	deCommandLine_destroy(parsedCmdLine);
783 	return isOk;
784 }
785 
getLogFileName(void) const786 const char*				CommandLine::getLogFileName					(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();			}
getLogFlags(void) const787 deUint32				CommandLine::getLogFlags					(void) const	{ return m_logFlags;												}
getRunMode(void) const788 RunMode					CommandLine::getRunMode						(void) const	{ return m_cmdLine.getOption<opt::RunMode>();						}
getCaseListExportFile(void) const789 const char*				CommandLine::getCaseListExportFile			(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();	}
getVisibility(void) const790 WindowVisibility		CommandLine::getVisibility					(void) const	{ return m_cmdLine.getOption<opt::Visibility>();					}
isWatchDogEnabled(void) const791 bool					CommandLine::isWatchDogEnabled				(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();						}
isCrashHandlingEnabled(void) const792 bool					CommandLine::isCrashHandlingEnabled			(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();					}
getBaseSeed(void) const793 int						CommandLine::getBaseSeed					(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();						}
getTestIterationCount(void) const794 int						CommandLine::getTestIterationCount			(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();			}
getSurfaceWidth(void) const795 int						CommandLine::getSurfaceWidth				(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();					}
getSurfaceHeight(void) const796 int						CommandLine::getSurfaceHeight				(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();					}
getSurfaceType(void) const797 SurfaceType				CommandLine::getSurfaceType					(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();					}
getScreenRotation(void) const798 ScreenRotation			CommandLine::getScreenRotation				(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();				}
getGLConfigId(void) const799 int						CommandLine::getGLConfigId					(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();					}
getCLPlatformId(void) const800 int						CommandLine::getCLPlatformId				(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();					}
getCLDeviceIds(void) const801 const std::vector<int>&	CommandLine::getCLDeviceIds					(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();					}
getVKDeviceId(void) const802 int						CommandLine::getVKDeviceId					(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();					}
getVKDeviceGroupId(void) const803 int						CommandLine::getVKDeviceGroupId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();				}
isValidationEnabled(void) const804 bool					CommandLine::isValidationEnabled			(void) const	{ return m_cmdLine.getOption<opt::Validation>();					}
isOutOfMemoryTestEnabled(void) const805 bool					CommandLine::isOutOfMemoryTestEnabled		(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();						}
isShadercacheEnabled(void) const806 bool					CommandLine::isShadercacheEnabled			(void) const	{ return m_cmdLine.getOption<opt::ShaderCache>();					}
getShaderCacheFilename(void) const807 const char*				CommandLine::getShaderCacheFilename			(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();	}
isShaderCacheTruncateEnabled(void) const808 bool					CommandLine::isShaderCacheTruncateEnabled	(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheTruncate>();			}
getOptimizationRecipe(void) const809 int						CommandLine::getOptimizationRecipe			(void) const	{ return m_cmdLine.getOption<opt::Optimization>();					}
isSpirvOptimizationEnabled(void) const810 bool					CommandLine::isSpirvOptimizationEnabled		(void) const	{ return m_cmdLine.getOption<opt::OptimizeSpirv>();					}
isRenderDocEnabled(void) const811 bool					CommandLine::isRenderDocEnabled				(void) const	{ return m_cmdLine.getOption<opt::RenderDoc>();						}
812 
getGLContextType(void) const813 const char* CommandLine::getGLContextType (void) const
814 {
815 	if (m_cmdLine.hasOption<opt::GLContextType>())
816 		return m_cmdLine.getOption<opt::GLContextType>().c_str();
817 	else
818 		return DE_NULL;
819 }
getGLConfigName(void) const820 const char* CommandLine::getGLConfigName (void) const
821 {
822 	if (m_cmdLine.hasOption<opt::GLConfigName>())
823 		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
824 	else
825 		return DE_NULL;
826 }
827 
getGLContextFlags(void) const828 const char* CommandLine::getGLContextFlags (void) const
829 {
830 	if (m_cmdLine.hasOption<opt::GLContextFlags>())
831 		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
832 	else
833 		return DE_NULL;
834 }
835 
getCLBuildOptions(void) const836 const char* CommandLine::getCLBuildOptions (void) const
837 {
838 	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
839 		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
840 	else
841 		return DE_NULL;
842 }
843 
getEGLDisplayType(void) const844 const char* CommandLine::getEGLDisplayType (void) const
845 {
846 	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
847 		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
848 	else
849 		return DE_NULL;
850 }
851 
getEGLWindowType(void) const852 const char* CommandLine::getEGLWindowType (void) const
853 {
854 	if (m_cmdLine.hasOption<opt::EGLWindowType>())
855 		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
856 	else
857 		return DE_NULL;
858 }
859 
getEGLPixmapType(void) const860 const char* CommandLine::getEGLPixmapType (void) const
861 {
862 	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
863 		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
864 	else
865 		return DE_NULL;
866 }
867 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)868 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
869 {
870 	const CaseTreeNode* node = findNode(root, groupPath);
871 	return node && node->hasChildren();
872 }
873 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)874 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
875 {
876 	const CaseTreeNode* node = findNode(root, casePath);
877 	return node && !node->hasChildren();
878 }
879 
createCaseListFilter(const tcu::Archive & archive) const880 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
881 {
882 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
883 }
884 
checkTestGroupName(const char * groupName) const885 bool CaseListFilter::checkTestGroupName (const char* groupName) const
886 {
887 	if (m_casePaths)
888 		return m_casePaths->matches(groupName, true);
889 	else if (m_caseTree)
890 		return groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName);
891 	else
892 		return true;
893 }
894 
checkTestCaseName(const char * caseName) const895 bool CaseListFilter::checkTestCaseName (const char* caseName) const
896 {
897 	if (m_casePaths)
898 		return m_casePaths->matches(caseName, false);
899 	else if (m_caseTree)
900 		return tcu::checkTestCaseName(m_caseTree, caseName);
901 	else
902 		return true;
903 }
904 
CaseListFilter(void)905 CaseListFilter::CaseListFilter (void)
906 	: m_caseTree(DE_NULL)
907 {
908 }
909 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)910 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
911 	: m_caseTree(DE_NULL)
912 {
913 	if (cmdLine.hasOption<opt::CaseList>())
914 	{
915 		std::istringstream str(cmdLine.getOption<opt::CaseList>());
916 
917 		m_caseTree = parseCaseList(str);
918 	}
919 	else if (cmdLine.hasOption<opt::CaseListFile>())
920 	{
921 		std::ifstream in(cmdLine.getOption<opt::CaseListFile>().c_str(), std::ios_base::binary);
922 
923 		if (!in.is_open() || !in.good())
924 			throw Exception("Failed to open case list file '" + cmdLine.getOption<opt::CaseListFile>() + "'");
925 
926 		m_caseTree = parseCaseList(in);
927 	}
928 	else if (cmdLine.hasOption<opt::CaseListResource>())
929 	{
930 		// \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
931 		//						   istream adaptor for tcu::Resource.
932 		de::UniquePtr<Resource>	caseListResource	(archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
933 		const int				bufferSize			= caseListResource->getSize();
934 		std::vector<char>		buffer				((size_t)bufferSize);
935 
936 		if (buffer.empty())
937 			throw Exception("Empty case list resource");
938 
939 		caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
940 
941 		{
942 			std::istringstream	in	(std::string(&buffer[0], (size_t)bufferSize));
943 
944 			m_caseTree = parseCaseList(in);
945 		}
946 	}
947 	else if (cmdLine.getOption<opt::StdinCaseList>())
948 	{
949 		m_caseTree = parseCaseList(std::cin);
950 	}
951 	else if (cmdLine.hasOption<opt::CasePath>())
952 		m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
953 }
954 
~CaseListFilter(void)955 CaseListFilter::~CaseListFilter (void)
956 {
957 	delete m_caseTree;
958 }
959 
960 } // tcu
961