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