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
parseIntList(const char * src,std::vector<int> * dst)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
registerOptions(de::cmdline::Parser & parser)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
registerLegacyOptions(de::cmdline::Parser & parser)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
DebugOutStreambuf(void)211 DebugOutStreambuf::DebugOutStreambuf (void)
212 {
213 }
214
~DebugOutStreambuf(void)215 DebugOutStreambuf::~DebugOutStreambuf (void)
216 {
217 if (m_curLine.tellp() != std::streampos(0))
218 flushLine();
219 }
220
xsputn(const char * s,std::streamsize count)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
overflow(int ch)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
flushLine(void)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:
CaseTreeNode(const std::string & name)255 CaseTreeNode (const std::string& name) : m_name(name) {}
256 ~CaseTreeNode (void);
257
getName(void) const258 const std::string& getName (void) const { return m_name; }
hasChildren(void) const259 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
addChild(CaseTreeNode * child)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
~CaseTreeNode(void)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
findChildNdx(const std::string & name) const286 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
hasChild(const std::string & name) const296 inline bool CaseTreeNode::hasChild (const std::string& name) const
297 {
298 return findChildNdx(name) != NOT_FOUND;
299 }
300
getChild(const std::string & name) const301 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
getChild(const std::string & name)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
getCurrentComponentLen(const char * path)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
findNode(const CaseTreeNode * root,const char * path)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
parseCaseTrie(CaseTreeNode * root,std::istream & in)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
parseCaseList(CaseTreeNode * root,std::istream & in)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
parseCaseList(std::istream & in)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
CasePaths(const string & pathList)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.
matchWildcards(string::const_iterator patternStart,string::const_iterator patternEnd,string::const_iterator pathStart,string::const_iterator pathEnd,bool allowPrefix)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.
patternMatches(vector<string>::const_iterator patternStart,vector<string>::const_iterator patternEnd,vector<string>::const_iterator pathStart,vector<string>::const_iterator pathEnd,bool allowPrefix)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
matches(const string & caseName,bool allowPrefix) const619 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 *//*--------------------------------------------------------------------*/
CommandLine(void)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 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)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 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)672 CommandLine::CommandLine (const std::string& cmdLine)
673 {
674 if (!parse(cmdLine))
675 throw Exception("Failed to parse command line");
676 }
677
~CommandLine(void)678 CommandLine::~CommandLine (void)
679 {
680 }
681
clear(void)682 void CommandLine::clear (void)
683 {
684 m_cmdLine.clear();
685 m_logFlags = 0;
686 }
687
getCommandLine(void) const688 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
689 {
690 return m_cmdLine;
691 }
692
registerExtendedOptions(de::cmdline::Parser & parser)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 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)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 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)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
getLogFileName(void) const774 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const775 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const776 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const777 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const778 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const779 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const780 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const781 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const782 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const783 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const784 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const785 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const786 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const787 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const788 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const789 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const790 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
getVKDeviceGroupId(void) const791 int CommandLine::getVKDeviceGroupId (void) const { return m_cmdLine.getOption<opt::VKDeviceGroupID>(); }
isValidationEnabled(void) const792 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
isOutOfMemoryTestEnabled(void) const793 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
794
getGLContextType(void) const795 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 }
getGLConfigName(void) const802 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
getGLContextFlags(void) const810 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
getCLBuildOptions(void) const818 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
getEGLDisplayType(void) const826 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
getEGLWindowType(void) const834 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
getEGLPixmapType(void) const842 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
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)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
checkTestCaseName(const CaseTreeNode * root,const char * casePath)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
createCaseListFilter(const tcu::Archive & archive) const862 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
863 {
864 return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
865 }
866
checkTestGroupName(const char * groupName) const867 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
checkTestCaseName(const char * caseName) const877 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
CaseListFilter(void)887 CaseListFilter::CaseListFilter (void)
888 : m_caseTree(DE_NULL)
889 {
890 }
891
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)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
~CaseListFilter(void)937 CaseListFilter::~CaseListFilter (void)
938 {
939 delete m_caseTree;
940 }
941
942 } // tcu
943