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