1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Command line parsing.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuCommandLine.hpp"
25 #include "tcuPlatform.hpp"
26 #include "tcuTestCase.hpp"
27 #include "tcuResource.hpp"
28 #include "deFilePath.hpp"
29 #include "deStringUtil.hpp"
30 #include "deString.h"
31 #include "deInt32.h"
32 #include "deCommandLine.h"
33 #include "qpTestLog.h"
34 #include "qpDebugOut.h"
35 
36 #include <string>
37 #include <vector>
38 #include <sstream>
39 #include <fstream>
40 #include <iostream>
41 #include <algorithm>
42 
43 using std::string;
44 using std::vector;
45 
46 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
47 #if (DE_OS == DE_OS_WIN32)
48 #	define TEST_OOM_DEFAULT		"enable"
49 #else
50 #	define TEST_OOM_DEFAULT		"disable"
51 #endif
52 
53 namespace tcu
54 {
55 
56 namespace opt
57 {
58 
59 DE_DECLARE_COMMAND_LINE_OPT(CasePath,					std::string);
60 DE_DECLARE_COMMAND_LINE_OPT(CaseList,					std::string);
61 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile,				std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource,			std::string);
63 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList,				bool);
64 DE_DECLARE_COMMAND_LINE_OPT(LogFilename,				std::string);
65 DE_DECLARE_COMMAND_LINE_OPT(RunMode,					tcu::RunMode);
66 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern,		std::string);
67 DE_DECLARE_COMMAND_LINE_OPT(WatchDog,					bool);
68 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler,				bool);
69 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed,					int);
70 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount,			int);
71 DE_DECLARE_COMMAND_LINE_OPT(Visibility,					WindowVisibility);
72 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth,				int);
73 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight,				int);
74 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType,				tcu::SurfaceType);
75 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation,				tcu::ScreenRotation);
76 DE_DECLARE_COMMAND_LINE_OPT(GLContextType,				std::string);
77 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID,					int);
78 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName,				std::string);
79 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags,				std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID,				int);
81 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs,				std::vector<int>);
82 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions,				std::string);
83 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType,				std::string);
84 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType,				std::string);
85 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType,				std::string);
86 DE_DECLARE_COMMAND_LINE_OPT(LogImages,					bool);
87 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources,			bool);
88 DE_DECLARE_COMMAND_LINE_OPT(TestOOM,					bool);
89 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir,					std::string);
90 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID,					int);
91 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID,			int);
92 DE_DECLARE_COMMAND_LINE_OPT(LogFlush,					bool);
93 DE_DECLARE_COMMAND_LINE_OPT(Validation,					bool);
94 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors,		bool);
95 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache,				bool);
96 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,		std::string);
97 DE_DECLARE_COMMAND_LINE_OPT(Optimization,				int);
98 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,				bool);
99 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,		bool);
100 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,					bool);
101 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction,				std::vector<int>);
102 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests,	std::string);
103 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile,					std::string);
104 DE_DECLARE_COMMAND_LINE_OPT(RunnerType,					tcu::TestRunnerType);
105 
parseIntList(const char * src,std::vector<int> * dst)106 static void parseIntList (const char* src, std::vector<int>* dst)
107 {
108 	std::istringstream	str	(src);
109 	std::string			val;
110 
111 	while (std::getline(str, val, ','))
112 	{
113 		int intVal = 0;
114 		de::cmdline::parseType(val.c_str(), &intVal);
115 		dst->push_back(intVal);
116 	}
117 }
118 
registerOptions(de::cmdline::Parser & parser)119 void registerOptions (de::cmdline::Parser& parser)
120 {
121 	using de::cmdline::Option;
122 	using de::cmdline::NamedValue;
123 
124 	static const NamedValue<bool> s_enableNames[] =
125 	{
126 		{ "enable",		true	},
127 		{ "disable",	false	}
128 	};
129 	static const NamedValue<tcu::RunMode> s_runModes[] =
130 	{
131 		{ "execute",		RUNMODE_EXECUTE				},
132 		{ "xml-caselist",	RUNMODE_DUMP_XML_CASELIST	},
133 		{ "txt-caselist",	RUNMODE_DUMP_TEXT_CASELIST	},
134 		{ "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST}
135 	};
136 	static const NamedValue<WindowVisibility> s_visibilites[] =
137 	{
138 		{ "windowed",		WINDOWVISIBILITY_WINDOWED	},
139 		{ "fullscreen",		WINDOWVISIBILITY_FULLSCREEN	},
140 		{ "hidden",			WINDOWVISIBILITY_HIDDEN		}
141 	};
142 	static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
143 	{
144 		{ "window",			SURFACETYPE_WINDOW				},
145 		{ "pixmap",			SURFACETYPE_OFFSCREEN_NATIVE	},
146 		{ "pbuffer",		SURFACETYPE_OFFSCREEN_GENERIC	},
147 		{ "fbo",			SURFACETYPE_FBO					}
148 	};
149 	static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
150 	{
151 		{ "unspecified",	SCREENROTATION_UNSPECIFIED	},
152 		{ "0",				SCREENROTATION_0			},
153 		{ "90",				SCREENROTATION_90			},
154 		{ "180",			SCREENROTATION_180			},
155 		{ "270",			SCREENROTATION_270			}
156 	};
157 	static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
158 	{
159 		{ "any",	tcu::RUNNERTYPE_ANY		},
160 		{ "none",	tcu::RUNNERTYPE_NONE	},
161 		{ "amber",	tcu::RUNNERTYPE_AMBER	},
162 	};
163 
164 	parser
165 		<< Option<CasePath>						("n",		"deqp-case",								"Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
166 		<< Option<CaseList>						(DE_NULL,	"deqp-caselist",							"Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
167 		<< Option<CaseListFile>					(DE_NULL,	"deqp-caselist-file",						"Read case list (in trie format) from given file")
168 		<< Option<CaseListResource>				(DE_NULL,	"deqp-caselist-resource",					"Read case list (in trie format) from given file located application's assets")
169 		<< Option<StdinCaseList>				(DE_NULL,	"deqp-stdin-caselist",						"Read case list (in trie format) from stdin")
170 		<< Option<LogFilename>					(DE_NULL,	"deqp-log-filename",						"Write test results to given file",					"TestResults.qpa")
171 		<< Option<RunMode>						(DE_NULL,	"deqp-runmode",								"Execute tests, or write list of test cases into a file",
172 																																							s_runModes,			"execute")
173 		<< Option<ExportFilenamePattern>		(DE_NULL,	"deqp-caselist-export-file",				"Set the target file name pattern for caselist export",					"${packageName}-cases.${typeExtension}")
174 		<< Option<WatchDog>						(DE_NULL,	"deqp-watchdog",							"Enable test watchdog",								s_enableNames,		"disable")
175 		<< Option<CrashHandler>					(DE_NULL,	"deqp-crashhandler",						"Enable crash handling",							s_enableNames,		"disable")
176 		<< Option<BaseSeed>						(DE_NULL,	"deqp-base-seed",							"Base seed for test cases that use randomization",						"0")
177 		<< Option<TestIterationCount>			(DE_NULL,	"deqp-test-iteration-count",				"Iteration count for cases that support variable number of iterations",	"0")
178 		<< Option<Visibility>					(DE_NULL,	"deqp-visibility",							"Default test window visibility",					s_visibilites,		"windowed")
179 		<< Option<SurfaceWidth>					(DE_NULL,	"deqp-surface-width",						"Use given surface width if possible",									"-1")
180 		<< Option<SurfaceHeight>				(DE_NULL,	"deqp-surface-height",						"Use given surface height if possible",									"-1")
181 		<< Option<SurfaceType>					(DE_NULL,	"deqp-surface-type",						"Use given surface type",							s_surfaceTypes,		"window")
182 		<< Option<ScreenRotation>				(DE_NULL,	"deqp-screen-rotation",						"Screen rotation for platforms that support it",	s_screenRotations,	"0")
183 		<< Option<GLContextType>				(DE_NULL,	"deqp-gl-context-type",						"OpenGL context type for platforms that support multiple")
184 		<< Option<GLConfigID>					(DE_NULL,	"deqp-gl-config-id",						"OpenGL (ES) render config ID (EGL config id on EGL platforms)",		"-1")
185 		<< Option<GLConfigName>					(DE_NULL,	"deqp-gl-config-name",						"Symbolic OpenGL (ES) render config name")
186 		<< Option<GLContextFlags>				(DE_NULL,	"deqp-gl-context-flags",					"OpenGL context flags (comma-separated, supports debug and robust)")
187 		<< Option<CLPlatformID>					(DE_NULL,	"deqp-cl-platform-id",						"Execute tests on given OpenCL platform (IDs start from 1)",			"1")
188 		<< Option<CLDeviceIDs>					(DE_NULL,	"deqp-cl-device-ids",						"Execute tests on given CL devices (comma-separated, IDs start from 1)",	parseIntList,	"")
189 		<< Option<CLBuildOptions>				(DE_NULL,	"deqp-cl-build-options",					"Extra build options for OpenCL compiler")
190 		<< Option<EGLDisplayType>				(DE_NULL,	"deqp-egl-display-type",					"EGL native display type")
191 		<< Option<EGLWindowType>				(DE_NULL,	"deqp-egl-window-type",						"EGL native window type")
192 		<< Option<EGLPixmapType>				(DE_NULL,	"deqp-egl-pixmap-type",						"EGL native pixmap type")
193 		<< Option<VKDeviceID>					(DE_NULL,	"deqp-vk-device-id",						"Vulkan device ID (IDs start from 1)",									"1")
194 		<< Option<VKDeviceGroupID>				(DE_NULL,	"deqp-vk-device-group-id",					"Vulkan device Group ID (IDs start from 1)",							"1")
195 		<< Option<LogImages>					(DE_NULL,	"deqp-log-images",							"Enable or disable logging of result images",		s_enableNames,		"enable")
196 		<< Option<LogShaderSources>				(DE_NULL,	"deqp-log-shader-sources",					"Enable or disable logging of shader sources",		s_enableNames,		"enable")
197 		<< Option<TestOOM>						(DE_NULL,	"deqp-test-oom",							"Run tests that exhaust memory on purpose",			s_enableNames,		TEST_OOM_DEFAULT)
198 		<< Option<ArchiveDir>					(DE_NULL,	"deqp-archive-dir",							"Path to test resource files",											".")
199 		<< Option<LogFlush>						(DE_NULL,	"deqp-log-flush",							"Enable or disable log file fflush",				s_enableNames,		"enable")
200 		<< Option<Validation>					(DE_NULL,	"deqp-validation",							"Enable or disable test case validation",			s_enableNames,		"disable")
201 		<< Option<PrintValidationErrors>		(DE_NULL,	"deqp-print-validation-errors",				"Print validation errors to standard error")
202 		<< Option<Optimization>					(DE_NULL,	"deqp-optimization-recipe",					"Shader optimization recipe (0=disabled, 1=performance, 2=size)",		"0")
203 		<< Option<OptimizeSpirv>				(DE_NULL,	"deqp-optimize-spirv",						"Apply optimization to spir-v shaders as well",		s_enableNames,		"disable")
204 		<< Option<ShaderCache>					(DE_NULL,	"deqp-shadercache",							"Enable or disable shader cache",					s_enableNames,		"enable")
205 		<< Option<ShaderCacheFilename>			(DE_NULL,	"deqp-shadercache-filename",				"Write shader cache to given file",										"shadercache.bin")
206 		<< Option<ShaderCacheTruncate>			(DE_NULL,	"deqp-shadercache-truncate",				"Truncate shader cache before running tests",		s_enableNames,		"enable")
207 		<< Option<RenderDoc>					(DE_NULL,	"deqp-renderdoc",							"Enable RenderDoc frame markers",					s_enableNames,		"disable")
208 		<< Option<CaseFraction>					(DE_NULL,	"deqp-fraction",							"Run a fraction of the test cases (e.g. N,M means run group%M==N)",	parseIntList,	"")
209 		<< Option<CaseFractionMandatoryTests>	(DE_NULL,	"deqp-fraction-mandatory-caselist-file",	"Case list file that must be run for each fraction",					"")
210 		<< Option<WaiverFile>					(DE_NULL,	"deqp-waiver-file",							"Read waived tests from given file",									"")
211 		<< Option<RunnerType>					(DE_NULL,	"deqp-runner-type",							"Filter test cases based on runner",				s_runnerTypes,		"any");
212 }
213 
registerLegacyOptions(de::cmdline::Parser & parser)214 void registerLegacyOptions (de::cmdline::Parser& parser)
215 {
216 	using de::cmdline::Option;
217 
218 	parser
219 		<< Option<GLConfigID>			(DE_NULL,	"deqp-egl-config-id",			"Legacy name for --deqp-gl-config-id",	"-1")
220 		<< Option<GLConfigName>			(DE_NULL,	"deqp-egl-config-name",			"Legacy name for --deqp-gl-config-name");
221 }
222 
223 } // opt
224 
225 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
226 class DebugOutStreambuf : public std::streambuf
227 {
228 public:
229 						DebugOutStreambuf	(void);
230 						~DebugOutStreambuf	(void);
231 
232 protected:
233 	std::streamsize		xsputn				(const char* s, std::streamsize count);
234 	int					overflow			(int ch = -1);
235 
236 private:
237 	void				flushLine			(void);
238 
239 	std::ostringstream	m_curLine;
240 };
241 
DebugOutStreambuf(void)242 DebugOutStreambuf::DebugOutStreambuf (void)
243 {
244 }
245 
~DebugOutStreambuf(void)246 DebugOutStreambuf::~DebugOutStreambuf (void)
247 {
248 	if (m_curLine.tellp() != std::streampos(0))
249 		flushLine();
250 }
251 
xsputn(const char * s,std::streamsize count)252 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
253 {
254 	for (std::streamsize pos = 0; pos < count; pos++)
255 	{
256 		m_curLine.put(s[pos]);
257 
258 		if (s[pos] == '\n')
259 			flushLine();
260 	}
261 
262 	return count;
263 }
264 
overflow(int ch)265 int DebugOutStreambuf::overflow (int ch)
266 {
267 	if (ch == -1)
268 		return -1;
269 	else
270 	{
271 		DE_ASSERT((ch & 0xff) == ch);
272 		const char chVal = (char)(deUint8)(ch & 0xff);
273 		return xsputn(&chVal, 1) == 1 ? ch : -1;
274 	}
275 }
276 
flushLine(void)277 void DebugOutStreambuf::flushLine (void)
278 {
279 	qpPrint(m_curLine.str().c_str());
280 	m_curLine.str("");
281 }
282 
283 class CaseTreeNode
284 {
285 public:
CaseTreeNode(const std::string & name)286 										CaseTreeNode		(const std::string& name) : m_name(name) {}
287 										~CaseTreeNode		(void);
288 
getName(void) const289 	const std::string&					getName				(void) const { return m_name;				}
hasChildren(void) const290 	bool								hasChildren			(void) const { return !m_children.empty();	}
291 
292 	bool								hasChild			(const std::string& name) const;
293 	const CaseTreeNode*					getChild			(const std::string& name) const;
294 	CaseTreeNode*						getChild			(const std::string& name);
295 
addChild(CaseTreeNode * child)296 	void								addChild			(CaseTreeNode* child) { m_children.push_back(child); }
297 
298 private:
299 										CaseTreeNode		(const CaseTreeNode&);
300 	CaseTreeNode&						operator=			(const CaseTreeNode&);
301 
302 	enum { NOT_FOUND = -1 };
303 
304 	// \todo [2014-10-30 pyry] Speed up with hash / sorting
305 	int									findChildNdx		(const std::string& name) const;
306 
307 	std::string							m_name;
308 	std::vector<CaseTreeNode*>			m_children;
309 };
310 
~CaseTreeNode(void)311 CaseTreeNode::~CaseTreeNode (void)
312 {
313 	for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
314 		delete *i;
315 }
316 
findChildNdx(const std::string & name) const317 int CaseTreeNode::findChildNdx (const std::string& name) const
318 {
319 	for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
320 	{
321 		if (m_children[ndx]->getName() == name)
322 			return ndx;
323 	}
324 	return NOT_FOUND;
325 }
326 
hasChild(const std::string & name) const327 inline bool CaseTreeNode::hasChild (const std::string& name) const
328 {
329 	return findChildNdx(name) != NOT_FOUND;
330 }
331 
getChild(const std::string & name) const332 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
333 {
334 	const int ndx = findChildNdx(name);
335 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
336 }
337 
getChild(const std::string & name)338 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
339 {
340 	const int ndx = findChildNdx(name);
341 	return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
342 }
343 
getCurrentComponentLen(const char * path)344 static int getCurrentComponentLen (const char* path)
345 {
346 	int ndx = 0;
347 	for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
348 	return ndx;
349 }
350 
findNode(const CaseTreeNode * root,const char * path)351 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
352 {
353 	const CaseTreeNode*	curNode		= root;
354 	const char*			curPath		= path;
355 	int					curLen		= getCurrentComponentLen(curPath);
356 
357 	for (;;)
358 	{
359 		curNode = curNode->getChild(std::string(curPath, curPath+curLen));
360 
361 		if (!curNode)
362 			break;
363 
364 		curPath	+= curLen;
365 
366 		if (curPath[0] == 0)
367 			break;
368 		else
369 		{
370 			DE_ASSERT(curPath[0] == '.');
371 			curPath		+= 1;
372 			curLen		 = getCurrentComponentLen(curPath);
373 		}
374 	}
375 
376 	return curNode;
377 }
378 
parseCaseTrie(CaseTreeNode * root,std::istream & in)379 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
380 {
381 	vector<CaseTreeNode*>	nodeStack;
382 	string					curName;
383 	bool					expectNode		= true;
384 
385 	if (in.get() != '{')
386 		throw std::invalid_argument("Malformed case trie");
387 
388 	nodeStack.push_back(root);
389 
390 	while (!nodeStack.empty())
391 	{
392 		const int	curChr	= in.get();
393 
394 		if (curChr == std::char_traits<char>::eof() || curChr == 0)
395 			throw std::invalid_argument("Unterminated case tree");
396 
397 		if (curChr == '{' || curChr == ',' || curChr == '}')
398 		{
399 			if (!curName.empty() && expectNode)
400 			{
401 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
402 
403 				try
404 				{
405 					nodeStack.back()->addChild(newChild);
406 				}
407 				catch (...)
408 				{
409 					delete newChild;
410 					throw;
411 				}
412 
413 				if (curChr == '{')
414 					nodeStack.push_back(newChild);
415 
416 				curName.clear();
417 			}
418 			else if (curName.empty() == expectNode)
419 				throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
420 
421 			if (curChr == '}')
422 			{
423 				expectNode = false;
424 				nodeStack.pop_back();
425 
426 				// consume trailing new line
427 				if (nodeStack.empty())
428 				{
429 					if (in.peek() == '\r')
430 					  in.get();
431 					if (in.peek() == '\n')
432 					  in.get();
433 				}
434 			}
435 			else
436 				expectNode = true;
437 		}
438 		else if (isValidTestCaseNameChar((char)curChr))
439 			curName += (char)curChr;
440 		else
441 			throw std::invalid_argument("Illegal character in node name");
442 	}
443 }
444 
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates)445 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates)
446 {
447 	// \note Algorithm assumes that cases are sorted by groups, but will
448 	//		 function fine, albeit more slowly, if that is not the case.
449 	vector<CaseTreeNode*>	nodeStack;
450 	int						stackPos	= 0;
451 	string					curName;
452 
453 	nodeStack.resize(8, DE_NULL);
454 
455 	nodeStack[0] = root;
456 
457 	for (;;)
458 	{
459 		const int	curChr	= in.get();
460 
461 		if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
462 		{
463 			if (curName.empty())
464 				throw std::invalid_argument("Empty test case name");
465 
466 			if (!nodeStack[stackPos]->hasChild(curName))
467 			{
468 				CaseTreeNode* const newChild = new CaseTreeNode(curName);
469 
470 				try
471 				{
472 					nodeStack[stackPos]->addChild(newChild);
473 				}
474 				catch (...)
475 				{
476 					delete newChild;
477 					throw;
478 				}
479 			}
480 			else if (reportDuplicates)
481 				throw std::invalid_argument("Duplicate test case");
482 
483 			curName.clear();
484 			stackPos = 0;
485 
486 			if (curChr == '\r' && in.peek() == '\n')
487 				in.get();
488 
489 			{
490 				const int nextChr = in.peek();
491 
492 				if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
493 					break;
494 			}
495 		}
496 		else if (curChr == '.')
497 		{
498 			if (curName.empty())
499 				throw std::invalid_argument("Empty test group name");
500 
501 			if ((int)nodeStack.size() <= stackPos+1)
502 				nodeStack.resize(nodeStack.size()*2, DE_NULL);
503 
504 			if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
505 			{
506 				CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
507 
508 				if (!curGroup)
509 				{
510 					curGroup = new CaseTreeNode(curName);
511 
512 					try
513 					{
514 						nodeStack[stackPos]->addChild(curGroup);
515 					}
516 					catch (...)
517 					{
518 						delete curGroup;
519 						throw;
520 					}
521 				}
522 
523 				nodeStack[stackPos+1] = curGroup;
524 
525 				if ((int)nodeStack.size() > stackPos+2)
526 					nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
527 			}
528 
529 			DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
530 
531 			curName.clear();
532 			stackPos += 1;
533 		}
534 		else if (isValidTestCaseNameChar((char)curChr))
535 			curName += (char)curChr;
536 		else
537 			throw std::invalid_argument("Illegal character in test case name");
538 	}
539 }
540 
parseCaseList(std::istream & in)541 static CaseTreeNode* parseCaseList (std::istream& in)
542 {
543 	CaseTreeNode* const root = new CaseTreeNode("");
544 	try
545 	{
546 		if (in.peek() == '{')
547 			parseCaseTrie(root, in);
548 		else
549 			parseCaseList(root, in, true);
550 
551 		{
552 			const int curChr = in.get();
553 			if (curChr != std::char_traits<char>::eof() && curChr != 0)
554 				throw std::invalid_argument("Trailing characters at end of case list");
555 		}
556 
557 		return root;
558 	}
559 	catch (...)
560 	{
561 		delete root;
562 		throw;
563 	}
564 }
565 
566 class CasePaths
567 {
568 public:
569 	CasePaths(const string& pathList);
570 	CasePaths(const vector<string>& pathList);
571 	bool					matches(const string& caseName, bool allowPrefix = false) const;
572 
573 private:
574 	const vector<string>	m_casePatterns;
575 };
576 
CasePaths(const string & pathList)577 CasePaths::CasePaths (const string& pathList)
578 	: m_casePatterns(de::splitString(pathList, ','))
579 {
580 }
581 
CasePaths(const vector<string> & pathList)582 CasePaths::CasePaths(const vector<string>& pathList)
583 	: m_casePatterns(pathList)
584 {
585 }
586 
587 // Match a single path component against a pattern component that may contain *-wildcards.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)588 bool matchWildcards(string::const_iterator	patternStart,
589 					string::const_iterator	patternEnd,
590 					string::const_iterator	pathStart,
591 					string::const_iterator	pathEnd,
592 					bool					allowPrefix)
593 {
594 	string::const_iterator	pattern	= patternStart;
595 	string::const_iterator	path	= pathStart;
596 
597 	while (pattern != patternEnd && path != pathEnd && *pattern == *path)
598 	{
599 		++pattern;
600 		++path;
601 	}
602 
603 	if (pattern == patternEnd)
604 		return (path == pathEnd);
605 	else if (*pattern == '*')
606 	{
607 		for (; path != pathEnd; ++path)
608 		{
609 			if (matchWildcards(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
610 				return true;
611 		}
612 
613 		if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
614 			return true;
615 	}
616 	else if (path == pathEnd && allowPrefix)
617 		return true;
618 
619 	return false;
620 }
621 
622 #if defined(TCU_HIERARCHICAL_CASEPATHS)
623 // Match a list of pattern components to a list of path components. A pattern
624 // component may contain *-wildcards. A pattern component "**" matches zero or
625 // more whole path components.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)626 static bool patternMatches(vector<string>::const_iterator	patternStart,
627 						   vector<string>::const_iterator	patternEnd,
628 						   vector<string>::const_iterator	pathStart,
629 						   vector<string>::const_iterator	pathEnd,
630 						   bool								allowPrefix)
631 {
632 	vector<string>::const_iterator	pattern	= patternStart;
633 	vector<string>::const_iterator	path	= pathStart;
634 
635 	while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
636 		   (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
637 												path->begin(), path->end(), false)))
638 	{
639 		++pattern;
640 		++path;
641 	}
642 
643 	if (path == pathEnd && (allowPrefix || pattern == patternEnd))
644 		return true;
645 	else if (pattern != patternEnd && *pattern == "**")
646 	{
647 		for (; path != pathEnd; ++path)
648 			if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
649 				return true;
650 		if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
651 			return true;
652 	}
653 
654 	return false;
655 }
656 #endif
657 
matches(const string & caseName,bool allowPrefix) const658 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
659 {
660 	const vector<string> components = de::splitString(caseName, '.');
661 
662 	for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
663 	{
664 #if defined(TCU_HIERARCHICAL_CASEPATHS)
665 		const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
666 
667 		if (patternMatches(patternComponents.begin(), patternComponents.end(),
668 						   components.begin(), components.end(), allowPrefix))
669 			return true;
670 #else
671 		if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
672 						   caseName.begin(), caseName.end(), allowPrefix))
673 			return true;
674 #endif
675 	}
676 
677 	return false;
678 }
679 
680 /*--------------------------------------------------------------------*//*!
681  * \brief Construct command line
682  * \note CommandLine is not fully initialized until parse() has been called.
683  *//*--------------------------------------------------------------------*/
CommandLine(void)684 CommandLine::CommandLine (void)
685 	: m_logFlags	(0)
686 {
687 }
688 
689 /*--------------------------------------------------------------------*//*!
690  * \brief Construct command line from standard argc, argv pair.
691  *
692  * Calls parse() with given arguments
693  * \param archive application's assets
694  * \param argc Number of arguments
695  * \param argv Command line arguments
696  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)697 CommandLine::CommandLine (int argc, const char* const* argv)
698 	: m_logFlags	(0)
699 {
700 	if (argc > 1)
701 	{
702 		int loop = 1;		// skip application name
703 		while (true)
704 		{
705 			m_initialCmdLine += std::string(argv[loop++]);
706 			if (loop >= argc)
707 				break;
708 			m_initialCmdLine += " ";
709 		}
710 	}
711 
712 	if (!parse(argc, argv))
713 		throw Exception("Failed to parse command line");
714 }
715 
716 /*--------------------------------------------------------------------*//*!
717  * \brief Construct command line from string.
718  *
719  * Calls parse() with given argument.
720  * \param archive application's assets
721  * \param cmdLine Full command line string.
722  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)723 CommandLine::CommandLine (const std::string& cmdLine)
724 	: m_initialCmdLine	(cmdLine)
725 {
726 	if (!parse(cmdLine))
727 		throw Exception("Failed to parse command line");
728 }
729 
~CommandLine(void)730 CommandLine::~CommandLine (void)
731 {
732 }
733 
clear(void)734 void CommandLine::clear (void)
735 {
736 	m_cmdLine.clear();
737 	m_logFlags = 0;
738 }
739 
getCommandLine(void) const740 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
741 {
742 	return m_cmdLine;
743 }
744 
getInitialCmdLine(void) const745 const std::string& CommandLine::getInitialCmdLine(void) const
746 {
747 	return m_initialCmdLine;
748 }
749 
registerExtendedOptions(de::cmdline::Parser & parser)750 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
751 {
752 	DE_UNREF(parser);
753 }
754 
755 /*--------------------------------------------------------------------*//*!
756  * \brief Parse command line from standard argc, argv pair.
757  * \note parse() must be called exactly once.
758  * \param argc Number of arguments
759  * \param argv Command line arguments
760  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)761 bool CommandLine::parse (int argc, const char* const* argv)
762 {
763 	DebugOutStreambuf	sbuf;
764 	std::ostream		debugOut	(&sbuf);
765 	de::cmdline::Parser	parser;
766 
767 	opt::registerOptions(parser);
768 	opt::registerLegacyOptions(parser);
769 	registerExtendedOptions(parser);
770 
771 	clear();
772 
773 	if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
774 	{
775 		debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
776 		parser.help(debugOut);
777 
778 		clear();
779 		return false;
780 	}
781 
782 	if (!m_cmdLine.getOption<opt::LogImages>())
783 		m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
784 
785 	if (!m_cmdLine.getOption<opt::LogShaderSources>())
786 		m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
787 
788 	if (!m_cmdLine.getOption<opt::LogFlush>())
789 		m_logFlags |= QP_TEST_LOG_NO_FLUSH;
790 
791 	if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
792 		(m_cmdLine.hasOption<opt::CaseList>()?1:0) +
793 		(m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
794 		(m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
795 		(m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
796 	{
797 		debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
798 		clear();
799 		return false;
800 	}
801 
802 	return true;
803 }
804 
805 /*--------------------------------------------------------------------*//*!
806  * \brief Parse command line from string.
807  * \note parse() must be called exactly once.
808  * \param cmdLine Full command line string.
809  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)810 bool CommandLine::parse (const std::string& cmdLine)
811 {
812 	deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
813 	if (!parsedCmdLine)
814 		throw std::bad_alloc();
815 
816 	bool isOk = false;
817 	try
818 	{
819 		isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
820 	}
821 	catch (...)
822 	{
823 		deCommandLine_destroy(parsedCmdLine);
824 		throw;
825 	}
826 
827 	deCommandLine_destroy(parsedCmdLine);
828 	return isOk;
829 }
830 
getLogFileName(void) const831 const char*				CommandLine::getLogFileName					(void) const	{ return m_cmdLine.getOption<opt::LogFilename>().c_str();					}
getLogFlags(void) const832 deUint32				CommandLine::getLogFlags					(void) const	{ return m_logFlags;														}
getRunMode(void) const833 RunMode					CommandLine::getRunMode						(void) const	{ return m_cmdLine.getOption<opt::RunMode>();								}
getCaseListExportFile(void) const834 const char*				CommandLine::getCaseListExportFile			(void) const	{ return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();			}
getVisibility(void) const835 WindowVisibility		CommandLine::getVisibility					(void) const	{ return m_cmdLine.getOption<opt::Visibility>();							}
isWatchDogEnabled(void) const836 bool					CommandLine::isWatchDogEnabled				(void) const	{ return m_cmdLine.getOption<opt::WatchDog>();								}
isCrashHandlingEnabled(void) const837 bool					CommandLine::isCrashHandlingEnabled			(void) const	{ return m_cmdLine.getOption<opt::CrashHandler>();							}
getBaseSeed(void) const838 int						CommandLine::getBaseSeed					(void) const	{ return m_cmdLine.getOption<opt::BaseSeed>();								}
getTestIterationCount(void) const839 int						CommandLine::getTestIterationCount			(void) const	{ return m_cmdLine.getOption<opt::TestIterationCount>();					}
getSurfaceWidth(void) const840 int						CommandLine::getSurfaceWidth				(void) const	{ return m_cmdLine.getOption<opt::SurfaceWidth>();							}
getSurfaceHeight(void) const841 int						CommandLine::getSurfaceHeight				(void) const	{ return m_cmdLine.getOption<opt::SurfaceHeight>();							}
getSurfaceType(void) const842 SurfaceType				CommandLine::getSurfaceType					(void) const	{ return m_cmdLine.getOption<opt::SurfaceType>();							}
getScreenRotation(void) const843 ScreenRotation			CommandLine::getScreenRotation				(void) const	{ return m_cmdLine.getOption<opt::ScreenRotation>();						}
getGLConfigId(void) const844 int						CommandLine::getGLConfigId					(void) const	{ return m_cmdLine.getOption<opt::GLConfigID>();							}
getCLPlatformId(void) const845 int						CommandLine::getCLPlatformId				(void) const	{ return m_cmdLine.getOption<opt::CLPlatformID>();							}
getCLDeviceIds(void) const846 const std::vector<int>&	CommandLine::getCLDeviceIds					(void) const	{ return m_cmdLine.getOption<opt::CLDeviceIDs>();							}
getVKDeviceId(void) const847 int						CommandLine::getVKDeviceId					(void) const	{ return m_cmdLine.getOption<opt::VKDeviceID>();							}
getVKDeviceGroupId(void) const848 int						CommandLine::getVKDeviceGroupId				(void) const	{ return m_cmdLine.getOption<opt::VKDeviceGroupID>();						}
isValidationEnabled(void) const849 bool					CommandLine::isValidationEnabled			(void) const	{ return m_cmdLine.getOption<opt::Validation>();							}
printValidationErrors(void) const850 bool					CommandLine::printValidationErrors			(void) const	{ return m_cmdLine.getOption<opt::PrintValidationErrors>();					}
isOutOfMemoryTestEnabled(void) const851 bool					CommandLine::isOutOfMemoryTestEnabled		(void) const	{ return m_cmdLine.getOption<opt::TestOOM>();								}
isShadercacheEnabled(void) const852 bool					CommandLine::isShadercacheEnabled			(void) const	{ return m_cmdLine.getOption<opt::ShaderCache>();							}
getShaderCacheFilename(void) const853 const char*				CommandLine::getShaderCacheFilename			(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();			}
isShaderCacheTruncateEnabled(void) const854 bool					CommandLine::isShaderCacheTruncateEnabled	(void) const	{ return m_cmdLine.getOption<opt::ShaderCacheTruncate>();					}
getOptimizationRecipe(void) const855 int						CommandLine::getOptimizationRecipe			(void) const	{ return m_cmdLine.getOption<opt::Optimization>();							}
isSpirvOptimizationEnabled(void) const856 bool					CommandLine::isSpirvOptimizationEnabled		(void) const	{ return m_cmdLine.getOption<opt::OptimizeSpirv>();							}
isRenderDocEnabled(void) const857 bool					CommandLine::isRenderDocEnabled				(void) const	{ return m_cmdLine.getOption<opt::RenderDoc>();								}
getWaiverFileName(void) const858 const char*				CommandLine::getWaiverFileName				(void) const	{ return m_cmdLine.getOption<opt::WaiverFile>().c_str();					}
getCaseFraction(void) const859 const std::vector<int>&	CommandLine::getCaseFraction				(void) const	{ return m_cmdLine.getOption<opt::CaseFraction>();							}
getCaseFractionMandatoryTests(void) const860 const char*				CommandLine::getCaseFractionMandatoryTests	(void) const	{ return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str();	}
getArchiveDir(void) const861 const char*				CommandLine::getArchiveDir					(void) const	{ return m_cmdLine.getOption<opt::ArchiveDir>().c_str();					}
getRunnerType(void) const862 tcu::TestRunnerType		CommandLine::getRunnerType					(void) const	{ return m_cmdLine.getOption<opt::RunnerType>();							}
863 
getGLContextType(void) const864 const char* CommandLine::getGLContextType (void) const
865 {
866 	if (m_cmdLine.hasOption<opt::GLContextType>())
867 		return m_cmdLine.getOption<opt::GLContextType>().c_str();
868 	else
869 		return DE_NULL;
870 }
getGLConfigName(void) const871 const char* CommandLine::getGLConfigName (void) const
872 {
873 	if (m_cmdLine.hasOption<opt::GLConfigName>())
874 		return m_cmdLine.getOption<opt::GLConfigName>().c_str();
875 	else
876 		return DE_NULL;
877 }
878 
getGLContextFlags(void) const879 const char* CommandLine::getGLContextFlags (void) const
880 {
881 	if (m_cmdLine.hasOption<opt::GLContextFlags>())
882 		return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
883 	else
884 		return DE_NULL;
885 }
886 
getCLBuildOptions(void) const887 const char* CommandLine::getCLBuildOptions (void) const
888 {
889 	if (m_cmdLine.hasOption<opt::CLBuildOptions>())
890 		return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
891 	else
892 		return DE_NULL;
893 }
894 
getEGLDisplayType(void) const895 const char* CommandLine::getEGLDisplayType (void) const
896 {
897 	if (m_cmdLine.hasOption<opt::EGLDisplayType>())
898 		return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
899 	else
900 		return DE_NULL;
901 }
902 
getEGLWindowType(void) const903 const char* CommandLine::getEGLWindowType (void) const
904 {
905 	if (m_cmdLine.hasOption<opt::EGLWindowType>())
906 		return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
907 	else
908 		return DE_NULL;
909 }
910 
getEGLPixmapType(void) const911 const char* CommandLine::getEGLPixmapType (void) const
912 {
913 	if (m_cmdLine.hasOption<opt::EGLPixmapType>())
914 		return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
915 	else
916 		return DE_NULL;
917 }
918 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)919 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
920 {
921 	const CaseTreeNode* node = findNode(root, groupPath);
922 	return node && node->hasChildren();
923 }
924 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)925 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
926 {
927 	const CaseTreeNode* node = findNode(root, casePath);
928 	return node && !node->hasChildren();
929 }
930 
createCaseListFilter(const tcu::Archive & archive) const931 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
932 {
933 	return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
934 }
935 
checkTestGroupName(const char * groupName) const936 bool CaseListFilter::checkTestGroupName (const char* groupName) const
937 {
938 	bool result = false;
939 	if (m_casePaths)
940 		result = m_casePaths->matches(groupName, true);
941 	else if (m_caseTree)
942 		result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
943 	else
944 		return true;
945 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
946 		result = m_caseFractionMandatoryTests->matches(groupName, true);
947 	return result;
948 }
949 
checkTestCaseName(const char * caseName) const950 bool CaseListFilter::checkTestCaseName (const char* caseName) const
951 {
952 	bool result = false;
953 	if (m_casePaths)
954 		result = m_casePaths->matches(caseName, false);
955 	else if (m_caseTree)
956 		result = tcu::checkTestCaseName(m_caseTree, caseName);
957 	else
958 		return true;
959 	if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
960 		result = m_caseFractionMandatoryTests->matches(caseName, false);
961 	return result;
962 }
963 
checkCaseFraction(int i,const std::string & testCaseName) const964 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
965 {
966 	return	m_caseFraction.size() != 2 ||
967 		((i % m_caseFraction[1]) == m_caseFraction[0]) ||
968 		(m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
969 }
970 
CaseListFilter(void)971 CaseListFilter::CaseListFilter (void)
972 	: m_caseTree	(DE_NULL)
973 	, m_runnerType	(tcu::RUNNERTYPE_ANY)
974 {
975 }
976 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)977 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
978 	: m_caseTree	(DE_NULL)
979 	, m_runnerType	(cmdLine.getOption<opt::RunnerType>())
980 {
981 	if (cmdLine.hasOption<opt::CaseList>())
982 	{
983 		std::istringstream str(cmdLine.getOption<opt::CaseList>());
984 
985 		m_caseTree = parseCaseList(str);
986 	}
987 	else if (cmdLine.hasOption<opt::CaseListFile>())
988 	{
989 		std::ifstream in(cmdLine.getOption<opt::CaseListFile>().c_str(), std::ios_base::binary);
990 
991 		if (!in.is_open() || !in.good())
992 			throw Exception("Failed to open case list file '" + cmdLine.getOption<opt::CaseListFile>() + "'");
993 
994 		m_caseTree = parseCaseList(in);
995 	}
996 	else if (cmdLine.hasOption<opt::CaseListResource>())
997 	{
998 		// \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
999 		//						   istream adaptor for tcu::Resource.
1000 		de::UniquePtr<Resource>	caseListResource	(archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1001 		const int				bufferSize			= caseListResource->getSize();
1002 		std::vector<char>		buffer				((size_t)bufferSize);
1003 
1004 		if (buffer.empty())
1005 			throw Exception("Empty case list resource");
1006 
1007 		caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1008 
1009 		{
1010 			std::istringstream	in	(std::string(&buffer[0], (size_t)bufferSize));
1011 
1012 			m_caseTree = parseCaseList(in);
1013 		}
1014 	}
1015 	else if (cmdLine.getOption<opt::StdinCaseList>())
1016 	{
1017 		m_caseTree = parseCaseList(std::cin);
1018 	}
1019 	else if (cmdLine.hasOption<opt::CasePath>())
1020 		m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1021 
1022 	m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1023 
1024 	if (m_caseFraction.size() == 2 &&
1025 		(m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1026 		throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1027 
1028 	if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1029 		throw Exception("Invalid case fraction. Must have two components.");
1030 
1031 	if (m_caseFraction.size() == 2)
1032 	{
1033 		std::string					caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1034 
1035 		if (!caseFractionMandatoryTestsFilename.empty())
1036 		{
1037 			std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1038 			if (!fileStream.is_open() || !fileStream.good())
1039 				throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1040 
1041 			std::vector<std::string>	cfPaths;
1042 			std::string					line;
1043 
1044 			while (std::getline(fileStream, line))
1045 			{
1046 				line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1047 				cfPaths.push_back(line);
1048 			}
1049 			if (!cfPaths.empty())
1050 			{
1051 				m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1052 				if (m_caseTree != DE_NULL)
1053 				{
1054 					fileStream.clear();
1055 					fileStream.seekg(0, fileStream.beg);
1056 					parseCaseList(m_caseTree, fileStream, false);
1057 				}
1058 			}
1059 		}
1060 	}
1061 }
1062 
~CaseListFilter(void)1063 CaseListFilter::~CaseListFilter (void)
1064 {
1065 	delete m_caseTree;
1066 }
1067 
1068 } // tcu
1069