Home | History | Annotate | Download | only in common
      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 
     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 
     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 
    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 
    195 DebugOutStreambuf::DebugOutStreambuf (void)
    196 {
    197 }
    198 
    199 DebugOutStreambuf::~DebugOutStreambuf (void)
    200 {
    201 	if (m_curLine.tellp() != std::streampos(0))
    202 		flushLine();
    203 }
    204 
    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 
    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 
    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:
    239 										CaseTreeNode		(const std::string& name) : m_name(name) {}
    240 										~CaseTreeNode		(void);
    241 
    242 	const std::string&					getName				(void) const { return m_name;				}
    243 	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 
    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 
    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 
    270 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 
    280 inline bool CaseTreeNode::hasChild (const std::string& name) const
    281 {
    282 	return findChildNdx(name) != NOT_FOUND;
    283 }
    284 
    285 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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.
    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 
    603 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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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 
    664 CommandLine::~CommandLine (void)
    665 {
    666 	delete m_caseTree;
    667 }
    668 
    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 
    678 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
    679 {
    680 	return m_cmdLine;
    681 }
    682 
    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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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 
    788 const char*				CommandLine::getLogFileName				(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();		}
    789 deUint32				CommandLine::getLogFlags				(void) const	{ return m_logFlags;											}
    790 RunMode					CommandLine::getRunMode					(void) const	{ return m_cmdLine.getOption<opt::RunMode>();					}
    791 WindowVisibility		CommandLine::getVisibility				(void) const	{ return m_cmdLine.getOption<opt::Visibility>();				}
    792 bool					CommandLine::isWatchDogEnabled			(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();					}
    793 bool					CommandLine::isCrashHandlingEnabled		(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();				}
    794 int						CommandLine::getBaseSeed				(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();					}
    795 int						CommandLine::getTestIterationCount		(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();		}
    796 int						CommandLine::getSurfaceWidth			(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();				}
    797 int						CommandLine::getSurfaceHeight			(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();				}
    798 SurfaceType				CommandLine::getSurfaceType				(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();				}
    799 ScreenRotation			CommandLine::getScreenRotation			(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();			}
    800 int						CommandLine::getGLConfigId				(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();				}
    801 int						CommandLine::getCLPlatformId			(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();				}
    802 const std::vector<int>&	CommandLine::getCLDeviceIds				(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();				}
    803 bool					CommandLine::isOutOfMemoryTestEnabled	(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();					}
    804 
    805 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 }
    812 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 
    820 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 
    828 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 
    836 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 
    844 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 
    852 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 
    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 
    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 
    872 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 
    882 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