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 { "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 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 195 DebugOutStreambuf::DebugOutStreambuf (void) 196 { 197 } 198 199 DebugOutStreambuf::~DebugOutStreambuf (void) 200 { 201 if (m_curLine.tellp() != std::streampos(0)) 202 flushLine(); 203 } 204 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 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 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: 239 CaseTreeNode (const std::string& name) : m_name(name) {} 240 ~CaseTreeNode (void); 241 242 const std::string& getName (void) const { return m_name; } 243 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 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 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 270 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 280 inline bool CaseTreeNode::hasChild (const std::string& name) const 281 { 282 return findChildNdx(name) != NOT_FOUND; 283 } 284 285 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 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 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 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 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 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 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 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. 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. 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 603 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 664 CommandLine::~CommandLine (void) 665 { 666 delete m_caseTree; 667 } 668 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 678 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const 679 { 680 return m_cmdLine; 681 } 682 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 *//*--------------------------------------------------------------------*/ 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 *//*--------------------------------------------------------------------*/ 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 788 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); } 789 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; } 790 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); } 791 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); } 792 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); } 793 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); } 794 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); } 795 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); } 796 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); } 797 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); } 798 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); } 799 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); } 800 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); } 801 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); } 802 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); } 803 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); } 804 805 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 } 812 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 820 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 828 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 836 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 844 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 852 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 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 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 872 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 882 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