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 
parseIntList(const char * src,std::vector<int> * dst)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 
registerOptions(de::cmdline::Parser & parser)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 
registerLegacyOptions(de::cmdline::Parser & parser)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 
DebugOutStreambuf(void)211 DebugOutStreambuf::DebugOutStreambuf (void)
212 {
213 }
214 
~DebugOutStreambuf(void)215 DebugOutStreambuf::~DebugOutStreambuf (void)
216 {
217 	if (m_curLine.tellp() != std::streampos(0))
218 		flushLine();
219 }
220 
xsputn(const char * s,std::streamsize count)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 
overflow(int ch)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 
flushLine(void)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:
CaseTreeNode(const std::string & name)255 										CaseTreeNode		(const std::string& name) : m_name(name) {}
256 										~CaseTreeNode		(void);
257 
getName(void) const258 	const std::string&					getName				(void) const { return m_name;				}
hasChildren(void) const259 	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 
addChild(CaseTreeNode * child)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 
~CaseTreeNode(void)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 
findChildNdx(const std::string & name) const286 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 
hasChild(const std::string & name) const296 inline bool CaseTreeNode::hasChild (const std::string& name) const
297 {
298 	return findChildNdx(name) != NOT_FOUND;
299 }
300 
getChild(const std::string & name) const301 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 
getChild(const std::string & name)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 
getCurrentComponentLen(const char * path)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 
findNode(const CaseTreeNode * root,const char * path)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 
parseCaseTrie(CaseTreeNode * root,std::istream & in)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 
parseCaseList(CaseTreeNode * root,std::istream & in)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 
parseCaseList(std::istream & in)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 
CasePaths(const string & pathList)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.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)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.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)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 
matches(const string & caseName,bool allowPrefix) const619 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  *//*--------------------------------------------------------------------*/
CommandLine(void)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  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)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  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)672 CommandLine::CommandLine (const std::string& cmdLine)
673 {
674 	if (!parse(cmdLine))
675 		throw Exception("Failed to parse command line");
676 }
677 
~CommandLine(void)678 CommandLine::~CommandLine (void)
679 {
680 }
681 
clear(void)682 void CommandLine::clear (void)
683 {
684 	m_cmdLine.clear();
685 	m_logFlags = 0;
686 }
687 
getCommandLine(void) const688 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
689 {
690 	return m_cmdLine;
691 }
692 
registerExtendedOptions(de::cmdline::Parser & parser)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  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)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  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)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 
getLogFileName(void) const774 const char*				CommandLine::getLogFileName				(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();			}
getLogFlags(void) const775 deUint32				CommandLine::getLogFlags				(void) const	{ return m_logFlags;												}
getRunMode(void) const776 RunMode					CommandLine::getRunMode					(void) const	{ return m_cmdLine.getOption<opt::RunMode>();						}
getCaseListExportFile(void) const777 const char*				CommandLine::getCaseListExportFile		(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();	}
getVisibility(void) const778 WindowVisibility		CommandLine::getVisibility				(void) const	{ return m_cmdLine.getOption<opt::Visibility>();					}
isWatchDogEnabled(void) const779 bool					CommandLine::isWatchDogEnabled			(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();						}
isCrashHandlingEnabled(void) const780 bool					CommandLine::isCrashHandlingEnabled		(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();					}
getBaseSeed(void) const781 int						CommandLine::getBaseSeed				(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();						}
getTestIterationCount(void) const782 int						CommandLine::getTestIterationCount		(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();			}
getSurfaceWidth(void) const783 int						CommandLine::getSurfaceWidth			(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();					}
getSurfaceHeight(void) const784 int						CommandLine::getSurfaceHeight			(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();					}
getSurfaceType(void) const785 SurfaceType				CommandLine::getSurfaceType				(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();					}
getScreenRotation(void) const786 ScreenRotation			CommandLine::getScreenRotation			(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();				}
getGLConfigId(void) const787 int						CommandLine::getGLConfigId				(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();					}
getCLPlatformId(void) const788 int						CommandLine::getCLPlatformId			(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();					}
getCLDeviceIds(void) const789 const std::vector<int>&	CommandLine::getCLDeviceIds				(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();					}
getVKDeviceId(void) const790 int						CommandLine::getVKDeviceId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();					}
getVKDeviceGroupId(void) const791 int						CommandLine::getVKDeviceGroupId			(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();				}
isValidationEnabled(void) const792 bool					CommandLine::isValidationEnabled		(void) const	{ return m_cmdLine.getOption<opt::Validation>();					}
isOutOfMemoryTestEnabled(void) const793 bool					CommandLine::isOutOfMemoryTestEnabled	(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();						}
794 
getGLContextType(void) const795 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 }
getGLConfigName(void) const802 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 
getGLContextFlags(void) const810 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 
getCLBuildOptions(void) const818 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 
getEGLDisplayType(void) const826 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 
getEGLWindowType(void) const834 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 
getEGLPixmapType(void) const842 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 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)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 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)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 
createCaseListFilter(const tcu::Archive & archive) const862 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
863 {
864 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
865 }
866 
checkTestGroupName(const char * groupName) const867 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 
checkTestCaseName(const char * caseName) const877 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 
CaseListFilter(void)887 CaseListFilter::CaseListFilter (void)
888 	: m_caseTree(DE_NULL)
889 {
890 }
891 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)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 
~CaseListFilter(void)937 CaseListFilter::~CaseListFilter (void)
938 {
939 	delete m_caseTree;
940 }
941 
942 } // tcu
943