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(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 
     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 
    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 
    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 
    206 DebugOutStreambuf::DebugOutStreambuf (void)
    207 {
    208 }
    209 
    210 DebugOutStreambuf::~DebugOutStreambuf (void)
    211 {
    212 	if (m_curLine.tellp() != std::streampos(0))
    213 		flushLine();
    214 }
    215 
    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 
    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 
    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:
    250 										CaseTreeNode		(const std::string& name) : m_name(name) {}
    251 										~CaseTreeNode		(void);
    252 
    253 	const std::string&					getName				(void) const { return m_name;				}
    254 	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 
    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 
    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 
    281 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 
    291 inline bool CaseTreeNode::hasChild (const std::string& name) const
    292 {
    293 	return findChildNdx(name) != NOT_FOUND;
    294 }
    295 
    296 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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.
    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 
    614 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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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 
    675 CommandLine::~CommandLine (void)
    676 {
    677 	delete m_caseTree;
    678 }
    679 
    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 
    689 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
    690 {
    691 	return m_cmdLine;
    692 }
    693 
    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  *//*--------------------------------------------------------------------*/
    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  *//*--------------------------------------------------------------------*/
    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 
    805 const char*				CommandLine::getLogFileName				(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();			}
    806 deUint32				CommandLine::getLogFlags				(void) const	{ return m_logFlags;												}
    807 RunMode					CommandLine::getRunMode					(void) const	{ return m_cmdLine.getOption<opt::RunMode>();						}
    808 const char*				CommandLine::getCaseListExportFile		(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();	}
    809 WindowVisibility		CommandLine::getVisibility				(void) const	{ return m_cmdLine.getOption<opt::Visibility>();					}
    810 bool					CommandLine::isWatchDogEnabled			(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();						}
    811 bool					CommandLine::isCrashHandlingEnabled		(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();					}
    812 int						CommandLine::getBaseSeed				(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();						}
    813 int						CommandLine::getTestIterationCount		(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();			}
    814 int						CommandLine::getSurfaceWidth			(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();					}
    815 int						CommandLine::getSurfaceHeight			(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();					}
    816 SurfaceType				CommandLine::getSurfaceType				(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();					}
    817 ScreenRotation			CommandLine::getScreenRotation			(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();				}
    818 int						CommandLine::getGLConfigId				(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();					}
    819 int						CommandLine::getCLPlatformId			(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();					}
    820 const std::vector<int>&	CommandLine::getCLDeviceIds				(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();					}
    821 int						CommandLine::getVKDeviceId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();					}
    822 bool					CommandLine::isValidationEnabled		(void) const	{ return m_cmdLine.getOption<opt::Validation>();					}
    823 bool					CommandLine::isOutOfMemoryTestEnabled	(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();						}
    824 
    825 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 }
    832 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 
    840 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 
    848 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 
    856 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 
    864 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 
    872 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 
    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 
    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 
    892 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 
    902 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