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