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
parseIntList(const char * src,std::vector<int> * dst)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
registerOptions(de::cmdline::Parser & parser)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
registerLegacyOptions(de::cmdline::Parser & parser)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
DebugOutStreambuf(void)223 DebugOutStreambuf::DebugOutStreambuf (void)
224 {
225 }
226
~DebugOutStreambuf(void)227 DebugOutStreambuf::~DebugOutStreambuf (void)
228 {
229 if (m_curLine.tellp() != std::streampos(0))
230 flushLine();
231 }
232
xsputn(const char * s,std::streamsize count)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
overflow(int ch)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
flushLine(void)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:
CaseTreeNode(const std::string & name)267 CaseTreeNode (const std::string& name) : m_name(name) {}
268 ~CaseTreeNode (void);
269
getName(void) const270 const std::string& getName (void) const { return m_name; }
hasChildren(void) const271 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
addChild(CaseTreeNode * child)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
~CaseTreeNode(void)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
findChildNdx(const std::string & name) const298 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
hasChild(const std::string & name) const308 inline bool CaseTreeNode::hasChild (const std::string& name) const
309 {
310 return findChildNdx(name) != NOT_FOUND;
311 }
312
getChild(const std::string & name) const313 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
getChild(const std::string & name)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
getCurrentComponentLen(const char * path)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
findNode(const CaseTreeNode * root,const char * path)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
parseCaseTrie(CaseTreeNode * root,std::istream & in)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
parseCaseList(CaseTreeNode * root,std::istream & in)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
parseCaseList(std::istream & in)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
CasePaths(const string & pathList)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.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)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.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)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
matches(const string & caseName,bool allowPrefix) const631 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 *//*--------------------------------------------------------------------*/
CommandLine(void)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 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)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 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)684 CommandLine::CommandLine (const std::string& cmdLine)
685 {
686 if (!parse(cmdLine))
687 throw Exception("Failed to parse command line");
688 }
689
~CommandLine(void)690 CommandLine::~CommandLine (void)
691 {
692 }
693
clear(void)694 void CommandLine::clear (void)
695 {
696 m_cmdLine.clear();
697 m_logFlags = 0;
698 }
699
getCommandLine(void) const700 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
701 {
702 return m_cmdLine;
703 }
704
registerExtendedOptions(de::cmdline::Parser & parser)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 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)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 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)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
getLogFileName(void) const786 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const787 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const788 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const789 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const790 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const791 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const792 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const793 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const794 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const795 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const796 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const797 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const798 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const799 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const800 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const801 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const802 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
getVKDeviceGroupId(void) const803 int CommandLine::getVKDeviceGroupId (void) const { return m_cmdLine.getOption<opt::VKDeviceGroupID>(); }
isValidationEnabled(void) const804 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
isOutOfMemoryTestEnabled(void) const805 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
isShadercacheEnabled(void) const806 bool CommandLine::isShadercacheEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCache>(); }
getShaderCacheFilename(void) const807 const char* CommandLine::getShaderCacheFilename (void) const { return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str(); }
isShaderCacheTruncateEnabled(void) const808 bool CommandLine::isShaderCacheTruncateEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheTruncate>(); }
getOptimizationRecipe(void) const809 int CommandLine::getOptimizationRecipe (void) const { return m_cmdLine.getOption<opt::Optimization>(); }
isSpirvOptimizationEnabled(void) const810 bool CommandLine::isSpirvOptimizationEnabled (void) const { return m_cmdLine.getOption<opt::OptimizeSpirv>(); }
isRenderDocEnabled(void) const811 bool CommandLine::isRenderDocEnabled (void) const { return m_cmdLine.getOption<opt::RenderDoc>(); }
812
getGLContextType(void) const813 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 }
getGLConfigName(void) const820 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
getGLContextFlags(void) const828 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
getCLBuildOptions(void) const836 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
getEGLDisplayType(void) const844 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
getEGLWindowType(void) const852 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
getEGLPixmapType(void) const860 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
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)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
checkTestCaseName(const CaseTreeNode * root,const char * casePath)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
createCaseListFilter(const tcu::Archive & archive) const880 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
881 {
882 return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
883 }
884
checkTestGroupName(const char * groupName) const885 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
checkTestCaseName(const char * caseName) const895 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
CaseListFilter(void)905 CaseListFilter::CaseListFilter (void)
906 : m_caseTree(DE_NULL)
907 {
908 }
909
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)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
~CaseListFilter(void)955 CaseListFilter::~CaseListFilter (void)
956 {
957 delete m_caseTree;
958 }
959
960 } // tcu
961