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
41 using std::string;
42 using 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
51 namespace tcu
52 {
53
54 namespace opt
55 {
56
57 DE_DECLARE_COMMAND_LINE_OPT(CasePath, std::string);
58 DE_DECLARE_COMMAND_LINE_OPT(CaseList, std::string);
59 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile, std::string);
60 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList, bool);
61 DE_DECLARE_COMMAND_LINE_OPT(LogFilename, std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(RunMode, tcu::RunMode);
63 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern, std::string);
64 DE_DECLARE_COMMAND_LINE_OPT(WatchDog, bool);
65 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler, bool);
66 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed, int);
67 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount, int);
68 DE_DECLARE_COMMAND_LINE_OPT(Visibility, WindowVisibility);
69 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth, int);
70 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight, int);
71 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType, tcu::SurfaceType);
72 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation, tcu::ScreenRotation);
73 DE_DECLARE_COMMAND_LINE_OPT(GLContextType, std::string);
74 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID, int);
75 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName, std::string);
76 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags, std::string);
77 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID, int);
78 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs, std::vector<int>);
79 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions, std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType, std::string);
81 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType, std::string);
82 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType, std::string);
83 DE_DECLARE_COMMAND_LINE_OPT(LogImages, bool);
84 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources, bool);
85 DE_DECLARE_COMMAND_LINE_OPT(TestOOM, bool);
86 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID, int);
87 DE_DECLARE_COMMAND_LINE_OPT(LogFlush, bool);
88 DE_DECLARE_COMMAND_LINE_OPT(Validation, bool);
89
parseIntList(const char * src,std::vector<int> * dst)90 static 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
registerOptions(de::cmdline::Parser & parser)103 void 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
registerLegacyOptions(de::cmdline::Parser & parser)178 void 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.
190 class DebugOutStreambuf : public std::streambuf
191 {
192 public:
193 DebugOutStreambuf (void);
194 ~DebugOutStreambuf (void);
195
196 protected:
197 std::streamsize xsputn (const char* s, std::streamsize count);
198 int overflow (int ch = -1);
199
200 private:
201 void flushLine (void);
202
203 std::ostringstream m_curLine;
204 };
205
DebugOutStreambuf(void)206 DebugOutStreambuf::DebugOutStreambuf (void)
207 {
208 }
209
~DebugOutStreambuf(void)210 DebugOutStreambuf::~DebugOutStreambuf (void)
211 {
212 if (m_curLine.tellp() != std::streampos(0))
213 flushLine();
214 }
215
xsputn(const char * s,std::streamsize count)216 std::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
overflow(int ch)229 int 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
flushLine(void)241 void DebugOutStreambuf::flushLine (void)
242 {
243 qpPrint(m_curLine.str().c_str());
244 m_curLine.str("");
245 }
246
247 class CaseTreeNode
248 {
249 public:
CaseTreeNode(const std::string & name)250 CaseTreeNode (const std::string& name) : m_name(name) {}
251 ~CaseTreeNode (void);
252
getName(void) const253 const std::string& getName (void) const { return m_name; }
hasChildren(void) const254 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
addChild(CaseTreeNode * child)260 void addChild (CaseTreeNode* child) { m_children.push_back(child); }
261
262 private:
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
~CaseTreeNode(void)275 CaseTreeNode::~CaseTreeNode (void)
276 {
277 for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
278 delete *i;
279 }
280
findChildNdx(const std::string & name) const281 int 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
hasChild(const std::string & name) const291 inline bool CaseTreeNode::hasChild (const std::string& name) const
292 {
293 return findChildNdx(name) != NOT_FOUND;
294 }
295
getChild(const std::string & name) const296 inline 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
getChild(const std::string & name)302 inline 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
getCurrentComponentLen(const char * path)308 static int getCurrentComponentLen (const char* path)
309 {
310 int ndx = 0;
311 for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
312 return ndx;
313 }
314
findNode(const CaseTreeNode * root,const char * path)315 static 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
parseCaseTrie(CaseTreeNode * root,std::istream & in)343 static 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
parseCaseList(CaseTreeNode * root,std::istream & in)409 static 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
parseCaseList(std::istream & in)503 static 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
528 class CasePaths
529 {
530 public:
531 CasePaths (const string& pathList);
532 bool matches (const string& caseName, bool allowPrefix=false) const;
533
534 private:
535 const vector<string> m_casePatterns;
536 };
537
CasePaths(const string & pathList)538 CasePaths::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.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)544 static 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.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)582 static 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
matches(const string & caseName,bool allowPrefix) const614 bool 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 *//*--------------------------------------------------------------------*/
CommandLine(void)640 CommandLine::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 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)653 CommandLine::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 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)667 CommandLine::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
~CommandLine(void)675 CommandLine::~CommandLine (void)
676 {
677 delete m_caseTree;
678 }
679
clear(void)680 void CommandLine::clear (void)
681 {
682 m_cmdLine.clear();
683 m_logFlags = 0;
684
685 delete m_caseTree;
686 m_caseTree = DE_NULL;
687 }
688
getCommandLine(void) const689 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
690 {
691 return m_cmdLine;
692 }
693
registerExtendedOptions(de::cmdline::Parser & parser)694 void 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 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)705 bool 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 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)784 bool 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
getLogFileName(void) const805 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const806 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const807 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const808 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const809 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const810 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const811 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const812 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const813 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const814 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const815 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const816 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const817 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const818 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const819 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const820 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const821 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
isValidationEnabled(void) const822 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
isOutOfMemoryTestEnabled(void) const823 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
824
getGLContextType(void) const825 const 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 }
getGLConfigName(void) const832 const 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
getGLContextFlags(void) const840 const 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
getCLBuildOptions(void) const848 const 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
getEGLDisplayType(void) const856 const 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
getEGLWindowType(void) const864 const 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
getEGLPixmapType(void) const872 const 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
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)880 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
881 {
882 const CaseTreeNode* node = findNode(root, groupPath);
883 return node && node->hasChildren();
884 }
885
checkTestCaseName(const CaseTreeNode * root,const char * casePath)886 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
887 {
888 const CaseTreeNode* node = findNode(root, casePath);
889 return node && !node->hasChildren();
890 }
891
checkTestGroupName(const char * groupName) const892 bool 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
checkTestCaseName(const char * caseName) const902 bool 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