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