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