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