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