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