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