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