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