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