tcuCommandLine.cpp revision 89659d2685195bf9e34ff7a2e321e6ce471c8462
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			else
379				expectNode = true;
380		}
381		else if (isValidTestCaseNameChar((char)curChr))
382			curName += (char)curChr;
383		else
384			throw std::invalid_argument("Illegal character in node name");
385	}
386}
387
388static void parseCaseList (CaseTreeNode* root, std::istream& in)
389{
390	// \note Algorithm assumes that cases are sorted by groups, but will
391	//		 function fine, albeit more slowly, if that is not the case.
392	vector<CaseTreeNode*>	nodeStack;
393	int						stackPos	= 0;
394	string					curName;
395
396	nodeStack.resize(8, DE_NULL);
397
398	nodeStack[0] = root;
399
400	for (;;)
401	{
402		const int	curChr	= in.get();
403
404		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
405		{
406			if (curName.empty())
407				throw std::invalid_argument("Empty test case name");
408
409			if (nodeStack[stackPos]->hasChild(curName))
410				throw std::invalid_argument("Duplicate test case");
411
412			CaseTreeNode* const newChild = new CaseTreeNode(curName);
413
414			try
415			{
416				nodeStack[stackPos]->addChild(newChild);
417			}
418			catch (...)
419			{
420				delete newChild;
421				throw;
422			}
423
424			curName.clear();
425			stackPos = 0;
426
427			if (curChr == '\r' && in.peek() == '\n')
428				in.get();
429
430			{
431				const int nextChr = in.peek();
432
433				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
434					break;
435			}
436		}
437		else if (curChr == '.')
438		{
439			if (curName.empty())
440				throw std::invalid_argument("Empty test group name");
441
442			if ((int)nodeStack.size() <= stackPos+1)
443				nodeStack.resize(nodeStack.size()*2, DE_NULL);
444
445			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
446			{
447				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
448
449				if (!curGroup)
450				{
451					curGroup = new CaseTreeNode(curName);
452
453					try
454					{
455						nodeStack[stackPos]->addChild(curGroup);
456					}
457					catch (...)
458					{
459						delete curGroup;
460						throw;
461					}
462				}
463
464				nodeStack[stackPos+1] = curGroup;
465
466				if ((int)nodeStack.size() > stackPos+2)
467					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
468			}
469
470			DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
471
472			curName.clear();
473			stackPos += 1;
474		}
475		else if (isValidTestCaseNameChar((char)curChr))
476			curName += (char)curChr;
477		else
478			throw std::invalid_argument("Illegal character in test case name");
479	}
480}
481
482static CaseTreeNode* parseCaseList (std::istream& in)
483{
484	CaseTreeNode* const root = new CaseTreeNode("");
485	try
486	{
487		if (in.peek() == '{')
488			parseCaseTrie(root, in);
489		else
490			parseCaseList(root, in);
491
492		{
493			const int curChr = in.get();
494			if (curChr != std::char_traits<char>::eof() && curChr != 0)
495				throw std::invalid_argument("Trailing characters at end of case list");
496		}
497
498		return root;
499	}
500	catch (...)
501	{
502		delete root;
503		throw;
504	}
505}
506
507class CasePaths
508{
509public:
510							CasePaths	(const string& pathList);
511	bool					matches		(const string& caseName, bool allowPrefix=false) const;
512
513private:
514	const vector<string>	m_casePatterns;
515};
516
517CasePaths::CasePaths (const string& pathList)
518	: m_casePatterns(de::splitString(pathList, ','))
519{
520}
521
522// Match a single path component against a pattern component that may contain *-wildcards.
523static bool matchWildcards(string::const_iterator	patternStart,
524						   string::const_iterator	patternEnd,
525						   string::const_iterator	pathStart,
526						   string::const_iterator	pathEnd,
527						   bool						allowPrefix)
528{
529	string::const_iterator	pattern	= patternStart;
530	string::const_iterator	path	= pathStart;
531
532	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
533	{
534		++pattern;
535		++path;
536	}
537
538	if (pattern == patternEnd)
539		return (path == pathEnd);
540	else if (*pattern == '*')
541	{
542		for (; path != pathEnd; ++path)
543		{
544			if (matchWildcards(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
545				return true;
546		}
547
548		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
549			return true;
550	}
551	else if (path == pathEnd && allowPrefix)
552		return true;
553
554	return false;
555}
556
557#if defined(TCU_HIERARCHICAL_CASEPATHS)
558// Match a list of pattern components to a list of path components. A pattern
559// component may contain *-wildcards. A pattern component "**" matches zero or
560// more whole path components.
561static bool patternMatches(vector<string>::const_iterator	patternStart,
562						   vector<string>::const_iterator	patternEnd,
563						   vector<string>::const_iterator	pathStart,
564						   vector<string>::const_iterator	pathEnd,
565						   bool								allowPrefix)
566{
567	vector<string>::const_iterator	pattern	= patternStart;
568	vector<string>::const_iterator	path	= pathStart;
569
570	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
571		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
572												path->begin(), path->end(), false)))
573	{
574		++pattern;
575		++path;
576	}
577
578	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
579		return true;
580	else if (pattern != patternEnd && *pattern == "**")
581	{
582		for (; path != pathEnd; ++path)
583			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
584				return true;
585		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
586			return true;
587	}
588
589	return false;
590}
591#endif
592
593bool CasePaths::matches (const string& caseName, bool allowPrefix) const
594{
595	const vector<string> components = de::splitString(caseName, '.');
596
597	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
598	{
599#if defined(TCU_HIERARCHICAL_CASEPATHS)
600		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
601
602		if (patternMatches(patternComponents.begin(), patternComponents.end(),
603						   components.begin(), components.end(), allowPrefix))
604			return true;
605#else
606		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
607						   caseName.begin(), caseName.end(), allowPrefix))
608			return true;
609#endif
610	}
611
612	return false;
613}
614
615/*--------------------------------------------------------------------*//*!
616 * \brief Construct command line
617 * \note CommandLine is not fully initialized until parse() has been called.
618 *//*--------------------------------------------------------------------*/
619CommandLine::CommandLine (void)
620	: m_logFlags	(0)
621	, m_caseTree	(DE_NULL)
622{
623}
624
625/*--------------------------------------------------------------------*//*!
626 * \brief Construct command line from standard argc, argv pair.
627 *
628 * Calls parse() with given arguments
629 * \param argc Number of arguments
630 * \param argv Command line arguments
631 *//*--------------------------------------------------------------------*/
632CommandLine::CommandLine (int argc, const char* const* argv)
633	: m_logFlags	(0)
634	, m_caseTree	(DE_NULL)
635{
636	if (!parse(argc, argv))
637		throw Exception("Failed to parse command line");
638}
639
640/*--------------------------------------------------------------------*//*!
641 * \brief Construct command line from string.
642 *
643 * Calls parse() with given argument.
644 * \param cmdLine Full command line string.
645 *//*--------------------------------------------------------------------*/
646CommandLine::CommandLine (const std::string& cmdLine)
647	: m_logFlags	(0)
648	, m_caseTree	(DE_NULL)
649{
650	if (!parse(cmdLine))
651		throw Exception("Failed to parse command line");
652}
653
654CommandLine::~CommandLine (void)
655{
656	delete m_caseTree;
657}
658
659void CommandLine::clear (void)
660{
661	m_cmdLine.clear();
662	m_logFlags = 0;
663
664	delete m_caseTree;
665	m_caseTree = DE_NULL;
666}
667
668/*--------------------------------------------------------------------*//*!
669 * \brief Parse command line from standard argc, argv pair.
670 * \note parse() must be called exactly once.
671 * \param argc Number of arguments
672 * \param argv Command line arguments
673 *//*--------------------------------------------------------------------*/
674bool CommandLine::parse (int argc, const char* const* argv)
675{
676	DebugOutStreambuf	sbuf;
677	std::ostream		debugOut	(&sbuf);
678	de::cmdline::Parser	parser;
679
680	opt::registerOptions(parser);
681	opt::registerLegacyOptions(parser);
682
683	clear();
684
685	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
686	{
687		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
688		parser.help(debugOut);
689
690		clear();
691		return false;
692	}
693
694	if (!m_cmdLine.getOption<opt::LogImages>())
695		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
696
697	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
698		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
699		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
700		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
701	{
702		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
703		clear();
704		return false;
705	}
706
707	try
708	{
709		if (m_cmdLine.hasOption<opt::CaseList>())
710		{
711			std::istringstream str(m_cmdLine.getOption<opt::CaseList>());
712
713			m_caseTree = parseCaseList(str);
714		}
715		else if (m_cmdLine.hasOption<opt::CaseListFile>())
716		{
717			std::ifstream in(m_cmdLine.getOption<opt::CaseListFile>().c_str(), std::ios_base::binary);
718
719			if (!in.is_open() || !in.good())
720				throw Exception("Failed to open case list file '" + m_cmdLine.getOption<opt::CaseListFile>() + "'");
721
722			m_caseTree = parseCaseList(in);
723		}
724		else if (m_cmdLine.getOption<opt::StdinCaseList>())
725		{
726			m_caseTree = parseCaseList(std::cin);
727		}
728		else if (m_cmdLine.hasOption<opt::CasePath>())
729			m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(m_cmdLine.getOption<opt::CasePath>()));
730	}
731	catch (const std::exception& e)
732	{
733		debugOut << "ERROR: Failed to parse test case list: " << e.what() << "\n";
734		clear();
735		return false;
736	}
737
738	return true;
739}
740
741/*--------------------------------------------------------------------*//*!
742 * \brief Parse command line from string.
743 * \note parse() must be called exactly once.
744 * \param cmdLine Full command line string.
745 *//*--------------------------------------------------------------------*/
746bool CommandLine::parse (const std::string& cmdLine)
747{
748	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
749	if (!parsedCmdLine)
750		throw std::bad_alloc();
751
752	bool isOk = false;
753	try
754	{
755		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
756	}
757	catch (...)
758	{
759		deCommandLine_destroy(parsedCmdLine);
760		throw;
761	}
762
763	deCommandLine_destroy(parsedCmdLine);
764	return isOk;
765}
766
767const char*				CommandLine::getLogFileName				(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();		}
768deUint32				CommandLine::getLogFlags				(void) const	{ return m_logFlags;											}
769RunMode					CommandLine::getRunMode					(void) const	{ return m_cmdLine.getOption<opt::RunMode>();					}
770WindowVisibility		CommandLine::getVisibility				(void) const	{ return m_cmdLine.getOption<opt::Visibility>();				}
771bool					CommandLine::isWatchDogEnabled			(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();					}
772bool					CommandLine::isCrashHandlingEnabled		(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();				}
773int						CommandLine::getBaseSeed				(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();					}
774int						CommandLine::getTestIterationCount		(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();		}
775int						CommandLine::getSurfaceWidth			(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();				}
776int						CommandLine::getSurfaceHeight			(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();				}
777SurfaceType				CommandLine::getSurfaceType				(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();				}
778ScreenRotation			CommandLine::getScreenRotation			(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();			}
779int						CommandLine::getGLConfigId				(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();				}
780int						CommandLine::getCLPlatformId			(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();				}
781const std::vector<int>&	CommandLine::getCLDeviceIds				(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();				}
782bool					CommandLine::isOutOfMemoryTestEnabled	(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();					}
783
784const char* CommandLine::getGLContextType (void) const
785{
786	if (m_cmdLine.hasOption<opt::GLContextType>())
787		return m_cmdLine.getOption<opt::GLContextType>().c_str();
788	else
789		return DE_NULL;
790}
791const char* CommandLine::getGLConfigName (void) const
792{
793	if (m_cmdLine.hasOption<opt::GLConfigName>())
794		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
795	else
796		return DE_NULL;
797}
798
799const char* CommandLine::getGLContextFlags (void) const
800{
801	if (m_cmdLine.hasOption<opt::GLContextFlags>())
802		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
803	else
804		return DE_NULL;
805}
806
807const char* CommandLine::getCLBuildOptions (void) const
808{
809	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
810		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
811	else
812		return DE_NULL;
813}
814
815const char* CommandLine::getEGLDisplayType (void) const
816{
817	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
818		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
819	else
820		return DE_NULL;
821}
822
823const char* CommandLine::getEGLWindowType (void) const
824{
825	if (m_cmdLine.hasOption<opt::EGLWindowType>())
826		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
827	else
828		return DE_NULL;
829}
830
831const char* CommandLine::getEGLPixmapType (void) const
832{
833	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
834		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
835	else
836		return DE_NULL;
837}
838
839static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
840{
841	const CaseTreeNode* node = findNode(root, groupPath);
842	return node && node->hasChildren();
843}
844
845static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
846{
847	const CaseTreeNode* node = findNode(root, casePath);
848	return node && !node->hasChildren();
849}
850
851bool CommandLine::checkTestGroupName (const char* groupName) const
852{
853	if (m_casePaths)
854		return m_casePaths->matches(groupName, true);
855	else if (m_caseTree)
856		return groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName);
857	else
858		return true;
859}
860
861bool CommandLine::checkTestCaseName (const char* caseName) const
862{
863	if (m_casePaths)
864		return m_casePaths->matches(caseName, false);
865	else if (m_caseTree)
866		return tcu::checkTestCaseName(m_caseTree, caseName);
867	else
868		return true;
869}
870
871} // tcu
872