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