• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <unordered_map>
43 
44 using std::string;
45 using std::vector;
46 
47 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
48 #if (DE_OS == DE_OS_WIN32)
49 #define TEST_OOM_DEFAULT "enable"
50 #else
51 #define TEST_OOM_DEFAULT "disable"
52 #endif
53 
54 // Duplicate name checks are enabled by default in Debug mode, and disabled in Release mode.
55 #if defined(DE_DEBUG)
56 #define DUPLICATE_CHECK_DEFAULT "enable"
57 #else
58 #define DUPLICATE_CHECK_DEFAULT "disable"
59 #endif
60 
61 // SPIRV validation is enabled by default in Debug mode, and disabled in Release mode.
62 #if defined(DE_DEBUG)
63 #define SPIRV_VALIDATION_DEFAULT "enable"
64 #else
65 #define SPIRV_VALIDATION_DEFAULT "disable"
66 #endif
67 
68 namespace tcu
69 {
70 
71 namespace opt
72 {
73 
74 DE_DECLARE_COMMAND_LINE_OPT(CasePath, std::string);
75 DE_DECLARE_COMMAND_LINE_OPT(CaseList, std::string);
76 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile, std::string);
77 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource, std::string);
78 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList, bool);
79 DE_DECLARE_COMMAND_LINE_OPT(LogFilename, std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(RunMode, tcu::RunMode);
81 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern, std::string);
82 DE_DECLARE_COMMAND_LINE_OPT(WatchDog, bool);
83 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler, bool);
84 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed, int);
85 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount, int);
86 DE_DECLARE_COMMAND_LINE_OPT(Visibility, WindowVisibility);
87 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth, int);
88 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight, int);
89 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType, tcu::SurfaceType);
90 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation, tcu::ScreenRotation);
91 DE_DECLARE_COMMAND_LINE_OPT(GLContextType, std::string);
92 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID, int);
93 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName, std::string);
94 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags, std::string);
95 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID, int);
96 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs, std::vector<int>);
97 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions, std::string);
98 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType, std::string);
99 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType, std::string);
100 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType, std::string);
101 DE_DECLARE_COMMAND_LINE_OPT(LogImages, bool);
102 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources, bool);
103 DE_DECLARE_COMMAND_LINE_OPT(LogDecompiledSpirv, bool);
104 DE_DECLARE_COMMAND_LINE_OPT(LogEmptyLoginfo, bool);
105 DE_DECLARE_COMMAND_LINE_OPT(TestOOM, bool);
106 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir, std::string);
107 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID, int);
108 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID, int);
109 DE_DECLARE_COMMAND_LINE_OPT(LogFlush, bool);
110 DE_DECLARE_COMMAND_LINE_OPT(LogCompact, bool);
111 DE_DECLARE_COMMAND_LINE_OPT(Validation, bool);
112 DE_DECLARE_COMMAND_LINE_OPT(SpirvValidation, bool);
113 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors, bool);
114 DE_DECLARE_COMMAND_LINE_OPT(DuplicateCheck, bool);
115 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache, bool);
116 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename, std::string);
117 DE_DECLARE_COMMAND_LINE_OPT(Optimization, int);
118 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv, bool);
119 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate, bool);
120 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheIPC, bool);
121 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc, bool);
122 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction, std::vector<int>);
123 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests, std::string);
124 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile, std::string);
125 DE_DECLARE_COMMAND_LINE_OPT(RunnerType, tcu::TestRunnerType);
126 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail, bool);
127 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnDeviceLost, bool);
128 DE_DECLARE_COMMAND_LINE_OPT(SubProcess, bool);
129 DE_DECLARE_COMMAND_LINE_OPT(SubprocessTestCount, int);
130 DE_DECLARE_COMMAND_LINE_OPT(SubprocessConfigFile, std::string);
131 DE_DECLARE_COMMAND_LINE_OPT(ServerAddress, std::string);
132 DE_DECLARE_COMMAND_LINE_OPT(CommandPoolMinSize, int);
133 DE_DECLARE_COMMAND_LINE_OPT(CommandBufferMinSize, int);
134 DE_DECLARE_COMMAND_LINE_OPT(CommandDefaultSize, int);
135 DE_DECLARE_COMMAND_LINE_OPT(PipelineDefaultSize, int);
136 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath, std::string);
137 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir, std::string);
138 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs, std::string);
139 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile, std::string);
140 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile, std::string);
141 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerFilePrefix, std::string);
142 DE_DECLARE_COMMAND_LINE_OPT(VkLibraryPath, std::string);
143 DE_DECLARE_COMMAND_LINE_OPT(ApplicationParametersInputFile, std::string);
144 DE_DECLARE_COMMAND_LINE_OPT(QuietStdout, bool);
145 DE_DECLARE_COMMAND_LINE_OPT(ComputeOnly, bool);
146 
parseIntList(const char * src,std::vector<int> * dst)147 static void parseIntList(const char *src, std::vector<int> *dst)
148 {
149     std::istringstream str(src);
150     std::string val;
151 
152     while (std::getline(str, val, ','))
153     {
154         int intVal = 0;
155         de::cmdline::parseType(val.c_str(), &intVal);
156         dst->push_back(intVal);
157     }
158 }
159 
registerOptions(de::cmdline::Parser & parser)160 void registerOptions(de::cmdline::Parser &parser)
161 {
162     using de::cmdline::NamedValue;
163     using de::cmdline::Option;
164 
165     static const NamedValue<bool> s_enableNames[]                    = {{"enable", true}, {"disable", false}};
166     static const NamedValue<tcu::RunMode> s_runModes[]               = {{"execute", RUNMODE_EXECUTE},
167                                                                         {"xml-caselist", RUNMODE_DUMP_XML_CASELIST},
168                                                                         {"txt-caselist", RUNMODE_DUMP_TEXT_CASELIST},
169                                                                         {"stdout-caselist", RUNMODE_DUMP_STDOUT_CASELIST},
170                                                                         {"amber-verify", RUNMODE_VERIFY_AMBER_COHERENCY}};
171     static const NamedValue<WindowVisibility> s_visibilites[]        = {{"windowed", WINDOWVISIBILITY_WINDOWED},
172                                                                         {"fullscreen", WINDOWVISIBILITY_FULLSCREEN},
173                                                                         {"hidden", WINDOWVISIBILITY_HIDDEN}};
174     static const NamedValue<tcu::SurfaceType> s_surfaceTypes[]       = {{"window", SURFACETYPE_WINDOW},
175                                                                         {"pixmap", SURFACETYPE_OFFSCREEN_NATIVE},
176                                                                         {"pbuffer", SURFACETYPE_OFFSCREEN_GENERIC},
177                                                                         {"fbo", SURFACETYPE_FBO}};
178     static const NamedValue<tcu::ScreenRotation> s_screenRotations[] = {{"unspecified", SCREENROTATION_UNSPECIFIED},
179                                                                         {"0", SCREENROTATION_0},
180                                                                         {"90", SCREENROTATION_90},
181                                                                         {"180", SCREENROTATION_180},
182                                                                         {"270", SCREENROTATION_270}};
183     static const NamedValue<tcu::TestRunnerType> s_runnerTypes[]     = {
184         {"any", tcu::RUNNERTYPE_ANY},
185         {"none", tcu::RUNNERTYPE_NONE},
186         {"amber", tcu::RUNNERTYPE_AMBER},
187     };
188 
189     parser
190         << Option<QuietStdout>("q", "quiet", "Suppress messages to standard output")
191         << Option<CasePath>("n", "deqp-case", "Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
192         << Option<CaseListFile>("f", "deqp-caselist-file", "Read case list (in trie format) from given file")
193         << Option<CaseList>(nullptr, "deqp-caselist",
194                             "Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
195         << Option<CaseListResource>(nullptr, "deqp-caselist-resource",
196                                     "Read case list (in trie format) from given file located application's assets")
197         << Option<StdinCaseList>(nullptr, "deqp-stdin-caselist", "Read case list (in trie format) from stdin")
198         << Option<LogFilename>(nullptr, "deqp-log-filename", "Write test results to given file", "TestResults.qpa")
199         << Option<RunMode>(nullptr, "deqp-runmode",
200                            "Execute tests, write list of test cases into a file, or verify amber capability coherency",
201                            s_runModes, "execute")
202         << Option<ExportFilenamePattern>(nullptr, "deqp-caselist-export-file",
203                                          "Set the target file name pattern for caselist export",
204                                          "${packageName}-cases.${typeExtension}")
205         << Option<WatchDog>(nullptr, "deqp-watchdog", "Enable test watchdog", s_enableNames, "disable")
206         << Option<CrashHandler>(nullptr, "deqp-crashhandler", "Enable crash handling", s_enableNames, "disable")
207         << Option<BaseSeed>(nullptr, "deqp-base-seed", "Base seed for test cases that use randomization", "0")
208         << Option<TestIterationCount>(nullptr, "deqp-test-iteration-count",
209                                       "Iteration count for cases that support variable number of iterations", "0")
210         << Option<Visibility>(nullptr, "deqp-visibility", "Default test window visibility", s_visibilites, "windowed")
211         << Option<SurfaceWidth>(nullptr, "deqp-surface-width", "Use given surface width if possible", "-1")
212         << Option<SurfaceHeight>(nullptr, "deqp-surface-height", "Use given surface height if possible", "-1")
213         << Option<SurfaceType>(nullptr, "deqp-surface-type", "Use given surface type", s_surfaceTypes, "window")
214         << Option<ScreenRotation>(nullptr, "deqp-screen-rotation", "Screen rotation for platforms that support it",
215                                   s_screenRotations, "0")
216         << Option<GLContextType>(nullptr, "deqp-gl-context-type",
217                                  "OpenGL context type for platforms that support multiple")
218         << Option<GLConfigID>(nullptr, "deqp-gl-config-id",
219                               "OpenGL (ES) render config ID (EGL config id on EGL platforms)", "-1")
220         << Option<GLConfigName>(nullptr, "deqp-gl-config-name", "Symbolic OpenGL (ES) render config name")
221         << Option<GLContextFlags>(nullptr, "deqp-gl-context-flags",
222                                   "OpenGL context flags (comma-separated, supports debug and robust)")
223         << Option<CLPlatformID>(nullptr, "deqp-cl-platform-id",
224                                 "Execute tests on given OpenCL platform (IDs start from 1)", "1")
225         << Option<CLDeviceIDs>(nullptr, "deqp-cl-device-ids",
226                                "Execute tests on given CL devices (comma-separated, IDs start from 1)", parseIntList,
227                                "")
228         << Option<CLBuildOptions>(nullptr, "deqp-cl-build-options", "Extra build options for OpenCL compiler")
229         << Option<EGLDisplayType>(nullptr, "deqp-egl-display-type", "EGL native display type")
230         << Option<EGLWindowType>(nullptr, "deqp-egl-window-type", "EGL native window type")
231         << Option<EGLPixmapType>(nullptr, "deqp-egl-pixmap-type", "EGL native pixmap type")
232         << Option<VKDeviceID>(nullptr, "deqp-vk-device-id", "Vulkan device ID (IDs start from 1)", "1")
233         << Option<VKDeviceGroupID>(nullptr, "deqp-vk-device-group-id", "Vulkan device Group ID (IDs start from 1)", "1")
234         << Option<LogImages>(nullptr, "deqp-log-images", "Enable or disable logging of result images", s_enableNames,
235                              "enable")
236         << Option<LogShaderSources>(nullptr, "deqp-log-shader-sources", "Enable or disable logging of shader sources",
237                                     s_enableNames, "enable")
238         << Option<LogDecompiledSpirv>(nullptr, "deqp-log-decompiled-spirv",
239                                       "Enable or disable logging of decompiled spir-v", s_enableNames, "enable")
240         << Option<LogEmptyLoginfo>(nullptr, "deqp-log-empty-loginfo", "Logging of empty shader compile/link log info",
241                                    s_enableNames, "enable")
242         << Option<TestOOM>(nullptr, "deqp-test-oom", "Run tests that exhaust memory on purpose", s_enableNames,
243                            TEST_OOM_DEFAULT)
244         << Option<ArchiveDir>(nullptr, "deqp-archive-dir", "Path to test resource files", ".")
245         << Option<LogFlush>(nullptr, "deqp-log-flush", "Enable or disable log file fflush", s_enableNames, "enable")
246         << Option<LogCompact>(nullptr, "deqp-log-compact", "Enable or disable the compact version of the log",
247                               s_enableNames, "disable")
248         << Option<Validation>(nullptr, "deqp-validation", "Enable or disable test case validation", s_enableNames,
249                               "disable")
250         << Option<SpirvValidation>(nullptr, "deqp-spirv-validation", "Enable or disable spir-v shader validation",
251                                    s_enableNames, SPIRV_VALIDATION_DEFAULT)
252         << Option<PrintValidationErrors>(nullptr, "deqp-print-validation-errors",
253                                          "Print validation errors to standard error")
254         << Option<DuplicateCheck>(nullptr, "deqp-duplicate-case-name-check",
255                                   "Check for duplicate case names when creating test hierarchy", s_enableNames,
256                                   DUPLICATE_CHECK_DEFAULT)
257         << Option<Optimization>(nullptr, "deqp-optimization-recipe",
258                                 "Shader optimization recipe (0=disabled, 1=performance, 2=size)", "0")
259         << Option<OptimizeSpirv>(nullptr, "deqp-optimize-spirv", "Apply optimization to spir-v shaders as well",
260                                  s_enableNames, "disable")
261         << Option<ShaderCache>(nullptr, "deqp-shadercache", "Enable or disable shader cache", s_enableNames, "enable")
262         << Option<ShaderCacheFilename>(nullptr, "deqp-shadercache-filename", "Write shader cache to given file",
263                                        "shadercache.bin")
264         << Option<ShaderCacheTruncate>(nullptr, "deqp-shadercache-truncate",
265                                        "Truncate shader cache before running tests", s_enableNames, "enable")
266         << Option<ShaderCacheIPC>(nullptr, "deqp-shadercache-ipc", "Should shader cache use inter process comms",
267                                   s_enableNames, "disable")
268         << Option<RenderDoc>(nullptr, "deqp-renderdoc", "Enable RenderDoc frame markers", s_enableNames, "disable")
269         << Option<CaseFraction>(nullptr, "deqp-fraction",
270                                 "Run a fraction of the test cases (e.g. N,M means run group%M==N)", parseIntList, "")
271         << Option<CaseFractionMandatoryTests>(nullptr, "deqp-fraction-mandatory-caselist-file",
272                                               "Case list file that must be run for each fraction", "")
273         << Option<WaiverFile>(nullptr, "deqp-waiver-file", "Read waived tests from given file", "")
274         << Option<RunnerType>(nullptr, "deqp-runner-type", "Filter test cases based on runner", s_runnerTypes, "any")
275         << Option<TerminateOnFail>(nullptr, "deqp-terminate-on-fail", "Terminate the run on first failure",
276                                    s_enableNames, "disable")
277         << Option<TerminateOnDeviceLost>(nullptr, "deqp-terminate-on-device-lost",
278                                          "Terminate the run on a device lost error", s_enableNames, "enable")
279         << Option<SubProcess>(nullptr, "deqp-subprocess",
280                               "Inform app that it works as subprocess (Vulkan SC only, do not use manually)",
281                               s_enableNames, "disable")
282         << Option<SubprocessTestCount>(
283                nullptr, "deqp-subprocess-test-count",
284                "Define default number of tests performed in subprocess for specific test cases(Vulkan SC only)",
285                "65536")
286         << Option<SubprocessConfigFile>(nullptr, "deqp-subprocess-cfg-file",
287                                         "Config file defining number of tests performed in subprocess for specific "
288                                         "test branches (Vulkan SC only)",
289                                         "")
290         << Option<ServerAddress>(nullptr, "deqp-server-address",
291                                  "Server address (host:port) responsible for shader compilation (Vulkan SC only)", "")
292         << Option<CommandPoolMinSize>(nullptr, "deqp-command-pool-min-size",
293                                       "Define minimum size of the command pool (in bytes) to use (Vulkan SC only)", "0")
294         << Option<CommandBufferMinSize>(nullptr, "deqp-command-buffer-min-size",
295                                         "Define minimum size of the command buffer (in bytes) to use (Vulkan SC only)",
296                                         "0")
297         << Option<CommandDefaultSize>(nullptr, "deqp-command-default-size",
298                                       "Define default single command size (in bytes) to use (Vulkan SC only)", "256")
299         << Option<PipelineDefaultSize>(nullptr, "deqp-pipeline-default-size",
300                                        "Define default pipeline size (in bytes) to use (Vulkan SC only)", "16384")
301         << Option<PipelineCompilerPath>(nullptr, "deqp-pipeline-compiler",
302                                         "Path to offline pipeline compiler (Vulkan SC only)", "")
303         << Option<PipelineCompilerDataDir>(nullptr, "deqp-pipeline-dir",
304                                            "Offline pipeline data directory (Vulkan SC only)", "")
305         << Option<PipelineCompilerArgs>(nullptr, "deqp-pipeline-args",
306                                         "Additional compiler parameters (Vulkan SC only)", "")
307         << Option<PipelineCompilerOutputFile>(nullptr, "deqp-pipeline-file",
308                                               "Output file with pipeline cache (Vulkan SC only, do not use manually)",
309                                               "")
310         << Option<PipelineCompilerLogFile>(nullptr, "deqp-pipeline-logfile",
311                                            "Log file for pipeline compiler (Vulkan SC only, do not use manually)", "")
312         << Option<PipelineCompilerFilePrefix>(
313                nullptr, "deqp-pipeline-prefix",
314                "Prefix for input pipeline compiler files (Vulkan SC only, do not use manually)", "")
315         << Option<VkLibraryPath>(nullptr, "deqp-vk-library-path",
316                                  "Path to Vulkan library (e.g. loader library vulkan-1.dll)", "")
317         << Option<ApplicationParametersInputFile>(nullptr, "deqp-app-params-input-file",
318                                                   "File that provides a default set of application parameters")
319         << Option<ComputeOnly>(nullptr, "deqp-compute-only",
320                                "Perform tests for devices implementing compute-only functionality", s_enableNames,
321                                "disable");
322 }
323 
registerLegacyOptions(de::cmdline::Parser & parser)324 void registerLegacyOptions(de::cmdline::Parser &parser)
325 {
326     using de::cmdline::Option;
327 
328     parser << Option<GLConfigID>(nullptr, "deqp-egl-config-id", "Legacy name for --deqp-gl-config-id", "-1")
329            << Option<GLConfigName>(nullptr, "deqp-egl-config-name", "Legacy name for --deqp-gl-config-name");
330 }
331 
332 } // namespace opt
333 
334 // Used to store hashes of test case names
335 typedef uint64_t test_case_hash_t;
336 
337 // Source: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp
338 // MurmurHash2, 64-bit versions, by Austin Appleby
MurmurHash64B(const void * key,int len,uint64_t seed)339 static uint64_t MurmurHash64B(const void *key, int len, uint64_t seed)
340 {
341     const uint32_t m = 0x5bd1e995;
342     const int r      = 24;
343 
344     uint32_t h1 = uint32_t(seed) ^ len;
345     uint32_t h2 = uint32_t(seed >> 32);
346 
347     // Ensure that unaligned accesses to data are allowed.
348 #ifdef WIN32
349     typedef __declspec(align(1)) uint32_t uint32_t_unaligned;
350 #else
351     typedef __attribute__((aligned(1))) uint32_t uint32_t_unaligned;
352 #endif
353     const uint32_t_unaligned *data = (const uint32_t_unaligned *)key;
354 
355     while (len >= 8)
356     {
357         uint32_t k1 = *data++;
358         k1 *= m;
359         k1 ^= k1 >> r;
360         k1 *= m;
361         h1 *= m;
362         h1 ^= k1;
363         len -= 4;
364 
365         uint32_t k2 = *data++;
366         k2 *= m;
367         k2 ^= k2 >> r;
368         k2 *= m;
369         h2 *= m;
370         h2 ^= k2;
371         len -= 4;
372     }
373 
374     if (len >= 4)
375     {
376         uint32_t k1 = *data++;
377         k1 *= m;
378         k1 ^= k1 >> r;
379         k1 *= m;
380         h1 *= m;
381         h1 ^= k1;
382         len -= 4;
383     }
384 
385     switch (len)
386     {
387     case 3:
388         h2 ^= ((unsigned char *)data)[2] << 16;
389         // fall through
390     case 2:
391         h2 ^= ((unsigned char *)data)[1] << 8;
392         // fall through
393     case 1:
394         h2 ^= ((unsigned char *)data)[0];
395         h2 *= m;
396     };
397 
398     h1 ^= h2 >> 18;
399     h1 *= m;
400     h2 ^= h1 >> 22;
401     h2 *= m;
402     h1 ^= h2 >> 17;
403     h1 *= m;
404     h2 ^= h1 >> 19;
405     h2 *= m;
406 
407     uint64_t h = h1;
408 
409     h = (h << 32) | h2;
410 
411     return h;
412 }
413 
414 /*--------------------------------------------------------------------*//*!
415  * \brief Generates an hash for the test case name part provided.
416  * If a hashCollisionDetectionMap is passed, will detect hash
417  * collisions using that map. hashCollisionDetectionMap can be NULL.
418  * As an example, the standard std::hash<std::string> on a 32-bit
419  * machine will collide with 'random_298' and 'subgroupand_int16_t_mesh_requiredsubgroupsize'
420  *//*--------------------------------------------------------------------*/
hashTestNodeName(const std::string & name,std::unordered_map<test_case_hash_t,std::string> * hashCollisionDetectionMap)421 static test_case_hash_t hashTestNodeName(const std::string &name,
422                                          std::unordered_map<test_case_hash_t, std::string> *hashCollisionDetectionMap)
423 {
424     test_case_hash_t hash = MurmurHash64B(name.c_str(), (int)name.length(), 1);
425     if (hashCollisionDetectionMap != nullptr)
426     {
427         auto search = hashCollisionDetectionMap->find(hash);
428         if (search != hashCollisionDetectionMap->end())
429         {
430             if (search->second != name)
431             {
432                 print("There was an hash collision between '%s' and '%s'\n", search->second.c_str(), name.c_str());
433                 throw std::runtime_error("Hash collision detected!");
434             }
435         }
436         hashCollisionDetectionMap->insert({hash, name});
437     }
438     return hash;
439 }
440 
441 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
442 class DebugOutStreambuf : public std::streambuf
443 {
444 public:
445     DebugOutStreambuf(void);
446     ~DebugOutStreambuf(void);
447 
448 protected:
449     std::streamsize xsputn(const char *s, std::streamsize count);
450     int overflow(int ch = -1);
451 
452 private:
453     void flushLine(void);
454 
455     std::ostringstream m_curLine;
456 };
457 
DebugOutStreambuf(void)458 DebugOutStreambuf::DebugOutStreambuf(void)
459 {
460 }
461 
~DebugOutStreambuf(void)462 DebugOutStreambuf::~DebugOutStreambuf(void)
463 {
464     if (m_curLine.tellp() != std::streampos(0))
465         flushLine();
466 }
467 
xsputn(const char * s,std::streamsize count)468 std::streamsize DebugOutStreambuf::xsputn(const char *s, std::streamsize count)
469 {
470     for (std::streamsize pos = 0; pos < count; pos++)
471     {
472         m_curLine.put(s[pos]);
473 
474         if (s[pos] == '\n')
475             flushLine();
476     }
477 
478     return count;
479 }
480 
overflow(int ch)481 int DebugOutStreambuf::overflow(int ch)
482 {
483     if (ch == -1)
484         return -1;
485     else
486     {
487         DE_ASSERT((ch & 0xff) == ch);
488         const char chVal = (char)(uint8_t)(ch & 0xff);
489         return xsputn(&chVal, 1) == 1 ? ch : -1;
490     }
491 }
492 
flushLine(void)493 void DebugOutStreambuf::flushLine(void)
494 {
495     qpPrint(m_curLine.str().c_str());
496     m_curLine.str("");
497 }
498 
499 class CaseTreeNode
500 {
501 public:
CaseTreeNode(const test_case_hash_t hash)502     CaseTreeNode(const test_case_hash_t hash) : m_hash(hash)
503     {
504     }
505     ~CaseTreeNode(void);
506 
getHash(void) const507     test_case_hash_t getHash(void) const
508     {
509         return m_hash;
510     }
hasChildren(void) const511     bool hasChildren(void) const
512     {
513         return !m_children.empty();
514     }
515 
516     bool hasChild(test_case_hash_t hash) const;
517     CaseTreeNode *getChild(test_case_hash_t hash) const;
518 
addChild(CaseTreeNode * child)519     void addChild(CaseTreeNode *child)
520     {
521         m_children.push_back(child);
522     }
523 
524 private:
525     CaseTreeNode(const CaseTreeNode &);
526     CaseTreeNode &operator=(const CaseTreeNode &);
527 
528     enum
529     {
530         NOT_FOUND = -1
531     };
532 
533     int findChildNdx(test_case_hash_t hash) const;
534 
535     test_case_hash_t m_hash;
536     std::vector<CaseTreeNode *> m_children;
537 };
538 
~CaseTreeNode(void)539 CaseTreeNode::~CaseTreeNode(void)
540 {
541     for (vector<CaseTreeNode *>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
542         delete *i;
543 }
544 
findChildNdx(test_case_hash_t hash) const545 int CaseTreeNode::findChildNdx(test_case_hash_t hash) const
546 {
547     for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
548     {
549         if (m_children[ndx]->getHash() == hash)
550             return ndx;
551     }
552     return NOT_FOUND;
553 }
554 
hasChild(test_case_hash_t hash) const555 inline bool CaseTreeNode::hasChild(test_case_hash_t hash) const
556 {
557     return findChildNdx(hash) != NOT_FOUND;
558 }
559 
getChild(test_case_hash_t hash) const560 inline CaseTreeNode *CaseTreeNode::getChild(test_case_hash_t hash) const
561 {
562     const int ndx = findChildNdx(hash);
563     return ndx == NOT_FOUND ? nullptr : m_children[ndx];
564 }
565 
getCurrentComponentLen(const char * path)566 static int getCurrentComponentLen(const char *path)
567 {
568     int ndx = 0;
569     for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx)
570         ;
571     return ndx;
572 }
573 
findNode(const CaseTreeNode * root,const char * path)574 static const CaseTreeNode *findNode(const CaseTreeNode *root, const char *path)
575 {
576     const CaseTreeNode *curNode = root;
577     const char *curPath         = path;
578     int curLen                  = getCurrentComponentLen(curPath);
579 
580     for (;;)
581     {
582         test_case_hash_t hash = hashTestNodeName(std::string(curPath, curPath + curLen), nullptr);
583         curNode               = curNode->getChild(hash);
584 
585         if (!curNode)
586             break;
587 
588         curPath += curLen;
589 
590         if (curPath[0] == 0)
591             break;
592         else
593         {
594             DE_ASSERT(curPath[0] == '.');
595             curPath += 1;
596             curLen = getCurrentComponentLen(curPath);
597         }
598     }
599 
600     return curNode;
601 }
602 
parseCaseTrie(CaseTreeNode * root,std::istream & in,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)603 static void parseCaseTrie(CaseTreeNode *root, std::istream &in,
604                           std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
605 {
606     vector<CaseTreeNode *> nodeStack;
607     string curName;
608     bool expectNode = true;
609 
610     if (in.get() != '{')
611         throw std::invalid_argument("Malformed case trie");
612 
613     nodeStack.push_back(root);
614 
615     while (!nodeStack.empty())
616     {
617         const int curChr = in.get();
618 
619         if (curChr == std::char_traits<char>::eof() || curChr == 0)
620             throw std::invalid_argument("Unterminated case tree");
621 
622         if (curChr == '{' || curChr == ',' || curChr == '}')
623         {
624             if (!curName.empty() && expectNode)
625             {
626                 test_case_hash_t hash        = hashTestNodeName(curName, &hashCollisionDetectionMap);
627                 CaseTreeNode *const newChild = new CaseTreeNode(hash);
628 
629                 try
630                 {
631                     nodeStack.back()->addChild(newChild);
632                 }
633                 catch (...)
634                 {
635                     delete newChild;
636                     throw;
637                 }
638 
639                 if (curChr == '{')
640                     nodeStack.push_back(newChild);
641 
642                 curName.clear();
643             }
644             else if (curName.empty() == expectNode)
645                 throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
646 
647             if (curChr == '}')
648             {
649                 expectNode = false;
650                 nodeStack.pop_back();
651 
652                 // consume trailing new line
653                 if (nodeStack.empty())
654                 {
655                     if (in.peek() == '\r')
656                         in.get();
657                     if (in.peek() == '\n')
658                         in.get();
659                 }
660             }
661             else
662                 expectNode = true;
663         }
664         else if (isValidTestCaseNameChar((char)curChr))
665             curName += (char)curChr;
666         else
667             throw std::invalid_argument("Illegal character in node name");
668     }
669 }
670 
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)671 static void parseSimpleCaseList(vector<CaseTreeNode *> &nodeStack, std::istream &in, bool reportDuplicates,
672                                 std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
673 {
674     // \note Algorithm assumes that cases are sorted by groups, but will
675     //         function fine, albeit more slowly, if that is not the case.
676     int stackPos = 0;
677     string curName;
678 
679     for (;;)
680     {
681         const int curChr = in.get();
682 
683         if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
684         {
685             if (curName.empty())
686                 throw std::invalid_argument("Empty test case name");
687 
688             test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
689             if (!nodeStack[stackPos]->hasChild(hash))
690             {
691                 CaseTreeNode *const newChild = new CaseTreeNode(hash);
692 
693                 try
694                 {
695                     nodeStack[stackPos]->addChild(newChild);
696                 }
697                 catch (...)
698                 {
699                     delete newChild;
700                     throw;
701                 }
702             }
703             else if (reportDuplicates)
704                 throw std::invalid_argument("Duplicate test case");
705 
706             curName.clear();
707             stackPos = 0;
708 
709             if (curChr == '\r' && in.peek() == '\n')
710                 in.get();
711 
712             {
713                 const int nextChr = in.peek();
714 
715                 if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
716                     break;
717             }
718         }
719         else if (curChr == '.')
720         {
721             if (curName.empty())
722                 throw std::invalid_argument("Empty test group name");
723 
724             if ((int)nodeStack.size() <= stackPos + 1)
725                 nodeStack.resize(nodeStack.size() * 2, nullptr);
726 
727             test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
728             if (!nodeStack[stackPos + 1] || nodeStack[stackPos + 1]->getHash() != hash)
729             {
730                 CaseTreeNode *curGroup = nodeStack[stackPos]->getChild(hash);
731 
732                 if (!curGroup)
733                 {
734                     curGroup = new CaseTreeNode(hash);
735 
736                     try
737                     {
738                         nodeStack[stackPos]->addChild(curGroup);
739                     }
740                     catch (...)
741                     {
742                         delete curGroup;
743                         throw;
744                     }
745                 }
746 
747                 nodeStack[stackPos + 1] = curGroup;
748 
749                 if ((int)nodeStack.size() > stackPos + 2)
750                     nodeStack[stackPos + 2] = nullptr; // Invalidate rest of entries
751             }
752 
753             DE_ASSERT(nodeStack[stackPos + 1]->getHash() == hash);
754 
755             curName.clear();
756             stackPos += 1;
757         }
758         else if (isValidTestCaseNameChar((char)curChr))
759             curName += (char)curChr;
760         else
761             throw std::invalid_argument("Illegal character in test case name");
762     }
763 }
764 
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)765 static void parseCaseList(CaseTreeNode *root, std::istream &in, bool reportDuplicates,
766                           std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
767 {
768     vector<CaseTreeNode *> nodeStack(8, root);
769     parseSimpleCaseList(nodeStack, in, reportDuplicates, hashCollisionDetectionMap);
770 }
771 
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)772 static void parseGroupFile(CaseTreeNode *root, std::istream &inGroupList, const tcu::Archive &archive,
773                            bool reportDuplicates,
774                            std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
775 {
776     // read whole file and remove all '\r'
777     std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
778     buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
779 
780     vector<CaseTreeNode *> nodeStack(8, root);
781     std::stringstream namesStream(buffer);
782     std::string fileName;
783 
784     while (std::getline(namesStream, fileName))
785     {
786         de::FilePath groupPath(fileName);
787         de::UniquePtr<Resource> groupResource(archive.getResource(groupPath.normalize().getPath()));
788         const int groupBufferSize(groupResource->getSize());
789         std::vector<char> groupBuffer(static_cast<size_t>(groupBufferSize));
790 
791         groupResource->read(reinterpret_cast<uint8_t *>(&groupBuffer[0]), groupBufferSize);
792         if (groupBuffer.empty())
793             throw Exception("Empty case list resource");
794 
795         std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
796         parseSimpleCaseList(nodeStack, groupIn, reportDuplicates, hashCollisionDetectionMap);
797     }
798 }
799 
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=nullptr)800 static CaseTreeNode *parseCaseList(std::istream &in, const tcu::Archive &archive, const char *path = nullptr)
801 {
802     std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
803     auto rootName            = "";
804     test_case_hash_t hash    = hashTestNodeName(rootName, &hashCollisionDetectionMap);
805     CaseTreeNode *const root = new CaseTreeNode(hash);
806     try
807     {
808         if (in.peek() == '{')
809             parseCaseTrie(root, in, hashCollisionDetectionMap);
810         else
811         {
812             // if we are reading cases from file determine if we are
813             // reading group file or plain list of cases; this is done by
814             // reading single line and checking if it ends with ".txt"
815             bool readGroupFile = false;
816             if (path)
817             {
818                 // read the first line and make sure it doesn't contain '\r'
819                 std::string line;
820                 std::getline(in, line);
821                 line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
822 
823                 const std::string ending = ".txt";
824                 readGroupFile =
825                     (line.length() > ending.length()) && std::equal(ending.rbegin(), ending.rend(), line.rbegin());
826 
827                 // move to the beginning of the file to parse first line too
828                 in.seekg(0, in.beg);
829             }
830 
831             if (readGroupFile)
832                 parseGroupFile(root, in, archive, true, hashCollisionDetectionMap);
833             else
834                 parseCaseList(root, in, true, hashCollisionDetectionMap);
835         }
836 
837         {
838             const int curChr = in.get();
839             if (curChr != std::char_traits<char>::eof() && curChr != 0)
840                 throw std::invalid_argument("Trailing characters at end of case list");
841         }
842 
843         return root;
844     }
845     catch (...)
846     {
847         delete root;
848         throw;
849     }
850 }
851 
852 class CasePaths
853 {
854 public:
855     CasePaths(const string &pathList);
856     CasePaths(const vector<string> &pathList);
857     bool matches(const string &caseName, bool allowPrefix = false) const;
858 
859 private:
860     const vector<string> m_casePatterns;
861 };
862 
CasePaths(const string & pathList)863 CasePaths::CasePaths(const string &pathList) : m_casePatterns(de::splitString(pathList, ','))
864 {
865 }
866 
CasePaths(const vector<string> & pathList)867 CasePaths::CasePaths(const vector<string> &pathList) : m_casePatterns(pathList)
868 {
869 }
870 
871 // 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)872 bool matchWildcards(string::const_iterator patternStart, string::const_iterator patternEnd,
873                     string::const_iterator pathStart, string::const_iterator pathEnd, bool allowPrefix)
874 {
875     string::const_iterator pattern = patternStart;
876     string::const_iterator path    = pathStart;
877 
878     while (pattern != patternEnd && path != pathEnd && *pattern == *path)
879     {
880         ++pattern;
881         ++path;
882     }
883 
884     if (pattern == patternEnd)
885         return (path == pathEnd);
886     else if (*pattern == '*')
887     {
888         string::const_iterator patternNext = pattern + 1;
889         if (patternNext != patternEnd)
890         {
891             for (; path != pathEnd; ++path)
892             {
893                 if (*patternNext == *path)
894                     if (matchWildcards(patternNext, patternEnd, path, pathEnd, allowPrefix))
895                         return true;
896             }
897         }
898 
899         if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
900             return true;
901     }
902     else if (path == pathEnd && allowPrefix)
903         return true;
904 
905     return false;
906 }
907 
908 #if defined(TCU_HIERARCHICAL_CASEPATHS)
909 // Match a list of pattern components to a list of path components. A pattern
910 // component may contain *-wildcards. A pattern component "**" matches zero or
911 // 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)912 static bool patternMatches(vector<string>::const_iterator patternStart, vector<string>::const_iterator patternEnd,
913                            vector<string>::const_iterator pathStart, vector<string>::const_iterator pathEnd,
914                            bool allowPrefix)
915 {
916     vector<string>::const_iterator pattern = patternStart;
917     vector<string>::const_iterator path    = pathStart;
918 
919     while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
920            (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(), path->begin(), path->end(), false)))
921     {
922         ++pattern;
923         ++path;
924     }
925 
926     if (path == pathEnd && (allowPrefix || pattern == patternEnd))
927         return true;
928     else if (pattern != patternEnd && *pattern == "**")
929     {
930         for (; path != pathEnd; ++path)
931             if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
932                 return true;
933         if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
934             return true;
935     }
936 
937     return false;
938 }
939 #endif
940 
matches(const string & caseName,bool allowPrefix) const941 bool CasePaths::matches(const string &caseName, bool allowPrefix) const
942 {
943 #if defined(TCU_HIERARCHICAL_CASEPATHS)
944     const vector<string> components = de::splitString(caseName, '.');
945 #endif
946 
947     for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
948     {
949 #if defined(TCU_HIERARCHICAL_CASEPATHS)
950         const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
951 
952         if (patternMatches(patternComponents.begin(), patternComponents.end(), components.begin(), components.end(),
953                            allowPrefix))
954             return true;
955 #else
956         if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(), caseName.begin(), caseName.end(),
957                            allowPrefix))
958             return true;
959 #endif
960     }
961 
962     return false;
963 }
964 
965 /*--------------------------------------------------------------------*//*!
966  * \brief Construct command line
967  * \note CommandLine is not fully initialized until parse() has been called.
968  *//*--------------------------------------------------------------------*/
CommandLine(void)969 CommandLine::CommandLine(void) : m_appName(), m_logFlags(0), m_hadHelpSpecified(false)
970 {
971 }
972 
973 /*--------------------------------------------------------------------*//*!
974  * \brief Construct command line from standard argc, argv pair.
975  *
976  * Calls parse() with given arguments
977  * \param archive application's assets
978  * \param argc Number of arguments
979  * \param argv Command line arguments
980  *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)981 CommandLine::CommandLine(int argc, const char *const *argv)
982     : m_appName(argv[0])
983     , m_logFlags(0)
984     , m_hadHelpSpecified(false)
985 {
986     if (argc > 1)
987     {
988         int loop = 1; // skip application name
989         while (true)
990         {
991             m_initialCmdLine += std::string(argv[loop++]);
992             if (loop >= argc)
993                 break;
994             m_initialCmdLine += " ";
995         }
996     }
997 
998     if (!parse(argc, argv))
999     {
1000         if (m_hadHelpSpecified)
1001             exit(EXIT_SUCCESS);
1002         else
1003             throw Exception("Failed to parse command line");
1004     }
1005 }
1006 
1007 /*--------------------------------------------------------------------*//*!
1008  * \brief Construct command line from string.
1009  *
1010  * Calls parse() with given argument.
1011  * \param archive application's assets
1012  * \param cmdLine Full command line string.
1013  *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)1014 CommandLine::CommandLine(const std::string &cmdLine) : m_appName(), m_initialCmdLine(cmdLine), m_hadHelpSpecified(false)
1015 {
1016     if (!parse(cmdLine))
1017         throw Exception("Failed to parse command line");
1018 }
1019 
~CommandLine(void)1020 CommandLine::~CommandLine(void)
1021 {
1022 }
1023 
clear(void)1024 void CommandLine::clear(void)
1025 {
1026     m_cmdLine.clear();
1027     m_logFlags = 0;
1028 }
1029 
getCommandLine(void) const1030 const de::cmdline::CommandLine &CommandLine::getCommandLine(void) const
1031 {
1032     return m_cmdLine;
1033 }
1034 
getApplicationName(void) const1035 const std::string &CommandLine::getApplicationName(void) const
1036 {
1037     return m_appName;
1038 }
1039 
getInitialCmdLine(void) const1040 const std::string &CommandLine::getInitialCmdLine(void) const
1041 {
1042     return m_initialCmdLine;
1043 }
1044 
registerExtendedOptions(de::cmdline::Parser & parser)1045 void CommandLine::registerExtendedOptions(de::cmdline::Parser &parser)
1046 {
1047     DE_UNREF(parser);
1048 }
1049 
1050 /*--------------------------------------------------------------------*//*!
1051  * \brief Parse command line from standard argc, argv pair.
1052  * \note parse() must be called exactly once.
1053  * \param argc Number of arguments
1054  * \param argv Command line arguments
1055  *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)1056 bool CommandLine::parse(int argc, const char *const *argv)
1057 {
1058     DebugOutStreambuf sbuf;
1059     std::ostream debugOut(&sbuf);
1060     de::cmdline::Parser parser;
1061 
1062     opt::registerOptions(parser);
1063     opt::registerLegacyOptions(parser);
1064     registerExtendedOptions(parser);
1065 
1066     clear();
1067 
1068     if (!parser.parse(argc - 1, argv + 1, &m_cmdLine, std::cerr))
1069     {
1070         debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
1071         parser.help(debugOut);
1072 
1073         // We need to save this to avoid exiting with error later, and before the clear() call that wipes its value.
1074         m_hadHelpSpecified = m_cmdLine.helpSpecified();
1075 
1076         clear();
1077         return false;
1078     }
1079 
1080     if (!m_cmdLine.getOption<opt::LogImages>())
1081         m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
1082 
1083     if (!m_cmdLine.getOption<opt::LogShaderSources>())
1084         m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
1085 
1086     if (!m_cmdLine.getOption<opt::LogFlush>())
1087         m_logFlags |= QP_TEST_LOG_NO_FLUSH;
1088 
1089     if (m_cmdLine.getOption<opt::LogCompact>())
1090         m_logFlags |= QP_TEST_LOG_COMPACT;
1091 
1092     if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
1093         m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
1094 
1095     if (m_cmdLine.getOption<opt::SubProcess>())
1096         m_logFlags |= QP_TEST_LOG_NO_INITIAL_OUTPUT;
1097 
1098     if ((m_cmdLine.hasOption<opt::CasePath>() ? 1 : 0) + (m_cmdLine.hasOption<opt::CaseList>() ? 1 : 0) +
1099             (m_cmdLine.hasOption<opt::CaseListFile>() ? 1 : 0) +
1100             (m_cmdLine.hasOption<opt::CaseListResource>() ? 1 : 0) +
1101             (m_cmdLine.getOption<opt::StdinCaseList>() ? 1 : 0) >
1102         1)
1103     {
1104         debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
1105         clear();
1106         return false;
1107     }
1108 
1109     if (m_cmdLine.getArgs().size() > 0)
1110     {
1111         debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n"
1112                  << std::endl;
1113 
1114         debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
1115         parser.help(debugOut);
1116 
1117         clear();
1118         return false;
1119     }
1120 
1121     return true;
1122 }
1123 
1124 /*--------------------------------------------------------------------*//*!
1125  * \brief Parse command line from string.
1126  * \note parse() must be called exactly once.
1127  * \param cmdLine Full command line string.
1128  *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)1129 bool CommandLine::parse(const std::string &cmdLine)
1130 {
1131     deCommandLine *parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
1132     if (!parsedCmdLine)
1133         throw std::bad_alloc();
1134 
1135     bool isOk = false;
1136     try
1137     {
1138         isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
1139     }
1140     catch (...)
1141     {
1142         deCommandLine_destroy(parsedCmdLine);
1143         throw;
1144     }
1145 
1146     deCommandLine_destroy(parsedCmdLine);
1147     return isOk;
1148 }
1149 
quietMode(void) const1150 bool CommandLine::quietMode(void) const
1151 {
1152     return m_cmdLine.getOption<opt::QuietStdout>();
1153 }
getLogFileName(void) const1154 const char *CommandLine::getLogFileName(void) const
1155 {
1156     return m_cmdLine.getOption<opt::LogFilename>().c_str();
1157 }
getLogFlags(void) const1158 uint32_t CommandLine::getLogFlags(void) const
1159 {
1160     return m_logFlags;
1161 }
getRunMode(void) const1162 RunMode CommandLine::getRunMode(void) const
1163 {
1164     return m_cmdLine.getOption<opt::RunMode>();
1165 }
getCaseListExportFile(void) const1166 const char *CommandLine::getCaseListExportFile(void) const
1167 {
1168     return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str();
1169 }
getVisibility(void) const1170 WindowVisibility CommandLine::getVisibility(void) const
1171 {
1172     return m_cmdLine.getOption<opt::Visibility>();
1173 }
isWatchDogEnabled(void) const1174 bool CommandLine::isWatchDogEnabled(void) const
1175 {
1176     return m_cmdLine.getOption<opt::WatchDog>();
1177 }
isCrashHandlingEnabled(void) const1178 bool CommandLine::isCrashHandlingEnabled(void) const
1179 {
1180     return m_cmdLine.getOption<opt::CrashHandler>();
1181 }
getBaseSeed(void) const1182 int CommandLine::getBaseSeed(void) const
1183 {
1184     return m_cmdLine.getOption<opt::BaseSeed>();
1185 }
getTestIterationCount(void) const1186 int CommandLine::getTestIterationCount(void) const
1187 {
1188     return m_cmdLine.getOption<opt::TestIterationCount>();
1189 }
getSurfaceWidth(void) const1190 int CommandLine::getSurfaceWidth(void) const
1191 {
1192     return m_cmdLine.getOption<opt::SurfaceWidth>();
1193 }
getSurfaceHeight(void) const1194 int CommandLine::getSurfaceHeight(void) const
1195 {
1196     return m_cmdLine.getOption<opt::SurfaceHeight>();
1197 }
getSurfaceType(void) const1198 SurfaceType CommandLine::getSurfaceType(void) const
1199 {
1200     return m_cmdLine.getOption<opt::SurfaceType>();
1201 }
getScreenRotation(void) const1202 ScreenRotation CommandLine::getScreenRotation(void) const
1203 {
1204     return m_cmdLine.getOption<opt::ScreenRotation>();
1205 }
getGLConfigId(void) const1206 int CommandLine::getGLConfigId(void) const
1207 {
1208     return m_cmdLine.getOption<opt::GLConfigID>();
1209 }
getCLPlatformId(void) const1210 int CommandLine::getCLPlatformId(void) const
1211 {
1212     return m_cmdLine.getOption<opt::CLPlatformID>();
1213 }
getCLDeviceIds(void) const1214 const std::vector<int> &CommandLine::getCLDeviceIds(void) const
1215 {
1216     return m_cmdLine.getOption<opt::CLDeviceIDs>();
1217 }
getVKDeviceId(void) const1218 int CommandLine::getVKDeviceId(void) const
1219 {
1220     return m_cmdLine.getOption<opt::VKDeviceID>();
1221 }
getVKDeviceGroupId(void) const1222 int CommandLine::getVKDeviceGroupId(void) const
1223 {
1224     return m_cmdLine.getOption<opt::VKDeviceGroupID>();
1225 }
isValidationEnabled(void) const1226 bool CommandLine::isValidationEnabled(void) const
1227 {
1228     return m_cmdLine.getOption<opt::Validation>();
1229 }
isSpirvValidationEnabled(void) const1230 bool CommandLine::isSpirvValidationEnabled(void) const
1231 {
1232     return m_cmdLine.getOption<opt::SpirvValidation>();
1233 }
printValidationErrors(void) const1234 bool CommandLine::printValidationErrors(void) const
1235 {
1236     return m_cmdLine.getOption<opt::PrintValidationErrors>();
1237 }
checkDuplicateCaseNames(void) const1238 bool CommandLine::checkDuplicateCaseNames(void) const
1239 {
1240     return m_cmdLine.getOption<opt::DuplicateCheck>();
1241 }
isLogDecompiledSpirvEnabled(void) const1242 bool CommandLine::isLogDecompiledSpirvEnabled(void) const
1243 {
1244     return m_cmdLine.getOption<opt::LogDecompiledSpirv>();
1245 }
isOutOfMemoryTestEnabled(void) const1246 bool CommandLine::isOutOfMemoryTestEnabled(void) const
1247 {
1248     return m_cmdLine.getOption<opt::TestOOM>();
1249 }
isShadercacheEnabled(void) const1250 bool CommandLine::isShadercacheEnabled(void) const
1251 {
1252     return m_cmdLine.getOption<opt::ShaderCache>();
1253 }
getShaderCacheFilename(void) const1254 const char *CommandLine::getShaderCacheFilename(void) const
1255 {
1256     return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();
1257 }
isShaderCacheTruncateEnabled(void) const1258 bool CommandLine::isShaderCacheTruncateEnabled(void) const
1259 {
1260     return m_cmdLine.getOption<opt::ShaderCacheTruncate>();
1261 }
isShaderCacheIPCEnabled(void) const1262 bool CommandLine::isShaderCacheIPCEnabled(void) const
1263 {
1264     return m_cmdLine.getOption<opt::ShaderCacheIPC>();
1265 }
getOptimizationRecipe(void) const1266 int CommandLine::getOptimizationRecipe(void) const
1267 {
1268     return m_cmdLine.getOption<opt::Optimization>();
1269 }
isSpirvOptimizationEnabled(void) const1270 bool CommandLine::isSpirvOptimizationEnabled(void) const
1271 {
1272     return m_cmdLine.getOption<opt::OptimizeSpirv>();
1273 }
isRenderDocEnabled(void) const1274 bool CommandLine::isRenderDocEnabled(void) const
1275 {
1276     return m_cmdLine.getOption<opt::RenderDoc>();
1277 }
getWaiverFileName(void) const1278 const char *CommandLine::getWaiverFileName(void) const
1279 {
1280     return m_cmdLine.getOption<opt::WaiverFile>().c_str();
1281 }
getCaseFraction(void) const1282 const std::vector<int> &CommandLine::getCaseFraction(void) const
1283 {
1284     return m_cmdLine.getOption<opt::CaseFraction>();
1285 }
getCaseFractionMandatoryTests(void) const1286 const char *CommandLine::getCaseFractionMandatoryTests(void) const
1287 {
1288     return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str();
1289 }
getArchiveDir(void) const1290 const char *CommandLine::getArchiveDir(void) const
1291 {
1292     return m_cmdLine.getOption<opt::ArchiveDir>().c_str();
1293 }
getRunnerType(void) const1294 tcu::TestRunnerType CommandLine::getRunnerType(void) const
1295 {
1296     return m_cmdLine.getOption<opt::RunnerType>();
1297 }
isTerminateOnFailEnabled(void) const1298 bool CommandLine::isTerminateOnFailEnabled(void) const
1299 {
1300     return m_cmdLine.getOption<opt::TerminateOnFail>();
1301 }
isTerminateOnDeviceLostEnabled(void) const1302 bool CommandLine::isTerminateOnDeviceLostEnabled(void) const
1303 {
1304     return m_cmdLine.getOption<opt::TerminateOnDeviceLost>();
1305 }
isSubProcess(void) const1306 bool CommandLine::isSubProcess(void) const
1307 {
1308     return m_cmdLine.getOption<opt::SubProcess>();
1309 }
getSubprocessTestCount(void) const1310 int CommandLine::getSubprocessTestCount(void) const
1311 {
1312     return m_cmdLine.getOption<opt::SubprocessTestCount>();
1313 }
getCommandPoolMinSize(void) const1314 int CommandLine::getCommandPoolMinSize(void) const
1315 {
1316     return m_cmdLine.getOption<opt::CommandPoolMinSize>();
1317 }
getCommandBufferMinSize(void) const1318 int CommandLine::getCommandBufferMinSize(void) const
1319 {
1320     return m_cmdLine.getOption<opt::CommandBufferMinSize>();
1321 }
getCommandDefaultSize(void) const1322 int CommandLine::getCommandDefaultSize(void) const
1323 {
1324     return m_cmdLine.getOption<opt::CommandDefaultSize>();
1325 }
getPipelineDefaultSize(void) const1326 int CommandLine::getPipelineDefaultSize(void) const
1327 {
1328     return m_cmdLine.getOption<opt::PipelineDefaultSize>();
1329 }
isComputeOnly(void) const1330 bool CommandLine::isComputeOnly(void) const
1331 {
1332     return m_cmdLine.getOption<opt::ComputeOnly>();
1333 }
1334 
getGLContextType(void) const1335 const char *CommandLine::getGLContextType(void) const
1336 {
1337     if (m_cmdLine.hasOption<opt::GLContextType>())
1338         return m_cmdLine.getOption<opt::GLContextType>().c_str();
1339     else
1340         return nullptr;
1341 }
getGLConfigName(void) const1342 const char *CommandLine::getGLConfigName(void) const
1343 {
1344     if (m_cmdLine.hasOption<opt::GLConfigName>())
1345         return m_cmdLine.getOption<opt::GLConfigName>().c_str();
1346     else
1347         return nullptr;
1348 }
1349 
getGLContextFlags(void) const1350 const char *CommandLine::getGLContextFlags(void) const
1351 {
1352     if (m_cmdLine.hasOption<opt::GLContextFlags>())
1353         return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
1354     else
1355         return nullptr;
1356 }
1357 
getCLBuildOptions(void) const1358 const char *CommandLine::getCLBuildOptions(void) const
1359 {
1360     if (m_cmdLine.hasOption<opt::CLBuildOptions>())
1361         return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
1362     else
1363         return nullptr;
1364 }
1365 
getEGLDisplayType(void) const1366 const char *CommandLine::getEGLDisplayType(void) const
1367 {
1368     if (m_cmdLine.hasOption<opt::EGLDisplayType>())
1369         return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
1370     else
1371         return nullptr;
1372 }
1373 
getEGLWindowType(void) const1374 const char *CommandLine::getEGLWindowType(void) const
1375 {
1376     if (m_cmdLine.hasOption<opt::EGLWindowType>())
1377         return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
1378     else
1379         return nullptr;
1380 }
1381 
getEGLPixmapType(void) const1382 const char *CommandLine::getEGLPixmapType(void) const
1383 {
1384     if (m_cmdLine.hasOption<opt::EGLPixmapType>())
1385         return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
1386     else
1387         return nullptr;
1388 }
1389 
getSubprocessConfigFile(void) const1390 const char *CommandLine::getSubprocessConfigFile(void) const
1391 {
1392     if (m_cmdLine.hasOption<opt::SubprocessConfigFile>())
1393         return m_cmdLine.getOption<opt::SubprocessConfigFile>().c_str();
1394     else
1395         return nullptr;
1396 }
1397 
getServerAddress(void) const1398 const char *CommandLine::getServerAddress(void) const
1399 {
1400     if (m_cmdLine.hasOption<opt::ServerAddress>())
1401         return m_cmdLine.getOption<opt::ServerAddress>().c_str();
1402     else
1403         return nullptr;
1404 }
1405 
getPipelineCompilerPath(void) const1406 const char *CommandLine::getPipelineCompilerPath(void) const
1407 {
1408     if (m_cmdLine.hasOption<opt::PipelineCompilerPath>())
1409         return m_cmdLine.getOption<opt::PipelineCompilerPath>().c_str();
1410     else
1411         return nullptr;
1412 }
1413 
getPipelineCompilerDataDir(void) const1414 const char *CommandLine::getPipelineCompilerDataDir(void) const
1415 {
1416     if (m_cmdLine.hasOption<opt::PipelineCompilerDataDir>())
1417         return m_cmdLine.getOption<opt::PipelineCompilerDataDir>().c_str();
1418     else
1419         return nullptr;
1420 }
1421 
getPipelineCompilerArgs(void) const1422 const char *CommandLine::getPipelineCompilerArgs(void) const
1423 {
1424     if (m_cmdLine.hasOption<opt::PipelineCompilerArgs>())
1425         return m_cmdLine.getOption<opt::PipelineCompilerArgs>().c_str();
1426     else
1427         return nullptr;
1428 }
1429 
getPipelineCompilerOutputFile(void) const1430 const char *CommandLine::getPipelineCompilerOutputFile(void) const
1431 {
1432     if (m_cmdLine.hasOption<opt::PipelineCompilerOutputFile>())
1433         return m_cmdLine.getOption<opt::PipelineCompilerOutputFile>().c_str();
1434     else
1435         return nullptr;
1436 }
1437 
getPipelineCompilerLogFile(void) const1438 const char *CommandLine::getPipelineCompilerLogFile(void) const
1439 {
1440     if (m_cmdLine.hasOption<opt::PipelineCompilerLogFile>())
1441         return m_cmdLine.getOption<opt::PipelineCompilerLogFile>().c_str();
1442     else
1443         return nullptr;
1444 }
1445 
getPipelineCompilerFilePrefix(void) const1446 const char *CommandLine::getPipelineCompilerFilePrefix(void) const
1447 {
1448     if (m_cmdLine.hasOption<opt::PipelineCompilerFilePrefix>())
1449         return m_cmdLine.getOption<opt::PipelineCompilerFilePrefix>().c_str();
1450     else
1451         return nullptr;
1452 }
1453 
getVkLibraryPath(void) const1454 const char *CommandLine::getVkLibraryPath(void) const
1455 {
1456     if (m_cmdLine.hasOption<opt::VkLibraryPath>())
1457         return (m_cmdLine.getOption<opt::VkLibraryPath>() != "") ? m_cmdLine.getOption<opt::VkLibraryPath>().c_str() :
1458                                                                    nullptr;
1459     else
1460         return nullptr;
1461 }
1462 
getAppParamsInputFilePath(void) const1463 const char *CommandLine::getAppParamsInputFilePath(void) const
1464 {
1465     if (m_cmdLine.hasOption<opt::ApplicationParametersInputFile>())
1466         return m_cmdLine.getOption<opt::ApplicationParametersInputFile>().c_str();
1467     else
1468         return nullptr;
1469 }
1470 
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)1471 static bool checkTestGroupName(const CaseTreeNode *root, const char *groupPath)
1472 {
1473     const CaseTreeNode *node = findNode(root, groupPath);
1474     return node && node->hasChildren();
1475 }
1476 
checkTestCaseName(const CaseTreeNode * root,const char * casePath)1477 static bool checkTestCaseName(const CaseTreeNode *root, const char *casePath)
1478 {
1479     const CaseTreeNode *node = findNode(root, casePath);
1480     return node && !node->hasChildren();
1481 }
1482 
createCaseListFilter(const tcu::Archive & archive) const1483 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter(const tcu::Archive &archive) const
1484 {
1485     return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1486 }
1487 
checkTestGroupName(const char * groupName) const1488 bool CaseListFilter::checkTestGroupName(const char *groupName) const
1489 {
1490     bool result = false;
1491     if (m_casePaths)
1492         result = m_casePaths->matches(groupName, true);
1493     else if (m_caseTree)
1494         result = (groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName));
1495     else
1496         return true;
1497     if (!result && m_caseFractionMandatoryTests.get() != nullptr)
1498         result = m_caseFractionMandatoryTests->matches(groupName, true);
1499     return result;
1500 }
1501 
checkTestCaseName(const char * caseName) const1502 bool CaseListFilter::checkTestCaseName(const char *caseName) const
1503 {
1504     bool result = false;
1505     if (m_casePaths)
1506         result = m_casePaths->matches(caseName, false);
1507     else if (m_caseTree)
1508         result = tcu::checkTestCaseName(m_caseTree, caseName);
1509     else
1510         return true;
1511     if (!result && m_caseFractionMandatoryTests.get() != nullptr)
1512         result = m_caseFractionMandatoryTests->matches(caseName, false);
1513     return result;
1514 }
1515 
checkCaseFraction(int i,const std::string & testCaseName) const1516 bool CaseListFilter::checkCaseFraction(int i, const std::string &testCaseName) const
1517 {
1518     return m_caseFraction.size() != 2 || ((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1519            (m_caseFractionMandatoryTests.get() != nullptr && m_caseFractionMandatoryTests->matches(testCaseName));
1520 }
1521 
CaseListFilter(void)1522 CaseListFilter::CaseListFilter(void) : m_caseTree(nullptr), m_runnerType(tcu::RUNNERTYPE_ANY)
1523 {
1524 }
1525 
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1526 CaseListFilter::CaseListFilter(const de::cmdline::CommandLine &cmdLine, const tcu::Archive &archive)
1527     : m_caseTree(nullptr)
1528 {
1529     if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1530     {
1531         m_runnerType = RUNNERTYPE_AMBER;
1532     }
1533     else
1534     {
1535         m_runnerType = cmdLine.getOption<opt::RunnerType>();
1536     }
1537 
1538     if (cmdLine.hasOption<opt::CaseList>())
1539     {
1540         std::istringstream str(cmdLine.getOption<opt::CaseList>());
1541 
1542         m_caseTree = parseCaseList(str, archive);
1543     }
1544     else if (cmdLine.hasOption<opt::CaseListFile>())
1545     {
1546         std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1547         std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1548 
1549         if (!in.is_open() || !in.good())
1550             throw Exception("Failed to open case list file '" + caseListFile + "'");
1551 
1552         m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1553     }
1554     else if (cmdLine.hasOption<opt::CaseListResource>())
1555     {
1556         // \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1557         //                           istream adaptor for tcu::Resource.
1558         de::UniquePtr<Resource> caseListResource(
1559             archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1560         const int bufferSize = caseListResource->getSize();
1561         std::vector<char> buffer((size_t)bufferSize);
1562 
1563         if (buffer.empty())
1564             throw Exception("Empty case list resource");
1565 
1566         caseListResource->read(reinterpret_cast<uint8_t *>(&buffer[0]), bufferSize);
1567 
1568         {
1569             std::istringstream in(std::string(&buffer[0], (size_t)bufferSize));
1570 
1571             m_caseTree = parseCaseList(in, archive);
1572         }
1573     }
1574     else if (cmdLine.getOption<opt::StdinCaseList>())
1575     {
1576         m_caseTree = parseCaseList(std::cin, archive);
1577     }
1578     else if (cmdLine.hasOption<opt::CasePath>())
1579         m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1580 
1581     if (!cmdLine.getOption<opt::SubProcess>())
1582         m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1583 
1584     if (m_caseFraction.size() == 2 &&
1585         (m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1]))
1586         throw Exception("Invalid case fraction. First element must be non-negative and less than second element. "
1587                         "Second element must be greater than 0.");
1588 
1589     if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1590         throw Exception("Invalid case fraction. Must have two components.");
1591 
1592     if (m_caseFraction.size() == 2)
1593     {
1594         std::string caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1595 
1596         if (!caseFractionMandatoryTestsFilename.empty())
1597         {
1598             std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1599             if (!fileStream.is_open() || !fileStream.good())
1600                 throw Exception("Failed to open case fraction mandatory test list: '" +
1601                                 caseFractionMandatoryTestsFilename + "'");
1602 
1603             std::vector<std::string> cfPaths;
1604             std::string line;
1605 
1606             while (std::getline(fileStream, line))
1607             {
1608                 line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1609                 cfPaths.push_back(line);
1610             }
1611             if (!cfPaths.empty())
1612             {
1613                 m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1614                 if (m_caseTree != nullptr)
1615                 {
1616                     fileStream.clear();
1617                     fileStream.seekg(0, fileStream.beg);
1618                     std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
1619                     parseCaseList(m_caseTree, fileStream, false, hashCollisionDetectionMap);
1620                 }
1621             }
1622         }
1623     }
1624 }
1625 
~CaseListFilter(void)1626 CaseListFilter::~CaseListFilter(void)
1627 {
1628     delete m_caseTree;
1629 }
1630 
1631 } // namespace tcu
1632