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 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 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 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 194 DebugOutStreambuf::DebugOutStreambuf (void) 195 { 196 } 197 198 DebugOutStreambuf::~DebugOutStreambuf (void) 199 { 200 if (m_curLine.tellp() != std::streampos(0)) 201 flushLine(); 202 } 203 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 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 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: 238 CaseTreeNode (const std::string& name) : m_name(name) {} 239 ~CaseTreeNode (void); 240 241 const std::string& getName (void) const { return m_name; } 242 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 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 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 269 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 279 inline bool CaseTreeNode::hasChild (const std::string& name) const 280 { 281 return findChildNdx(name) != NOT_FOUND; 282 } 283 284 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 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 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 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 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 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 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 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. 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. 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 602 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 663 CommandLine::~CommandLine (void) 664 { 665 delete m_caseTree; 666 } 667 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 776 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); } 777 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; } 778 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); } 779 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); } 780 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); } 781 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); } 782 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); } 783 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); } 784 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); } 785 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); } 786 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); } 787 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); } 788 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); } 789 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); } 790 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); } 791 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); } 792 793 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 } 800 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 808 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 816 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 824 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 832 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 840 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 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 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 860 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 870 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