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