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(LogDecompiledSpirv, bool);
89 DE_DECLARE_COMMAND_LINE_OPT(LogEmptyLoginfo, bool);
90 DE_DECLARE_COMMAND_LINE_OPT(TestOOM, bool);
91 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir, std::string);
92 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID, int);
93 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID, int);
94 DE_DECLARE_COMMAND_LINE_OPT(LogFlush, bool);
95 DE_DECLARE_COMMAND_LINE_OPT(Validation, bool);
96 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors, bool);
97 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache, bool);
98 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename, std::string);
99 DE_DECLARE_COMMAND_LINE_OPT(Optimization, int);
100 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv, bool);
101 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate, bool);
102 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc, bool);
103 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction, std::vector<int>);
104 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests, std::string);
105 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile, std::string);
106 DE_DECLARE_COMMAND_LINE_OPT(RunnerType, tcu::TestRunnerType);
107 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail, bool);
108
parseIntList(const char * src,std::vector<int> * dst)109 static void parseIntList (const char* src, std::vector<int>* dst)
110 {
111 std::istringstream str (src);
112 std::string val;
113
114 while (std::getline(str, val, ','))
115 {
116 int intVal = 0;
117 de::cmdline::parseType(val.c_str(), &intVal);
118 dst->push_back(intVal);
119 }
120 }
121
registerOptions(de::cmdline::Parser & parser)122 void registerOptions (de::cmdline::Parser& parser)
123 {
124 using de::cmdline::Option;
125 using de::cmdline::NamedValue;
126
127 static const NamedValue<bool> s_enableNames[] =
128 {
129 { "enable", true },
130 { "disable", false }
131 };
132 static const NamedValue<tcu::RunMode> s_runModes[] =
133 {
134 { "execute", RUNMODE_EXECUTE },
135 { "xml-caselist", RUNMODE_DUMP_XML_CASELIST },
136 { "txt-caselist", RUNMODE_DUMP_TEXT_CASELIST },
137 { "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST },
138 { "amber-verify", RUNMODE_VERIFY_AMBER_COHERENCY}
139 };
140 static const NamedValue<WindowVisibility> s_visibilites[] =
141 {
142 { "windowed", WINDOWVISIBILITY_WINDOWED },
143 { "fullscreen", WINDOWVISIBILITY_FULLSCREEN },
144 { "hidden", WINDOWVISIBILITY_HIDDEN }
145 };
146 static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
147 {
148 { "window", SURFACETYPE_WINDOW },
149 { "pixmap", SURFACETYPE_OFFSCREEN_NATIVE },
150 { "pbuffer", SURFACETYPE_OFFSCREEN_GENERIC },
151 { "fbo", SURFACETYPE_FBO }
152 };
153 static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
154 {
155 { "unspecified", SCREENROTATION_UNSPECIFIED },
156 { "0", SCREENROTATION_0 },
157 { "90", SCREENROTATION_90 },
158 { "180", SCREENROTATION_180 },
159 { "270", SCREENROTATION_270 }
160 };
161 static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
162 {
163 { "any", tcu::RUNNERTYPE_ANY },
164 { "none", tcu::RUNNERTYPE_NONE },
165 { "amber", tcu::RUNNERTYPE_AMBER },
166 };
167
168 parser
169 << Option<CasePath> ("n", "deqp-case", "Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
170 << Option<CaseList> (DE_NULL, "deqp-caselist", "Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
171 << Option<CaseListFile> (DE_NULL, "deqp-caselist-file", "Read case list (in trie format) from given file")
172 << Option<CaseListResource> (DE_NULL, "deqp-caselist-resource", "Read case list (in trie format) from given file located application's assets")
173 << Option<StdinCaseList> (DE_NULL, "deqp-stdin-caselist", "Read case list (in trie format) from stdin")
174 << Option<LogFilename> (DE_NULL, "deqp-log-filename", "Write test results to given file", "TestResults.qpa")
175 << Option<RunMode> (DE_NULL, "deqp-runmode", "Execute tests, write list of test cases into a file, or verify amber capability coherency",
176 s_runModes, "execute")
177 << Option<ExportFilenamePattern> (DE_NULL, "deqp-caselist-export-file", "Set the target file name pattern for caselist export", "${packageName}-cases.${typeExtension}")
178 << Option<WatchDog> (DE_NULL, "deqp-watchdog", "Enable test watchdog", s_enableNames, "disable")
179 << Option<CrashHandler> (DE_NULL, "deqp-crashhandler", "Enable crash handling", s_enableNames, "disable")
180 << Option<BaseSeed> (DE_NULL, "deqp-base-seed", "Base seed for test cases that use randomization", "0")
181 << Option<TestIterationCount> (DE_NULL, "deqp-test-iteration-count", "Iteration count for cases that support variable number of iterations", "0")
182 << Option<Visibility> (DE_NULL, "deqp-visibility", "Default test window visibility", s_visibilites, "windowed")
183 << Option<SurfaceWidth> (DE_NULL, "deqp-surface-width", "Use given surface width if possible", "-1")
184 << Option<SurfaceHeight> (DE_NULL, "deqp-surface-height", "Use given surface height if possible", "-1")
185 << Option<SurfaceType> (DE_NULL, "deqp-surface-type", "Use given surface type", s_surfaceTypes, "window")
186 << Option<ScreenRotation> (DE_NULL, "deqp-screen-rotation", "Screen rotation for platforms that support it", s_screenRotations, "0")
187 << Option<GLContextType> (DE_NULL, "deqp-gl-context-type", "OpenGL context type for platforms that support multiple")
188 << Option<GLConfigID> (DE_NULL, "deqp-gl-config-id", "OpenGL (ES) render config ID (EGL config id on EGL platforms)", "-1")
189 << Option<GLConfigName> (DE_NULL, "deqp-gl-config-name", "Symbolic OpenGL (ES) render config name")
190 << Option<GLContextFlags> (DE_NULL, "deqp-gl-context-flags", "OpenGL context flags (comma-separated, supports debug and robust)")
191 << Option<CLPlatformID> (DE_NULL, "deqp-cl-platform-id", "Execute tests on given OpenCL platform (IDs start from 1)", "1")
192 << Option<CLDeviceIDs> (DE_NULL, "deqp-cl-device-ids", "Execute tests on given CL devices (comma-separated, IDs start from 1)", parseIntList, "")
193 << Option<CLBuildOptions> (DE_NULL, "deqp-cl-build-options", "Extra build options for OpenCL compiler")
194 << Option<EGLDisplayType> (DE_NULL, "deqp-egl-display-type", "EGL native display type")
195 << Option<EGLWindowType> (DE_NULL, "deqp-egl-window-type", "EGL native window type")
196 << Option<EGLPixmapType> (DE_NULL, "deqp-egl-pixmap-type", "EGL native pixmap type")
197 << Option<VKDeviceID> (DE_NULL, "deqp-vk-device-id", "Vulkan device ID (IDs start from 1)", "1")
198 << Option<VKDeviceGroupID> (DE_NULL, "deqp-vk-device-group-id", "Vulkan device Group ID (IDs start from 1)", "1")
199 << Option<LogImages> (DE_NULL, "deqp-log-images", "Enable or disable logging of result images", s_enableNames, "enable")
200 << Option<LogShaderSources> (DE_NULL, "deqp-log-shader-sources", "Enable or disable logging of shader sources", s_enableNames, "enable")
201 << Option<LogDecompiledSpirv> (DE_NULL, "deqp-log-decompiled-spirv", "Enable or disable logging of decompiled spir-v", s_enableNames, "enable")
202 << Option<LogEmptyLoginfo> (DE_NULL, "deqp-log-empty-loginfo", "Logging of empty shader compile/link log info", s_enableNames, "enable")
203 << Option<TestOOM> (DE_NULL, "deqp-test-oom", "Run tests that exhaust memory on purpose", s_enableNames, TEST_OOM_DEFAULT)
204 << Option<ArchiveDir> (DE_NULL, "deqp-archive-dir", "Path to test resource files", ".")
205 << Option<LogFlush> (DE_NULL, "deqp-log-flush", "Enable or disable log file fflush", s_enableNames, "enable")
206 << Option<Validation> (DE_NULL, "deqp-validation", "Enable or disable test case validation", s_enableNames, "disable")
207 << Option<PrintValidationErrors> (DE_NULL, "deqp-print-validation-errors", "Print validation errors to standard error")
208 << Option<Optimization> (DE_NULL, "deqp-optimization-recipe", "Shader optimization recipe (0=disabled, 1=performance, 2=size)", "0")
209 << Option<OptimizeSpirv> (DE_NULL, "deqp-optimize-spirv", "Apply optimization to spir-v shaders as well", s_enableNames, "disable")
210 << Option<ShaderCache> (DE_NULL, "deqp-shadercache", "Enable or disable shader cache", s_enableNames, "enable")
211 << Option<ShaderCacheFilename> (DE_NULL, "deqp-shadercache-filename", "Write shader cache to given file", "shadercache.bin")
212 << Option<ShaderCacheTruncate> (DE_NULL, "deqp-shadercache-truncate", "Truncate shader cache before running tests", s_enableNames, "enable")
213 << Option<RenderDoc> (DE_NULL, "deqp-renderdoc", "Enable RenderDoc frame markers", s_enableNames, "disable")
214 << Option<CaseFraction> (DE_NULL, "deqp-fraction", "Run a fraction of the test cases (e.g. N,M means run group%M==N)", parseIntList, "")
215 << Option<CaseFractionMandatoryTests> (DE_NULL, "deqp-fraction-mandatory-caselist-file", "Case list file that must be run for each fraction", "")
216 << Option<WaiverFile> (DE_NULL, "deqp-waiver-file", "Read waived tests from given file", "")
217 << Option<RunnerType> (DE_NULL, "deqp-runner-type", "Filter test cases based on runner", s_runnerTypes, "any")
218 << Option<TerminateOnFail> (DE_NULL, "deqp-terminate-on-fail", "Terminate the run on first failure", s_enableNames, "disable");
219 }
220
registerLegacyOptions(de::cmdline::Parser & parser)221 void registerLegacyOptions (de::cmdline::Parser& parser)
222 {
223 using de::cmdline::Option;
224
225 parser
226 << Option<GLConfigID> (DE_NULL, "deqp-egl-config-id", "Legacy name for --deqp-gl-config-id", "-1")
227 << Option<GLConfigName> (DE_NULL, "deqp-egl-config-name", "Legacy name for --deqp-gl-config-name");
228 }
229
230 } // opt
231
232 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
233 class DebugOutStreambuf : public std::streambuf
234 {
235 public:
236 DebugOutStreambuf (void);
237 ~DebugOutStreambuf (void);
238
239 protected:
240 std::streamsize xsputn (const char* s, std::streamsize count);
241 int overflow (int ch = -1);
242
243 private:
244 void flushLine (void);
245
246 std::ostringstream m_curLine;
247 };
248
DebugOutStreambuf(void)249 DebugOutStreambuf::DebugOutStreambuf (void)
250 {
251 }
252
~DebugOutStreambuf(void)253 DebugOutStreambuf::~DebugOutStreambuf (void)
254 {
255 if (m_curLine.tellp() != std::streampos(0))
256 flushLine();
257 }
258
xsputn(const char * s,std::streamsize count)259 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
260 {
261 for (std::streamsize pos = 0; pos < count; pos++)
262 {
263 m_curLine.put(s[pos]);
264
265 if (s[pos] == '\n')
266 flushLine();
267 }
268
269 return count;
270 }
271
overflow(int ch)272 int DebugOutStreambuf::overflow (int ch)
273 {
274 if (ch == -1)
275 return -1;
276 else
277 {
278 DE_ASSERT((ch & 0xff) == ch);
279 const char chVal = (char)(deUint8)(ch & 0xff);
280 return xsputn(&chVal, 1) == 1 ? ch : -1;
281 }
282 }
283
flushLine(void)284 void DebugOutStreambuf::flushLine (void)
285 {
286 qpPrint(m_curLine.str().c_str());
287 m_curLine.str("");
288 }
289
290 class CaseTreeNode
291 {
292 public:
CaseTreeNode(const std::string & name)293 CaseTreeNode (const std::string& name) : m_name(name) {}
294 ~CaseTreeNode (void);
295
getName(void) const296 const std::string& getName (void) const { return m_name; }
hasChildren(void) const297 bool hasChildren (void) const { return !m_children.empty(); }
298
299 bool hasChild (const std::string& name) const;
300 const CaseTreeNode* getChild (const std::string& name) const;
301 CaseTreeNode* getChild (const std::string& name);
302
addChild(CaseTreeNode * child)303 void addChild (CaseTreeNode* child) { m_children.push_back(child); }
304
305 private:
306 CaseTreeNode (const CaseTreeNode&);
307 CaseTreeNode& operator= (const CaseTreeNode&);
308
309 enum { NOT_FOUND = -1 };
310
311 // \todo [2014-10-30 pyry] Speed up with hash / sorting
312 int findChildNdx (const std::string& name) const;
313
314 std::string m_name;
315 std::vector<CaseTreeNode*> m_children;
316 };
317
~CaseTreeNode(void)318 CaseTreeNode::~CaseTreeNode (void)
319 {
320 for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
321 delete *i;
322 }
323
findChildNdx(const std::string & name) const324 int CaseTreeNode::findChildNdx (const std::string& name) const
325 {
326 for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
327 {
328 if (m_children[ndx]->getName() == name)
329 return ndx;
330 }
331 return NOT_FOUND;
332 }
333
hasChild(const std::string & name) const334 inline bool CaseTreeNode::hasChild (const std::string& name) const
335 {
336 return findChildNdx(name) != NOT_FOUND;
337 }
338
getChild(const std::string & name) const339 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
340 {
341 const int ndx = findChildNdx(name);
342 return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
343 }
344
getChild(const std::string & name)345 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
346 {
347 const int ndx = findChildNdx(name);
348 return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
349 }
350
getCurrentComponentLen(const char * path)351 static int getCurrentComponentLen (const char* path)
352 {
353 int ndx = 0;
354 for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
355 return ndx;
356 }
357
findNode(const CaseTreeNode * root,const char * path)358 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
359 {
360 const CaseTreeNode* curNode = root;
361 const char* curPath = path;
362 int curLen = getCurrentComponentLen(curPath);
363
364 for (;;)
365 {
366 curNode = curNode->getChild(std::string(curPath, curPath+curLen));
367
368 if (!curNode)
369 break;
370
371 curPath += curLen;
372
373 if (curPath[0] == 0)
374 break;
375 else
376 {
377 DE_ASSERT(curPath[0] == '.');
378 curPath += 1;
379 curLen = getCurrentComponentLen(curPath);
380 }
381 }
382
383 return curNode;
384 }
385
parseCaseTrie(CaseTreeNode * root,std::istream & in)386 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
387 {
388 vector<CaseTreeNode*> nodeStack;
389 string curName;
390 bool expectNode = true;
391
392 if (in.get() != '{')
393 throw std::invalid_argument("Malformed case trie");
394
395 nodeStack.push_back(root);
396
397 while (!nodeStack.empty())
398 {
399 const int curChr = in.get();
400
401 if (curChr == std::char_traits<char>::eof() || curChr == 0)
402 throw std::invalid_argument("Unterminated case tree");
403
404 if (curChr == '{' || curChr == ',' || curChr == '}')
405 {
406 if (!curName.empty() && expectNode)
407 {
408 CaseTreeNode* const newChild = new CaseTreeNode(curName);
409
410 try
411 {
412 nodeStack.back()->addChild(newChild);
413 }
414 catch (...)
415 {
416 delete newChild;
417 throw;
418 }
419
420 if (curChr == '{')
421 nodeStack.push_back(newChild);
422
423 curName.clear();
424 }
425 else if (curName.empty() == expectNode)
426 throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
427
428 if (curChr == '}')
429 {
430 expectNode = false;
431 nodeStack.pop_back();
432
433 // consume trailing new line
434 if (nodeStack.empty())
435 {
436 if (in.peek() == '\r')
437 in.get();
438 if (in.peek() == '\n')
439 in.get();
440 }
441 }
442 else
443 expectNode = true;
444 }
445 else if (isValidTestCaseNameChar((char)curChr))
446 curName += (char)curChr;
447 else
448 throw std::invalid_argument("Illegal character in node name");
449 }
450 }
451
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates)452 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates)
453 {
454 // \note Algorithm assumes that cases are sorted by groups, but will
455 // function fine, albeit more slowly, if that is not the case.
456 int stackPos = 0;
457 string curName;
458
459 for (;;)
460 {
461 const int curChr = in.get();
462
463 if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
464 {
465 if (curName.empty())
466 throw std::invalid_argument("Empty test case name");
467
468 if (!nodeStack[stackPos]->hasChild(curName))
469 {
470 CaseTreeNode* const newChild = new CaseTreeNode(curName);
471
472 try
473 {
474 nodeStack[stackPos]->addChild(newChild);
475 }
476 catch (...)
477 {
478 delete newChild;
479 throw;
480 }
481 }
482 else if (reportDuplicates)
483 throw std::invalid_argument("Duplicate test case");
484
485 curName.clear();
486 stackPos = 0;
487
488 if (curChr == '\r' && in.peek() == '\n')
489 in.get();
490
491 {
492 const int nextChr = in.peek();
493
494 if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
495 break;
496 }
497 }
498 else if (curChr == '.')
499 {
500 if (curName.empty())
501 throw std::invalid_argument("Empty test group name");
502
503 if ((int)nodeStack.size() <= stackPos+1)
504 nodeStack.resize(nodeStack.size()*2, DE_NULL);
505
506 if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
507 {
508 CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
509
510 if (!curGroup)
511 {
512 curGroup = new CaseTreeNode(curName);
513
514 try
515 {
516 nodeStack[stackPos]->addChild(curGroup);
517 }
518 catch (...)
519 {
520 delete curGroup;
521 throw;
522 }
523 }
524
525 nodeStack[stackPos+1] = curGroup;
526
527 if ((int)nodeStack.size() > stackPos+2)
528 nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
529 }
530
531 DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
532
533 curName.clear();
534 stackPos += 1;
535 }
536 else if (isValidTestCaseNameChar((char)curChr))
537 curName += (char)curChr;
538 else
539 throw std::invalid_argument("Illegal character in test case name");
540 }
541 }
542
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates)543 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates)
544 {
545 vector<CaseTreeNode*> nodeStack(8, root);
546 parseSimpleCaseList(nodeStack, in, reportDuplicates);
547 }
548
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates)549 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates)
550 {
551 // read whole file and remove all '\r'
552 std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
553 buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
554
555 vector<CaseTreeNode*> nodeStack(8, root);
556 std::stringstream namesStream(buffer);
557 std::string fileName;
558
559 while (std::getline(namesStream, fileName))
560 {
561 de::FilePath groupPath (fileName);
562 de::UniquePtr<Resource> groupResource (archive.getResource(groupPath.normalize().getPath()));
563 const int groupBufferSize (groupResource->getSize());
564 std::vector<char> groupBuffer (static_cast<size_t>(groupBufferSize));
565
566 groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
567 if (groupBuffer.empty())
568 throw Exception("Empty case list resource");
569
570 std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
571 parseSimpleCaseList(nodeStack, groupIn, reportDuplicates);
572 }
573 }
574
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)575 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
576 {
577 CaseTreeNode* const root = new CaseTreeNode("");
578 try
579 {
580 if (in.peek() == '{')
581 parseCaseTrie(root, in);
582 else
583 {
584 // if we are reading cases from file determine if we are
585 // reading group file or plain list of cases; this is done by
586 // reading single line and checking if it ends with ".txt"
587 bool readGroupFile = false;
588 if (path)
589 {
590 // read the first line and make sure it doesn't contain '\r'
591 std::string line;
592 std::getline(in, line);
593 line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
594
595 const std::string ending = ".txt";
596 readGroupFile = (line.length() > ending.length()) &&
597 std::equal(ending.rbegin(), ending.rend(), line.rbegin());
598
599 // move to the beginning of the file to parse first line too
600 in.seekg(0, in.beg);
601 }
602
603 if (readGroupFile)
604 parseGroupFile(root, in, archive, true);
605 else
606 parseCaseList(root, in, true);
607 }
608
609 {
610 const int curChr = in.get();
611 if (curChr != std::char_traits<char>::eof() && curChr != 0)
612 throw std::invalid_argument("Trailing characters at end of case list");
613 }
614
615 return root;
616 }
617 catch (...)
618 {
619 delete root;
620 throw;
621 }
622 }
623
624 class CasePaths
625 {
626 public:
627 CasePaths(const string& pathList);
628 CasePaths(const vector<string>& pathList);
629 bool matches(const string& caseName, bool allowPrefix = false) const;
630
631 private:
632 const vector<string> m_casePatterns;
633 };
634
CasePaths(const string & pathList)635 CasePaths::CasePaths (const string& pathList)
636 : m_casePatterns(de::splitString(pathList, ','))
637 {
638 }
639
CasePaths(const vector<string> & pathList)640 CasePaths::CasePaths(const vector<string>& pathList)
641 : m_casePatterns(pathList)
642 {
643 }
644
645 // 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)646 bool matchWildcards(string::const_iterator patternStart,
647 string::const_iterator patternEnd,
648 string::const_iterator pathStart,
649 string::const_iterator pathEnd,
650 bool allowPrefix)
651 {
652 string::const_iterator pattern = patternStart;
653 string::const_iterator path = pathStart;
654
655 while (pattern != patternEnd && path != pathEnd && *pattern == *path)
656 {
657 ++pattern;
658 ++path;
659 }
660
661 if (pattern == patternEnd)
662 return (path == pathEnd);
663 else if (*pattern == '*')
664 {
665 for (; path != pathEnd; ++path)
666 {
667 if (matchWildcards(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
668 return true;
669 }
670
671 if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
672 return true;
673 }
674 else if (path == pathEnd && allowPrefix)
675 return true;
676
677 return false;
678 }
679
680 #if defined(TCU_HIERARCHICAL_CASEPATHS)
681 // Match a list of pattern components to a list of path components. A pattern
682 // component may contain *-wildcards. A pattern component "**" matches zero or
683 // 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)684 static bool patternMatches(vector<string>::const_iterator patternStart,
685 vector<string>::const_iterator patternEnd,
686 vector<string>::const_iterator pathStart,
687 vector<string>::const_iterator pathEnd,
688 bool allowPrefix)
689 {
690 vector<string>::const_iterator pattern = patternStart;
691 vector<string>::const_iterator path = pathStart;
692
693 while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
694 (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
695 path->begin(), path->end(), false)))
696 {
697 ++pattern;
698 ++path;
699 }
700
701 if (path == pathEnd && (allowPrefix || pattern == patternEnd))
702 return true;
703 else if (pattern != patternEnd && *pattern == "**")
704 {
705 for (; path != pathEnd; ++path)
706 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
707 return true;
708 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
709 return true;
710 }
711
712 return false;
713 }
714 #endif
715
matches(const string & caseName,bool allowPrefix) const716 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
717 {
718 const vector<string> components = de::splitString(caseName, '.');
719
720 for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
721 {
722 #if defined(TCU_HIERARCHICAL_CASEPATHS)
723 const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
724
725 if (patternMatches(patternComponents.begin(), patternComponents.end(),
726 components.begin(), components.end(), allowPrefix))
727 return true;
728 #else
729 if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
730 caseName.begin(), caseName.end(), allowPrefix))
731 return true;
732 #endif
733 }
734
735 return false;
736 }
737
738 /*--------------------------------------------------------------------*//*!
739 * \brief Construct command line
740 * \note CommandLine is not fully initialized until parse() has been called.
741 *//*--------------------------------------------------------------------*/
CommandLine(void)742 CommandLine::CommandLine (void)
743 : m_logFlags (0)
744 {
745 }
746
747 /*--------------------------------------------------------------------*//*!
748 * \brief Construct command line from standard argc, argv pair.
749 *
750 * Calls parse() with given arguments
751 * \param archive application's assets
752 * \param argc Number of arguments
753 * \param argv Command line arguments
754 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)755 CommandLine::CommandLine (int argc, const char* const* argv)
756 : m_logFlags (0)
757 {
758 if (argc > 1)
759 {
760 int loop = 1; // skip application name
761 while (true)
762 {
763 m_initialCmdLine += std::string(argv[loop++]);
764 if (loop >= argc)
765 break;
766 m_initialCmdLine += " ";
767 }
768 }
769
770 if (!parse(argc, argv))
771 throw Exception("Failed to parse command line");
772 }
773
774 /*--------------------------------------------------------------------*//*!
775 * \brief Construct command line from string.
776 *
777 * Calls parse() with given argument.
778 * \param archive application's assets
779 * \param cmdLine Full command line string.
780 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)781 CommandLine::CommandLine (const std::string& cmdLine)
782 : m_initialCmdLine (cmdLine)
783 {
784 if (!parse(cmdLine))
785 throw Exception("Failed to parse command line");
786 }
787
~CommandLine(void)788 CommandLine::~CommandLine (void)
789 {
790 }
791
clear(void)792 void CommandLine::clear (void)
793 {
794 m_cmdLine.clear();
795 m_logFlags = 0;
796 }
797
getCommandLine(void) const798 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
799 {
800 return m_cmdLine;
801 }
802
getInitialCmdLine(void) const803 const std::string& CommandLine::getInitialCmdLine(void) const
804 {
805 return m_initialCmdLine;
806 }
807
registerExtendedOptions(de::cmdline::Parser & parser)808 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
809 {
810 DE_UNREF(parser);
811 }
812
813 /*--------------------------------------------------------------------*//*!
814 * \brief Parse command line from standard argc, argv pair.
815 * \note parse() must be called exactly once.
816 * \param argc Number of arguments
817 * \param argv Command line arguments
818 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)819 bool CommandLine::parse (int argc, const char* const* argv)
820 {
821 DebugOutStreambuf sbuf;
822 std::ostream debugOut (&sbuf);
823 de::cmdline::Parser parser;
824
825 opt::registerOptions(parser);
826 opt::registerLegacyOptions(parser);
827 registerExtendedOptions(parser);
828
829 clear();
830
831 if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
832 {
833 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
834 parser.help(debugOut);
835
836 clear();
837 return false;
838 }
839
840 if (!m_cmdLine.getOption<opt::LogImages>())
841 m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
842
843 if (!m_cmdLine.getOption<opt::LogShaderSources>())
844 m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
845
846 if (!m_cmdLine.getOption<opt::LogFlush>())
847 m_logFlags |= QP_TEST_LOG_NO_FLUSH;
848
849 if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
850 m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
851
852 if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
853 (m_cmdLine.hasOption<opt::CaseList>()?1:0) +
854 (m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
855 (m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
856 (m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
857 {
858 debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
859 clear();
860 return false;
861 }
862
863 if (m_cmdLine.getArgs().size() > 0)
864 {
865 debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
866
867 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
868 parser.help(debugOut);
869
870 clear();
871 return false;
872 }
873
874 return true;
875 }
876
877 /*--------------------------------------------------------------------*//*!
878 * \brief Parse command line from string.
879 * \note parse() must be called exactly once.
880 * \param cmdLine Full command line string.
881 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)882 bool CommandLine::parse (const std::string& cmdLine)
883 {
884 deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
885 if (!parsedCmdLine)
886 throw std::bad_alloc();
887
888 bool isOk = false;
889 try
890 {
891 isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
892 }
893 catch (...)
894 {
895 deCommandLine_destroy(parsedCmdLine);
896 throw;
897 }
898
899 deCommandLine_destroy(parsedCmdLine);
900 return isOk;
901 }
902
getLogFileName(void) const903 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const904 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const905 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const906 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const907 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const908 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const909 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const910 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const911 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const912 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const913 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const914 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const915 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const916 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const917 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const918 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const919 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
getVKDeviceGroupId(void) const920 int CommandLine::getVKDeviceGroupId (void) const { return m_cmdLine.getOption<opt::VKDeviceGroupID>(); }
isValidationEnabled(void) const921 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
printValidationErrors(void) const922 bool CommandLine::printValidationErrors (void) const { return m_cmdLine.getOption<opt::PrintValidationErrors>(); }
isLogDecompiledSpirvEnabled(void) const923 bool CommandLine::isLogDecompiledSpirvEnabled (void) const { return m_cmdLine.getOption<opt::LogDecompiledSpirv>(); }
isOutOfMemoryTestEnabled(void) const924 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
isShadercacheEnabled(void) const925 bool CommandLine::isShadercacheEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCache>(); }
getShaderCacheFilename(void) const926 const char* CommandLine::getShaderCacheFilename (void) const { return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str(); }
isShaderCacheTruncateEnabled(void) const927 bool CommandLine::isShaderCacheTruncateEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheTruncate>(); }
getOptimizationRecipe(void) const928 int CommandLine::getOptimizationRecipe (void) const { return m_cmdLine.getOption<opt::Optimization>(); }
isSpirvOptimizationEnabled(void) const929 bool CommandLine::isSpirvOptimizationEnabled (void) const { return m_cmdLine.getOption<opt::OptimizeSpirv>(); }
isRenderDocEnabled(void) const930 bool CommandLine::isRenderDocEnabled (void) const { return m_cmdLine.getOption<opt::RenderDoc>(); }
getWaiverFileName(void) const931 const char* CommandLine::getWaiverFileName (void) const { return m_cmdLine.getOption<opt::WaiverFile>().c_str(); }
getCaseFraction(void) const932 const std::vector<int>& CommandLine::getCaseFraction (void) const { return m_cmdLine.getOption<opt::CaseFraction>(); }
getCaseFractionMandatoryTests(void) const933 const char* CommandLine::getCaseFractionMandatoryTests (void) const { return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str(); }
getArchiveDir(void) const934 const char* CommandLine::getArchiveDir (void) const { return m_cmdLine.getOption<opt::ArchiveDir>().c_str(); }
getRunnerType(void) const935 tcu::TestRunnerType CommandLine::getRunnerType (void) const { return m_cmdLine.getOption<opt::RunnerType>(); }
isTerminateOnFailEnabled(void) const936 bool CommandLine::isTerminateOnFailEnabled (void) const { return m_cmdLine.getOption<opt::TerminateOnFail>(); }
937
getGLContextType(void) const938 const char* CommandLine::getGLContextType (void) const
939 {
940 if (m_cmdLine.hasOption<opt::GLContextType>())
941 return m_cmdLine.getOption<opt::GLContextType>().c_str();
942 else
943 return DE_NULL;
944 }
getGLConfigName(void) const945 const char* CommandLine::getGLConfigName (void) const
946 {
947 if (m_cmdLine.hasOption<opt::GLConfigName>())
948 return m_cmdLine.getOption<opt::GLConfigName>().c_str();
949 else
950 return DE_NULL;
951 }
952
getGLContextFlags(void) const953 const char* CommandLine::getGLContextFlags (void) const
954 {
955 if (m_cmdLine.hasOption<opt::GLContextFlags>())
956 return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
957 else
958 return DE_NULL;
959 }
960
getCLBuildOptions(void) const961 const char* CommandLine::getCLBuildOptions (void) const
962 {
963 if (m_cmdLine.hasOption<opt::CLBuildOptions>())
964 return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
965 else
966 return DE_NULL;
967 }
968
getEGLDisplayType(void) const969 const char* CommandLine::getEGLDisplayType (void) const
970 {
971 if (m_cmdLine.hasOption<opt::EGLDisplayType>())
972 return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
973 else
974 return DE_NULL;
975 }
976
getEGLWindowType(void) const977 const char* CommandLine::getEGLWindowType (void) const
978 {
979 if (m_cmdLine.hasOption<opt::EGLWindowType>())
980 return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
981 else
982 return DE_NULL;
983 }
984
getEGLPixmapType(void) const985 const char* CommandLine::getEGLPixmapType (void) const
986 {
987 if (m_cmdLine.hasOption<opt::EGLPixmapType>())
988 return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
989 else
990 return DE_NULL;
991 }
992
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)993 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
994 {
995 const CaseTreeNode* node = findNode(root, groupPath);
996 return node && node->hasChildren();
997 }
998
checkTestCaseName(const CaseTreeNode * root,const char * casePath)999 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1000 {
1001 const CaseTreeNode* node = findNode(root, casePath);
1002 return node && !node->hasChildren();
1003 }
1004
createCaseListFilter(const tcu::Archive & archive) const1005 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1006 {
1007 return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1008 }
1009
checkTestGroupName(const char * groupName) const1010 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1011 {
1012 bool result = false;
1013 if (m_casePaths)
1014 result = m_casePaths->matches(groupName, true);
1015 else if (m_caseTree)
1016 result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1017 else
1018 return true;
1019 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1020 result = m_caseFractionMandatoryTests->matches(groupName, true);
1021 return result;
1022 }
1023
checkTestCaseName(const char * caseName) const1024 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1025 {
1026 bool result = false;
1027 if (m_casePaths)
1028 result = m_casePaths->matches(caseName, false);
1029 else if (m_caseTree)
1030 result = tcu::checkTestCaseName(m_caseTree, caseName);
1031 else
1032 return true;
1033 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1034 result = m_caseFractionMandatoryTests->matches(caseName, false);
1035 return result;
1036 }
1037
checkCaseFraction(int i,const std::string & testCaseName) const1038 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1039 {
1040 return m_caseFraction.size() != 2 ||
1041 ((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1042 (m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1043 }
1044
CaseListFilter(void)1045 CaseListFilter::CaseListFilter (void)
1046 : m_caseTree (DE_NULL)
1047 , m_runnerType (tcu::RUNNERTYPE_ANY)
1048 {
1049 }
1050
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1051 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1052 : m_caseTree (DE_NULL)
1053 {
1054 if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1055 {
1056 m_runnerType = RUNNERTYPE_AMBER;
1057 }
1058 else
1059 {
1060 m_runnerType = cmdLine.getOption<opt::RunnerType>();
1061 }
1062
1063 if (cmdLine.hasOption<opt::CaseList>())
1064 {
1065 std::istringstream str(cmdLine.getOption<opt::CaseList>());
1066
1067 m_caseTree = parseCaseList(str, archive);
1068 }
1069 else if (cmdLine.hasOption<opt::CaseListFile>())
1070 {
1071 std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1072 std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1073
1074 if (!in.is_open() || !in.good())
1075 throw Exception("Failed to open case list file '" + caseListFile + "'");
1076
1077 m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1078 }
1079 else if (cmdLine.hasOption<opt::CaseListResource>())
1080 {
1081 // \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1082 // istream adaptor for tcu::Resource.
1083 de::UniquePtr<Resource> caseListResource (archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1084 const int bufferSize = caseListResource->getSize();
1085 std::vector<char> buffer ((size_t)bufferSize);
1086
1087 if (buffer.empty())
1088 throw Exception("Empty case list resource");
1089
1090 caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1091
1092 {
1093 std::istringstream in (std::string(&buffer[0], (size_t)bufferSize));
1094
1095 m_caseTree = parseCaseList(in, archive);
1096 }
1097 }
1098 else if (cmdLine.getOption<opt::StdinCaseList>())
1099 {
1100 m_caseTree = parseCaseList(std::cin, archive);
1101 }
1102 else if (cmdLine.hasOption<opt::CasePath>())
1103 m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1104
1105 m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1106
1107 if (m_caseFraction.size() == 2 &&
1108 (m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1109 throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1110
1111 if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1112 throw Exception("Invalid case fraction. Must have two components.");
1113
1114 if (m_caseFraction.size() == 2)
1115 {
1116 std::string caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1117
1118 if (!caseFractionMandatoryTestsFilename.empty())
1119 {
1120 std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1121 if (!fileStream.is_open() || !fileStream.good())
1122 throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1123
1124 std::vector<std::string> cfPaths;
1125 std::string line;
1126
1127 while (std::getline(fileStream, line))
1128 {
1129 line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1130 cfPaths.push_back(line);
1131 }
1132 if (!cfPaths.empty())
1133 {
1134 m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1135 if (m_caseTree != DE_NULL)
1136 {
1137 fileStream.clear();
1138 fileStream.seekg(0, fileStream.beg);
1139 parseCaseList(m_caseTree, fileStream, false);
1140 }
1141 }
1142 }
1143 }
1144 }
1145
~CaseListFilter(void)1146 CaseListFilter::~CaseListFilter (void)
1147 {
1148 delete m_caseTree;
1149 }
1150
1151 } // tcu
1152