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