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(SubProcess, bool);
112 DE_DECLARE_COMMAND_LINE_OPT(SubprocessTestCount, int);
113 DE_DECLARE_COMMAND_LINE_OPT(SubprocessConfigFile, std::string);
114 DE_DECLARE_COMMAND_LINE_OPT(ServerAddress, std::string);
115 DE_DECLARE_COMMAND_LINE_OPT(CommandPoolMinSize, int);
116 DE_DECLARE_COMMAND_LINE_OPT(CommandBufferMinSize, int);
117 DE_DECLARE_COMMAND_LINE_OPT(CommandDefaultSize, int);
118 DE_DECLARE_COMMAND_LINE_OPT(PipelineDefaultSize, int);
119 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath, std::string);
120 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir, std::string);
121 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs, std::string);
122 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile, std::string);
123 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile, std::string);
124 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerFilePrefix, std::string);
125 DE_DECLARE_COMMAND_LINE_OPT(VkLibraryPath, std::string);
126 DE_DECLARE_COMMAND_LINE_OPT(ApplicationParametersInputFile, std::string);
127
128
parseIntList(const char * src,std::vector<int> * dst)129 static void parseIntList (const char* src, std::vector<int>* dst)
130 {
131 std::istringstream str (src);
132 std::string val;
133
134 while (std::getline(str, val, ','))
135 {
136 int intVal = 0;
137 de::cmdline::parseType(val.c_str(), &intVal);
138 dst->push_back(intVal);
139 }
140 }
141
registerOptions(de::cmdline::Parser & parser)142 void registerOptions (de::cmdline::Parser& parser)
143 {
144 using de::cmdline::Option;
145 using de::cmdline::NamedValue;
146
147 static const NamedValue<bool> s_enableNames[] =
148 {
149 { "enable", true },
150 { "disable", false }
151 };
152 static const NamedValue<tcu::RunMode> s_runModes[] =
153 {
154 { "execute", RUNMODE_EXECUTE },
155 { "xml-caselist", RUNMODE_DUMP_XML_CASELIST },
156 { "txt-caselist", RUNMODE_DUMP_TEXT_CASELIST },
157 { "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST },
158 { "amber-verify", RUNMODE_VERIFY_AMBER_COHERENCY}
159 };
160 static const NamedValue<WindowVisibility> s_visibilites[] =
161 {
162 { "windowed", WINDOWVISIBILITY_WINDOWED },
163 { "fullscreen", WINDOWVISIBILITY_FULLSCREEN },
164 { "hidden", WINDOWVISIBILITY_HIDDEN }
165 };
166 static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
167 {
168 { "window", SURFACETYPE_WINDOW },
169 { "pixmap", SURFACETYPE_OFFSCREEN_NATIVE },
170 { "pbuffer", SURFACETYPE_OFFSCREEN_GENERIC },
171 { "fbo", SURFACETYPE_FBO }
172 };
173 static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
174 {
175 { "unspecified", SCREENROTATION_UNSPECIFIED },
176 { "0", SCREENROTATION_0 },
177 { "90", SCREENROTATION_90 },
178 { "180", SCREENROTATION_180 },
179 { "270", SCREENROTATION_270 }
180 };
181 static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
182 {
183 { "any", tcu::RUNNERTYPE_ANY },
184 { "none", tcu::RUNNERTYPE_NONE },
185 { "amber", tcu::RUNNERTYPE_AMBER },
186 };
187
188 parser
189 << Option<CasePath> ("n", "deqp-case", "Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
190 << Option<CaseList> (DE_NULL, "deqp-caselist", "Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
191 << Option<CaseListFile> (DE_NULL, "deqp-caselist-file", "Read case list (in trie format) from given file")
192 << Option<CaseListResource> (DE_NULL, "deqp-caselist-resource", "Read case list (in trie format) from given file located application's assets")
193 << Option<StdinCaseList> (DE_NULL, "deqp-stdin-caselist", "Read case list (in trie format) from stdin")
194 << Option<LogFilename> (DE_NULL, "deqp-log-filename", "Write test results to given file", "TestResults.qpa")
195 << Option<RunMode> (DE_NULL, "deqp-runmode", "Execute tests, write list of test cases into a file, or verify amber capability coherency",
196 s_runModes, "execute")
197 << Option<ExportFilenamePattern> (DE_NULL, "deqp-caselist-export-file", "Set the target file name pattern for caselist export", "${packageName}-cases.${typeExtension}")
198 << Option<WatchDog> (DE_NULL, "deqp-watchdog", "Enable test watchdog", s_enableNames, "disable")
199 << Option<CrashHandler> (DE_NULL, "deqp-crashhandler", "Enable crash handling", s_enableNames, "disable")
200 << Option<BaseSeed> (DE_NULL, "deqp-base-seed", "Base seed for test cases that use randomization", "0")
201 << Option<TestIterationCount> (DE_NULL, "deqp-test-iteration-count", "Iteration count for cases that support variable number of iterations", "0")
202 << Option<Visibility> (DE_NULL, "deqp-visibility", "Default test window visibility", s_visibilites, "windowed")
203 << Option<SurfaceWidth> (DE_NULL, "deqp-surface-width", "Use given surface width if possible", "-1")
204 << Option<SurfaceHeight> (DE_NULL, "deqp-surface-height", "Use given surface height if possible", "-1")
205 << Option<SurfaceType> (DE_NULL, "deqp-surface-type", "Use given surface type", s_surfaceTypes, "window")
206 << Option<ScreenRotation> (DE_NULL, "deqp-screen-rotation", "Screen rotation for platforms that support it", s_screenRotations, "0")
207 << Option<GLContextType> (DE_NULL, "deqp-gl-context-type", "OpenGL context type for platforms that support multiple")
208 << Option<GLConfigID> (DE_NULL, "deqp-gl-config-id", "OpenGL (ES) render config ID (EGL config id on EGL platforms)", "-1")
209 << Option<GLConfigName> (DE_NULL, "deqp-gl-config-name", "Symbolic OpenGL (ES) render config name")
210 << Option<GLContextFlags> (DE_NULL, "deqp-gl-context-flags", "OpenGL context flags (comma-separated, supports debug and robust)")
211 << Option<CLPlatformID> (DE_NULL, "deqp-cl-platform-id", "Execute tests on given OpenCL platform (IDs start from 1)", "1")
212 << Option<CLDeviceIDs> (DE_NULL, "deqp-cl-device-ids", "Execute tests on given CL devices (comma-separated, IDs start from 1)", parseIntList, "")
213 << Option<CLBuildOptions> (DE_NULL, "deqp-cl-build-options", "Extra build options for OpenCL compiler")
214 << Option<EGLDisplayType> (DE_NULL, "deqp-egl-display-type", "EGL native display type")
215 << Option<EGLWindowType> (DE_NULL, "deqp-egl-window-type", "EGL native window type")
216 << Option<EGLPixmapType> (DE_NULL, "deqp-egl-pixmap-type", "EGL native pixmap type")
217 << Option<VKDeviceID> (DE_NULL, "deqp-vk-device-id", "Vulkan device ID (IDs start from 1)", "1")
218 << Option<VKDeviceGroupID> (DE_NULL, "deqp-vk-device-group-id", "Vulkan device Group ID (IDs start from 1)", "1")
219 << Option<LogImages> (DE_NULL, "deqp-log-images", "Enable or disable logging of result images", s_enableNames, "enable")
220 << Option<LogShaderSources> (DE_NULL, "deqp-log-shader-sources", "Enable or disable logging of shader sources", s_enableNames, "enable")
221 << Option<LogDecompiledSpirv> (DE_NULL, "deqp-log-decompiled-spirv", "Enable or disable logging of decompiled spir-v", s_enableNames, "enable")
222 << Option<LogEmptyLoginfo> (DE_NULL, "deqp-log-empty-loginfo", "Logging of empty shader compile/link log info", s_enableNames, "enable")
223 << Option<TestOOM> (DE_NULL, "deqp-test-oom", "Run tests that exhaust memory on purpose", s_enableNames, TEST_OOM_DEFAULT)
224 << Option<ArchiveDir> (DE_NULL, "deqp-archive-dir", "Path to test resource files", ".")
225 << Option<LogFlush> (DE_NULL, "deqp-log-flush", "Enable or disable log file fflush", s_enableNames, "enable")
226 << Option<LogCompact> (DE_NULL, "deqp-log-compact", "Enable or disable the compact version of the log", s_enableNames, "disable")
227 << Option<Validation> (DE_NULL, "deqp-validation", "Enable or disable test case validation", s_enableNames, "disable")
228 << Option<PrintValidationErrors> (DE_NULL, "deqp-print-validation-errors", "Print validation errors to standard error")
229 << Option<Optimization> (DE_NULL, "deqp-optimization-recipe", "Shader optimization recipe (0=disabled, 1=performance, 2=size)", "0")
230 << Option<OptimizeSpirv> (DE_NULL, "deqp-optimize-spirv", "Apply optimization to spir-v shaders as well", s_enableNames, "disable")
231 << Option<ShaderCache> (DE_NULL, "deqp-shadercache", "Enable or disable shader cache", s_enableNames, "enable")
232 << Option<ShaderCacheFilename> (DE_NULL, "deqp-shadercache-filename", "Write shader cache to given file", "shadercache.bin")
233 << Option<ShaderCacheTruncate> (DE_NULL, "deqp-shadercache-truncate", "Truncate shader cache before running tests", s_enableNames, "enable")
234 << Option<ShaderCacheIPC> (DE_NULL, "deqp-shadercache-ipc", "Should shader cache use inter process comms", s_enableNames, "disable")
235 << Option<RenderDoc> (DE_NULL, "deqp-renderdoc", "Enable RenderDoc frame markers", s_enableNames, "disable")
236 << Option<CaseFraction> (DE_NULL, "deqp-fraction", "Run a fraction of the test cases (e.g. N,M means run group%M==N)", parseIntList, "")
237 << Option<CaseFractionMandatoryTests> (DE_NULL, "deqp-fraction-mandatory-caselist-file", "Case list file that must be run for each fraction", "")
238 << Option<WaiverFile> (DE_NULL, "deqp-waiver-file", "Read waived tests from given file", "")
239 << Option<RunnerType> (DE_NULL, "deqp-runner-type", "Filter test cases based on runner", s_runnerTypes, "any")
240 << Option<TerminateOnFail> (DE_NULL, "deqp-terminate-on-fail", "Terminate the run on first failure", s_enableNames, "disable")
241 << Option<SubProcess> (DE_NULL, "deqp-subprocess", "Inform app that it works as subprocess (Vulkan SC only, do not use manually)", s_enableNames, "disable")
242 << Option<SubprocessTestCount> (DE_NULL, "deqp-subprocess-test-count", "Define default number of tests performed in subprocess for specific test cases(Vulkan SC only)", "65536")
243 << Option<SubprocessConfigFile> (DE_NULL, "deqp-subprocess-cfg-file", "Config file defining number of tests performed in subprocess for specific test branches (Vulkan SC only)", "")
244 << Option<ServerAddress> (DE_NULL, "deqp-server-address", "Server address (host:port) responsible for shader compilation (Vulkan SC only)", "")
245 << Option<CommandPoolMinSize> (DE_NULL, "deqp-command-pool-min-size", "Define minimum size of the command pool (in bytes) to use (Vulkan SC only)","0")
246 << Option<CommandBufferMinSize> (DE_NULL, "deqp-command-buffer-min-size", "Define minimum size of the command buffer (in bytes) to use (Vulkan SC only)", "0")
247 << Option<CommandDefaultSize> (DE_NULL, "deqp-command-default-size", "Define default single command size (in bytes) to use (Vulkan SC only)", "256")
248 << Option<PipelineDefaultSize> (DE_NULL, "deqp-pipeline-default-size", "Define default pipeline size (in bytes) to use (Vulkan SC only)", "16384")
249 << Option<PipelineCompilerPath> (DE_NULL, "deqp-pipeline-compiler", "Path to offline pipeline compiler (Vulkan SC only)", "")
250 << Option<PipelineCompilerDataDir> (DE_NULL, "deqp-pipeline-dir", "Offline pipeline data directory (Vulkan SC only)", "")
251 << Option<PipelineCompilerArgs> (DE_NULL, "deqp-pipeline-args", "Additional compiler parameters (Vulkan SC only)", "")
252 << Option<PipelineCompilerOutputFile> (DE_NULL, "deqp-pipeline-file", "Output file with pipeline cache (Vulkan SC only, do not use manually)", "")
253 << Option<PipelineCompilerLogFile> (DE_NULL, "deqp-pipeline-logfile", "Log file for pipeline compiler (Vulkan SC only, do not use manually)", "")
254 << Option<PipelineCompilerFilePrefix> (DE_NULL, "deqp-pipeline-prefix", "Prefix for input pipeline compiler files (Vulkan SC only, do not use manually)", "")
255 << Option<VkLibraryPath> (DE_NULL, "deqp-vk-library-path", "Path to Vulkan library (e.g. loader library vulkan-1.dll)", "")
256 << Option<ApplicationParametersInputFile> (DE_NULL, "deqp-app-params-input-file", "File that provides a default set of application parameters");
257 }
258
registerLegacyOptions(de::cmdline::Parser & parser)259 void registerLegacyOptions (de::cmdline::Parser& parser)
260 {
261 using de::cmdline::Option;
262
263 parser
264 << Option<GLConfigID> (DE_NULL, "deqp-egl-config-id", "Legacy name for --deqp-gl-config-id", "-1")
265 << Option<GLConfigName> (DE_NULL, "deqp-egl-config-name", "Legacy name for --deqp-gl-config-name");
266 }
267
268 } // opt
269
270
271 // Used to store hashes of test case names
272 typedef uint64_t test_case_hash_t;
273
274 // Source: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp
275 // MurmurHash2, 64-bit versions, by Austin Appleby
MurmurHash64B(const void * key,int len,uint64_t seed)276 static uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed )
277 {
278 const uint32_t m = 0x5bd1e995;
279 const int r = 24;
280
281 uint32_t h1 = uint32_t(seed) ^ len;
282 uint32_t h2 = uint32_t(seed >> 32);
283
284 // Ensure that unaligned accesses to data are allowed.
285 #ifdef WIN32
286 typedef __declspec(align(1)) uint32_t uint32_t_unaligned;
287 #else
288 typedef __attribute__((aligned(1))) uint32_t uint32_t_unaligned;
289 #endif
290 const uint32_t_unaligned * data = (const uint32_t_unaligned *)key;
291
292 while(len >= 8)
293 {
294 uint32_t k1 = *data++;
295 k1 *= m; k1 ^= k1 >> r; k1 *= m;
296 h1 *= m; h1 ^= k1;
297 len -= 4;
298
299 uint32_t k2 = *data++;
300 k2 *= m; k2 ^= k2 >> r; k2 *= m;
301 h2 *= m; h2 ^= k2;
302 len -= 4;
303 }
304
305 if(len >= 4)
306 {
307 uint32_t k1 = *data++;
308 k1 *= m; k1 ^= k1 >> r; k1 *= m;
309 h1 *= m; h1 ^= k1;
310 len -= 4;
311 }
312
313 switch(len)
314 {
315 case 3: h2 ^= ((unsigned char*)data)[2] << 16;
316 // fall through
317 case 2: h2 ^= ((unsigned char*)data)[1] << 8;
318 // fall through
319 case 1: h2 ^= ((unsigned char*)data)[0];
320 h2 *= m;
321 };
322
323 h1 ^= h2 >> 18; h1 *= m;
324 h2 ^= h1 >> 22; h2 *= m;
325 h1 ^= h2 >> 17; h1 *= m;
326 h2 ^= h1 >> 19; h2 *= m;
327
328 uint64_t h = h1;
329
330 h = (h << 32) | h2;
331
332 return h;
333 }
334
335 /*--------------------------------------------------------------------*//*!
336 * \brief Generates an hash for the test case name part provided.
337 * If a hashCollisionDetectionMap is passed, will detect hash
338 * collisions using that map. hashCollisionDetectionMap can be NULL.
339 * As an example, the standard std::hash<std::string> on a 32-bit
340 * machine will collide with 'random_298' and 'subgroupand_int16_t_mesh_requiredsubgroupsize'
341 *//*--------------------------------------------------------------------*/
hashTestNodeName(const std::string & name,std::unordered_map<test_case_hash_t,std::string> * hashCollisionDetectionMap)342 static test_case_hash_t hashTestNodeName (const std::string &name, std::unordered_map<test_case_hash_t, std::string> *hashCollisionDetectionMap)
343 {
344 test_case_hash_t hash = MurmurHash64B(name.c_str(), (int)name.length(), 1);
345 if(hashCollisionDetectionMap != nullptr) {
346 auto search = hashCollisionDetectionMap->find(hash);
347 if (search != hashCollisionDetectionMap->end()) {
348 if (search->second != name) {
349 print("There was an hash collision between '%s' and '%s'\n", search->second.c_str(), name.c_str());
350 throw std::runtime_error("Hash collision detected!");
351 }
352 }
353 hashCollisionDetectionMap->insert({hash, name});
354 }
355 return hash;
356 }
357
358 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
359 class DebugOutStreambuf : public std::streambuf
360 {
361 public:
362 DebugOutStreambuf (void);
363 ~DebugOutStreambuf (void);
364
365 protected:
366 std::streamsize xsputn (const char* s, std::streamsize count);
367 int overflow (int ch = -1);
368
369 private:
370 void flushLine (void);
371
372 std::ostringstream m_curLine;
373 };
374
DebugOutStreambuf(void)375 DebugOutStreambuf::DebugOutStreambuf (void)
376 {
377 }
378
~DebugOutStreambuf(void)379 DebugOutStreambuf::~DebugOutStreambuf (void)
380 {
381 if (m_curLine.tellp() != std::streampos(0))
382 flushLine();
383 }
384
xsputn(const char * s,std::streamsize count)385 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
386 {
387 for (std::streamsize pos = 0; pos < count; pos++)
388 {
389 m_curLine.put(s[pos]);
390
391 if (s[pos] == '\n')
392 flushLine();
393 }
394
395 return count;
396 }
397
overflow(int ch)398 int DebugOutStreambuf::overflow (int ch)
399 {
400 if (ch == -1)
401 return -1;
402 else
403 {
404 DE_ASSERT((ch & 0xff) == ch);
405 const char chVal = (char)(deUint8)(ch & 0xff);
406 return xsputn(&chVal, 1) == 1 ? ch : -1;
407 }
408 }
409
flushLine(void)410 void DebugOutStreambuf::flushLine (void)
411 {
412 qpPrint(m_curLine.str().c_str());
413 m_curLine.str("");
414 }
415
416 class CaseTreeNode
417 {
418 public:
CaseTreeNode(const test_case_hash_t hash)419 CaseTreeNode (const test_case_hash_t hash) : m_hash(hash) {}
420 ~CaseTreeNode (void);
421
getHash(void) const422 test_case_hash_t getHash (void) const { return m_hash; }
hasChildren(void) const423 bool hasChildren (void) const { return !m_children.empty(); }
424
425 bool hasChild (test_case_hash_t hash) const;
426 CaseTreeNode* getChild (test_case_hash_t hash) const;
427
addChild(CaseTreeNode * child)428 void addChild (CaseTreeNode* child) { m_children.push_back(child); }
429
430 private:
431 CaseTreeNode (const CaseTreeNode&);
432 CaseTreeNode& operator= (const CaseTreeNode&);
433
434 enum { NOT_FOUND = -1 };
435
436 int findChildNdx (test_case_hash_t hash) const;
437
438 test_case_hash_t m_hash;
439 std::vector<CaseTreeNode*> m_children;
440 };
441
~CaseTreeNode(void)442 CaseTreeNode::~CaseTreeNode (void)
443 {
444 for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
445 delete *i;
446 }
447
findChildNdx(test_case_hash_t hash) const448 int CaseTreeNode::findChildNdx (test_case_hash_t hash) const
449 {
450 for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
451 {
452 if (m_children[ndx]->getHash() == hash)
453 return ndx;
454 }
455 return NOT_FOUND;
456 }
457
hasChild(test_case_hash_t hash) const458 inline bool CaseTreeNode::hasChild (test_case_hash_t hash) const
459 {
460 return findChildNdx(hash) != NOT_FOUND;
461 }
462
getChild(test_case_hash_t hash) const463 inline CaseTreeNode* CaseTreeNode::getChild (test_case_hash_t hash) const
464 {
465 const int ndx = findChildNdx(hash);
466 return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
467 }
468
getCurrentComponentLen(const char * path)469 static int getCurrentComponentLen (const char* path)
470 {
471 int ndx = 0;
472 for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
473 return ndx;
474 }
475
findNode(const CaseTreeNode * root,const char * path)476 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
477 {
478 const CaseTreeNode* curNode = root;
479 const char* curPath = path;
480 int curLen = getCurrentComponentLen(curPath);
481
482 for (;;)
483 {
484 test_case_hash_t hash = hashTestNodeName(std::string(curPath, curPath + curLen), nullptr);
485 curNode = curNode->getChild(hash);
486
487 if (!curNode)
488 break;
489
490 curPath += curLen;
491
492 if (curPath[0] == 0)
493 break;
494 else
495 {
496 DE_ASSERT(curPath[0] == '.');
497 curPath += 1;
498 curLen = getCurrentComponentLen(curPath);
499 }
500 }
501
502 return curNode;
503 }
504
parseCaseTrie(CaseTreeNode * root,std::istream & in,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)505 static void parseCaseTrie (CaseTreeNode* root, std::istream& in, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
506 {
507 vector<CaseTreeNode*> nodeStack;
508 string curName;
509 bool expectNode = true;
510
511 if (in.get() != '{')
512 throw std::invalid_argument("Malformed case trie");
513
514 nodeStack.push_back(root);
515
516 while (!nodeStack.empty())
517 {
518 const int curChr = in.get();
519
520 if (curChr == std::char_traits<char>::eof() || curChr == 0)
521 throw std::invalid_argument("Unterminated case tree");
522
523 if (curChr == '{' || curChr == ',' || curChr == '}')
524 {
525 if (!curName.empty() && expectNode)
526 {
527 test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
528 CaseTreeNode* const newChild = new CaseTreeNode(hash);
529
530 try
531 {
532 nodeStack.back()->addChild(newChild);
533 }
534 catch (...)
535 {
536 delete newChild;
537 throw;
538 }
539
540 if (curChr == '{')
541 nodeStack.push_back(newChild);
542
543 curName.clear();
544 }
545 else if (curName.empty() == expectNode)
546 throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
547
548 if (curChr == '}')
549 {
550 expectNode = false;
551 nodeStack.pop_back();
552
553 // consume trailing new line
554 if (nodeStack.empty())
555 {
556 if (in.peek() == '\r')
557 in.get();
558 if (in.peek() == '\n')
559 in.get();
560 }
561 }
562 else
563 expectNode = true;
564 }
565 else if (isValidTestCaseNameChar((char)curChr))
566 curName += (char)curChr;
567 else
568 throw std::invalid_argument("Illegal character in node name");
569 }
570 }
571
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)572 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
573 {
574 // \note Algorithm assumes that cases are sorted by groups, but will
575 // function fine, albeit more slowly, if that is not the case.
576 int stackPos = 0;
577 string curName;
578
579 for (;;)
580 {
581 const int curChr = in.get();
582
583 if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
584 {
585 if (curName.empty())
586 throw std::invalid_argument("Empty test case name");
587
588 test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
589 if (!nodeStack[stackPos]->hasChild(hash))
590 {
591 CaseTreeNode* const newChild = new CaseTreeNode(hash);
592
593 try
594 {
595 nodeStack[stackPos]->addChild(newChild);
596 }
597 catch (...)
598 {
599 delete newChild;
600 throw;
601 }
602 }
603 else if (reportDuplicates)
604 throw std::invalid_argument("Duplicate test case");
605
606 curName.clear();
607 stackPos = 0;
608
609 if (curChr == '\r' && in.peek() == '\n')
610 in.get();
611
612 {
613 const int nextChr = in.peek();
614
615 if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
616 break;
617 }
618 }
619 else if (curChr == '.')
620 {
621 if (curName.empty())
622 throw std::invalid_argument("Empty test group name");
623
624 if ((int)nodeStack.size() <= stackPos+1)
625 nodeStack.resize(nodeStack.size()*2, DE_NULL);
626
627 test_case_hash_t hash = hashTestNodeName(curName, &hashCollisionDetectionMap);
628 if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getHash() != hash)
629 {
630 CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(hash);
631
632 if (!curGroup)
633 {
634 curGroup = new CaseTreeNode(hash);
635
636 try
637 {
638 nodeStack[stackPos]->addChild(curGroup);
639 }
640 catch (...)
641 {
642 delete curGroup;
643 throw;
644 }
645 }
646
647 nodeStack[stackPos+1] = curGroup;
648
649 if ((int)nodeStack.size() > stackPos+2)
650 nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
651 }
652
653 DE_ASSERT(nodeStack[stackPos+1]->getHash() == hash);
654
655 curName.clear();
656 stackPos += 1;
657 }
658 else if (isValidTestCaseNameChar((char)curChr))
659 curName += (char)curChr;
660 else
661 throw std::invalid_argument("Illegal character in test case name");
662 }
663 }
664
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)665 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
666 {
667 vector<CaseTreeNode*> nodeStack(8, root);
668 parseSimpleCaseList(nodeStack, in, reportDuplicates, hashCollisionDetectionMap);
669 }
670
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates,std::unordered_map<test_case_hash_t,string> & hashCollisionDetectionMap)671 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates, std::unordered_map<test_case_hash_t, string> &hashCollisionDetectionMap)
672 {
673 // read whole file and remove all '\r'
674 std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
675 buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
676
677 vector<CaseTreeNode*> nodeStack(8, root);
678 std::stringstream namesStream(buffer);
679 std::string fileName;
680
681 while (std::getline(namesStream, fileName))
682 {
683 de::FilePath groupPath (fileName);
684 de::UniquePtr<Resource> groupResource (archive.getResource(groupPath.normalize().getPath()));
685 const int groupBufferSize (groupResource->getSize());
686 std::vector<char> groupBuffer (static_cast<size_t>(groupBufferSize));
687
688 groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
689 if (groupBuffer.empty())
690 throw Exception("Empty case list resource");
691
692 std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
693 parseSimpleCaseList(nodeStack, groupIn, reportDuplicates, hashCollisionDetectionMap);
694 }
695 }
696
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)697 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
698 {
699 std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
700 auto rootName = "";
701 test_case_hash_t hash = hashTestNodeName(rootName, &hashCollisionDetectionMap);
702 CaseTreeNode* const root = new CaseTreeNode(hash);
703 try
704 {
705 if (in.peek() == '{')
706 parseCaseTrie(root, in, hashCollisionDetectionMap);
707 else
708 {
709 // if we are reading cases from file determine if we are
710 // reading group file or plain list of cases; this is done by
711 // reading single line and checking if it ends with ".txt"
712 bool readGroupFile = false;
713 if (path)
714 {
715 // read the first line and make sure it doesn't contain '\r'
716 std::string line;
717 std::getline(in, line);
718 line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
719
720 const std::string ending = ".txt";
721 readGroupFile = (line.length() > ending.length()) &&
722 std::equal(ending.rbegin(), ending.rend(), line.rbegin());
723
724 // move to the beginning of the file to parse first line too
725 in.seekg(0, in.beg);
726 }
727
728 if (readGroupFile)
729 parseGroupFile(root, in, archive, true, hashCollisionDetectionMap);
730 else
731 parseCaseList(root, in, true, hashCollisionDetectionMap);
732 }
733
734 {
735 const int curChr = in.get();
736 if (curChr != std::char_traits<char>::eof() && curChr != 0)
737 throw std::invalid_argument("Trailing characters at end of case list");
738 }
739
740 return root;
741 }
742 catch (...)
743 {
744 delete root;
745 throw;
746 }
747 }
748
749 class CasePaths
750 {
751 public:
752 CasePaths(const string& pathList);
753 CasePaths(const vector<string>& pathList);
754 bool matches(const string& caseName, bool allowPrefix = false) const;
755
756 private:
757 const vector<string> m_casePatterns;
758 };
759
CasePaths(const string & pathList)760 CasePaths::CasePaths (const string& pathList)
761 : m_casePatterns(de::splitString(pathList, ','))
762 {
763 }
764
CasePaths(const vector<string> & pathList)765 CasePaths::CasePaths(const vector<string>& pathList)
766 : m_casePatterns(pathList)
767 {
768 }
769
770 // 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)771 bool matchWildcards(string::const_iterator patternStart,
772 string::const_iterator patternEnd,
773 string::const_iterator pathStart,
774 string::const_iterator pathEnd,
775 bool allowPrefix)
776 {
777 string::const_iterator pattern = patternStart;
778 string::const_iterator path = pathStart;
779
780 while (pattern != patternEnd && path != pathEnd && *pattern == *path)
781 {
782 ++pattern;
783 ++path;
784 }
785
786 if (pattern == patternEnd)
787 return (path == pathEnd);
788 else if (*pattern == '*')
789 {
790 string::const_iterator patternNext = pattern + 1;
791 if (patternNext != patternEnd) {
792 for (; path != pathEnd; ++path)
793 {
794 if (*patternNext == *path)
795 if (matchWildcards(patternNext, patternEnd, path, pathEnd, allowPrefix))
796 return true;
797 }
798 }
799
800 if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
801 return true;
802 }
803 else if (path == pathEnd && allowPrefix)
804 return true;
805
806 return false;
807 }
808
809 #if defined(TCU_HIERARCHICAL_CASEPATHS)
810 // Match a list of pattern components to a list of path components. A pattern
811 // component may contain *-wildcards. A pattern component "**" matches zero or
812 // 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)813 static bool patternMatches(vector<string>::const_iterator patternStart,
814 vector<string>::const_iterator patternEnd,
815 vector<string>::const_iterator pathStart,
816 vector<string>::const_iterator pathEnd,
817 bool allowPrefix)
818 {
819 vector<string>::const_iterator pattern = patternStart;
820 vector<string>::const_iterator path = pathStart;
821
822 while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
823 (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
824 path->begin(), path->end(), false)))
825 {
826 ++pattern;
827 ++path;
828 }
829
830 if (path == pathEnd && (allowPrefix || pattern == patternEnd))
831 return true;
832 else if (pattern != patternEnd && *pattern == "**")
833 {
834 for (; path != pathEnd; ++path)
835 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
836 return true;
837 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
838 return true;
839 }
840
841 return false;
842 }
843 #endif
844
matches(const string & caseName,bool allowPrefix) const845 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
846 {
847 #if defined(TCU_HIERARCHICAL_CASEPATHS)
848 const vector<string> components = de::splitString(caseName, '.');
849 #endif
850
851 for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
852 {
853 #if defined(TCU_HIERARCHICAL_CASEPATHS)
854 const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
855
856 if (patternMatches(patternComponents.begin(), patternComponents.end(),
857 components.begin(), components.end(), allowPrefix))
858 return true;
859 #else
860 if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
861 caseName.begin(), caseName.end(), allowPrefix))
862 return true;
863 #endif
864 }
865
866 return false;
867 }
868
869 /*--------------------------------------------------------------------*//*!
870 * \brief Construct command line
871 * \note CommandLine is not fully initialized until parse() has been called.
872 *//*--------------------------------------------------------------------*/
CommandLine(void)873 CommandLine::CommandLine (void)
874 : m_appName(), m_logFlags(0), m_hadHelpSpecified(false)
875 {
876 }
877
878 /*--------------------------------------------------------------------*//*!
879 * \brief Construct command line from standard argc, argv pair.
880 *
881 * Calls parse() with given arguments
882 * \param archive application's assets
883 * \param argc Number of arguments
884 * \param argv Command line arguments
885 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)886 CommandLine::CommandLine (int argc, const char* const* argv)
887 : m_appName(argv[0]), m_logFlags (0), m_hadHelpSpecified(false)
888 {
889 if (argc > 1)
890 {
891 int loop = 1; // skip application name
892 while (true)
893 {
894 m_initialCmdLine += std::string(argv[loop++]);
895 if (loop >= argc)
896 break;
897 m_initialCmdLine += " ";
898 }
899 }
900
901 if (!parse(argc, argv))
902 {
903 if (m_hadHelpSpecified)
904 exit(EXIT_SUCCESS);
905 else
906 throw Exception("Failed to parse command line");
907 }
908 }
909
910 /*--------------------------------------------------------------------*//*!
911 * \brief Construct command line from string.
912 *
913 * Calls parse() with given argument.
914 * \param archive application's assets
915 * \param cmdLine Full command line string.
916 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)917 CommandLine::CommandLine (const std::string& cmdLine)
918 : m_appName(), m_initialCmdLine (cmdLine), m_hadHelpSpecified(false)
919 {
920 if (!parse(cmdLine))
921 throw Exception("Failed to parse command line");
922 }
923
~CommandLine(void)924 CommandLine::~CommandLine (void)
925 {
926 }
927
clear(void)928 void CommandLine::clear (void)
929 {
930 m_cmdLine.clear();
931 m_logFlags = 0;
932 }
933
getCommandLine(void) const934 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
935 {
936 return m_cmdLine;
937 }
938
getApplicationName(void) const939 const std::string& CommandLine::getApplicationName(void) const
940 {
941 return m_appName;
942 }
943
getInitialCmdLine(void) const944 const std::string& CommandLine::getInitialCmdLine(void) const
945 {
946 return m_initialCmdLine;
947 }
948
registerExtendedOptions(de::cmdline::Parser & parser)949 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
950 {
951 DE_UNREF(parser);
952 }
953
954 /*--------------------------------------------------------------------*//*!
955 * \brief Parse command line from standard argc, argv pair.
956 * \note parse() must be called exactly once.
957 * \param argc Number of arguments
958 * \param argv Command line arguments
959 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)960 bool CommandLine::parse (int argc, const char* const* argv)
961 {
962 DebugOutStreambuf sbuf;
963 std::ostream debugOut (&sbuf);
964 de::cmdline::Parser parser;
965
966 opt::registerOptions(parser);
967 opt::registerLegacyOptions(parser);
968 registerExtendedOptions(parser);
969
970 clear();
971
972 if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
973 {
974 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
975 parser.help(debugOut);
976
977 // We need to save this to avoid exiting with error later, and before the clear() call that wipes its value.
978 m_hadHelpSpecified = m_cmdLine.helpSpecified();
979
980 clear();
981 return false;
982 }
983
984 if (!m_cmdLine.getOption<opt::LogImages>())
985 m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
986
987 if (!m_cmdLine.getOption<opt::LogShaderSources>())
988 m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
989
990 if (!m_cmdLine.getOption<opt::LogFlush>())
991 m_logFlags |= QP_TEST_LOG_NO_FLUSH;
992
993 if (m_cmdLine.getOption<opt::LogCompact>())
994 m_logFlags |= QP_TEST_LOG_COMPACT;
995
996 if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
997 m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
998
999 if (m_cmdLine.getOption<opt::SubProcess>())
1000 m_logFlags |= QP_TEST_LOG_NO_INITIAL_OUTPUT;
1001
1002 if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
1003 (m_cmdLine.hasOption<opt::CaseList>()?1:0) +
1004 (m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
1005 (m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
1006 (m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
1007 {
1008 debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
1009 clear();
1010 return false;
1011 }
1012
1013 if (m_cmdLine.getArgs().size() > 0)
1014 {
1015 debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
1016
1017 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
1018 parser.help(debugOut);
1019
1020 clear();
1021 return false;
1022 }
1023
1024 return true;
1025 }
1026
1027 /*--------------------------------------------------------------------*//*!
1028 * \brief Parse command line from string.
1029 * \note parse() must be called exactly once.
1030 * \param cmdLine Full command line string.
1031 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)1032 bool CommandLine::parse (const std::string& cmdLine)
1033 {
1034 deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
1035 if (!parsedCmdLine)
1036 throw std::bad_alloc();
1037
1038 bool isOk = false;
1039 try
1040 {
1041 isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
1042 }
1043 catch (...)
1044 {
1045 deCommandLine_destroy(parsedCmdLine);
1046 throw;
1047 }
1048
1049 deCommandLine_destroy(parsedCmdLine);
1050 return isOk;
1051 }
1052
getLogFileName(void) const1053 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const1054 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const1055 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const1056 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const1057 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const1058 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const1059 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const1060 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const1061 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const1062 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const1063 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const1064 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const1065 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const1066 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const1067 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const1068 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const1069 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
getVKDeviceGroupId(void) const1070 int CommandLine::getVKDeviceGroupId (void) const { return m_cmdLine.getOption<opt::VKDeviceGroupID>(); }
isValidationEnabled(void) const1071 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
printValidationErrors(void) const1072 bool CommandLine::printValidationErrors (void) const { return m_cmdLine.getOption<opt::PrintValidationErrors>(); }
isLogDecompiledSpirvEnabled(void) const1073 bool CommandLine::isLogDecompiledSpirvEnabled (void) const { return m_cmdLine.getOption<opt::LogDecompiledSpirv>(); }
isOutOfMemoryTestEnabled(void) const1074 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
isShadercacheEnabled(void) const1075 bool CommandLine::isShadercacheEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCache>(); }
getShaderCacheFilename(void) const1076 const char* CommandLine::getShaderCacheFilename (void) const { return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str(); }
isShaderCacheTruncateEnabled(void) const1077 bool CommandLine::isShaderCacheTruncateEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheTruncate>(); }
isShaderCacheIPCEnabled(void) const1078 bool CommandLine::isShaderCacheIPCEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheIPC>(); }
getOptimizationRecipe(void) const1079 int CommandLine::getOptimizationRecipe (void) const { return m_cmdLine.getOption<opt::Optimization>(); }
isSpirvOptimizationEnabled(void) const1080 bool CommandLine::isSpirvOptimizationEnabled (void) const { return m_cmdLine.getOption<opt::OptimizeSpirv>(); }
isRenderDocEnabled(void) const1081 bool CommandLine::isRenderDocEnabled (void) const { return m_cmdLine.getOption<opt::RenderDoc>(); }
getWaiverFileName(void) const1082 const char* CommandLine::getWaiverFileName (void) const { return m_cmdLine.getOption<opt::WaiverFile>().c_str(); }
getCaseFraction(void) const1083 const std::vector<int>& CommandLine::getCaseFraction (void) const { return m_cmdLine.getOption<opt::CaseFraction>(); }
getCaseFractionMandatoryTests(void) const1084 const char* CommandLine::getCaseFractionMandatoryTests (void) const { return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str(); }
getArchiveDir(void) const1085 const char* CommandLine::getArchiveDir (void) const { return m_cmdLine.getOption<opt::ArchiveDir>().c_str(); }
getRunnerType(void) const1086 tcu::TestRunnerType CommandLine::getRunnerType (void) const { return m_cmdLine.getOption<opt::RunnerType>(); }
isTerminateOnFailEnabled(void) const1087 bool CommandLine::isTerminateOnFailEnabled (void) const { return m_cmdLine.getOption<opt::TerminateOnFail>(); }
isSubProcess(void) const1088 bool CommandLine::isSubProcess (void) const { return m_cmdLine.getOption<opt::SubProcess>(); }
getSubprocessTestCount(void) const1089 int CommandLine::getSubprocessTestCount (void) const { return m_cmdLine.getOption<opt::SubprocessTestCount>(); }
getCommandPoolMinSize(void) const1090 int CommandLine::getCommandPoolMinSize (void) const { return m_cmdLine.getOption<opt::CommandPoolMinSize>(); }
getCommandBufferMinSize(void) const1091 int CommandLine::getCommandBufferMinSize (void) const { return m_cmdLine.getOption<opt::CommandBufferMinSize>(); }
getCommandDefaultSize(void) const1092 int CommandLine::getCommandDefaultSize (void) const { return m_cmdLine.getOption<opt::CommandDefaultSize>(); }
getPipelineDefaultSize(void) const1093 int CommandLine::getPipelineDefaultSize (void) const { return m_cmdLine.getOption<opt::PipelineDefaultSize>(); }
1094
getGLContextType(void) const1095 const char* CommandLine::getGLContextType (void) const
1096 {
1097 if (m_cmdLine.hasOption<opt::GLContextType>())
1098 return m_cmdLine.getOption<opt::GLContextType>().c_str();
1099 else
1100 return DE_NULL;
1101 }
getGLConfigName(void) const1102 const char* CommandLine::getGLConfigName (void) const
1103 {
1104 if (m_cmdLine.hasOption<opt::GLConfigName>())
1105 return m_cmdLine.getOption<opt::GLConfigName>().c_str();
1106 else
1107 return DE_NULL;
1108 }
1109
getGLContextFlags(void) const1110 const char* CommandLine::getGLContextFlags (void) const
1111 {
1112 if (m_cmdLine.hasOption<opt::GLContextFlags>())
1113 return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
1114 else
1115 return DE_NULL;
1116 }
1117
getCLBuildOptions(void) const1118 const char* CommandLine::getCLBuildOptions (void) const
1119 {
1120 if (m_cmdLine.hasOption<opt::CLBuildOptions>())
1121 return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
1122 else
1123 return DE_NULL;
1124 }
1125
getEGLDisplayType(void) const1126 const char* CommandLine::getEGLDisplayType (void) const
1127 {
1128 if (m_cmdLine.hasOption<opt::EGLDisplayType>())
1129 return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
1130 else
1131 return DE_NULL;
1132 }
1133
getEGLWindowType(void) const1134 const char* CommandLine::getEGLWindowType (void) const
1135 {
1136 if (m_cmdLine.hasOption<opt::EGLWindowType>())
1137 return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
1138 else
1139 return DE_NULL;
1140 }
1141
getEGLPixmapType(void) const1142 const char* CommandLine::getEGLPixmapType (void) const
1143 {
1144 if (m_cmdLine.hasOption<opt::EGLPixmapType>())
1145 return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
1146 else
1147 return DE_NULL;
1148 }
1149
getSubprocessConfigFile(void) const1150 const char* CommandLine::getSubprocessConfigFile (void) const
1151 {
1152 if (m_cmdLine.hasOption<opt::SubprocessConfigFile>())
1153 return m_cmdLine.getOption<opt::SubprocessConfigFile>().c_str();
1154 else
1155 return DE_NULL;
1156 }
1157
1158
getServerAddress(void) const1159 const char* CommandLine::getServerAddress (void) const
1160 {
1161 if (m_cmdLine.hasOption<opt::ServerAddress>())
1162 return m_cmdLine.getOption<opt::ServerAddress>().c_str();
1163 else
1164 return DE_NULL;
1165 }
1166
getPipelineCompilerPath(void) const1167 const char* CommandLine::getPipelineCompilerPath(void) const
1168 {
1169 if (m_cmdLine.hasOption<opt::PipelineCompilerPath>())
1170 return m_cmdLine.getOption<opt::PipelineCompilerPath>().c_str();
1171 else
1172 return DE_NULL;
1173 }
1174
getPipelineCompilerDataDir(void) const1175 const char* CommandLine::getPipelineCompilerDataDir(void) const
1176 {
1177 if (m_cmdLine.hasOption<opt::PipelineCompilerDataDir>())
1178 return m_cmdLine.getOption<opt::PipelineCompilerDataDir>().c_str();
1179 else
1180 return DE_NULL;
1181 }
1182
getPipelineCompilerArgs(void) const1183 const char* CommandLine::getPipelineCompilerArgs(void) const
1184 {
1185 if (m_cmdLine.hasOption<opt::PipelineCompilerArgs>())
1186 return m_cmdLine.getOption<opt::PipelineCompilerArgs>().c_str();
1187 else
1188 return DE_NULL;
1189 }
1190
getPipelineCompilerOutputFile(void) const1191 const char* CommandLine::getPipelineCompilerOutputFile(void) const
1192 {
1193 if (m_cmdLine.hasOption<opt::PipelineCompilerOutputFile>())
1194 return m_cmdLine.getOption<opt::PipelineCompilerOutputFile>().c_str();
1195 else
1196 return DE_NULL;
1197 }
1198
getPipelineCompilerLogFile(void) const1199 const char* CommandLine::getPipelineCompilerLogFile(void) const
1200 {
1201 if (m_cmdLine.hasOption<opt::PipelineCompilerLogFile>())
1202 return m_cmdLine.getOption<opt::PipelineCompilerLogFile>().c_str();
1203 else
1204 return DE_NULL;
1205 }
1206
getPipelineCompilerFilePrefix(void) const1207 const char* CommandLine::getPipelineCompilerFilePrefix(void) const
1208 {
1209 if (m_cmdLine.hasOption<opt::PipelineCompilerFilePrefix>())
1210 return m_cmdLine.getOption<opt::PipelineCompilerFilePrefix>().c_str();
1211 else
1212 return DE_NULL;
1213 }
1214
getVkLibraryPath(void) const1215 const char* CommandLine::getVkLibraryPath(void) const
1216 {
1217 if (m_cmdLine.hasOption<opt::VkLibraryPath>())
1218 return (m_cmdLine.getOption<opt::VkLibraryPath>() != "") ? m_cmdLine.getOption<opt::VkLibraryPath>().c_str() : DE_NULL;
1219 else
1220 return DE_NULL;
1221 }
1222
getAppParamsInputFilePath(void) const1223 const char* CommandLine::getAppParamsInputFilePath(void) const
1224 {
1225 if (m_cmdLine.hasOption<opt::ApplicationParametersInputFile>())
1226 return m_cmdLine.getOption<opt::ApplicationParametersInputFile>().c_str();
1227 else
1228 return DE_NULL;
1229 }
1230
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)1231 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
1232 {
1233 const CaseTreeNode* node = findNode(root, groupPath);
1234 return node && node->hasChildren();
1235 }
1236
checkTestCaseName(const CaseTreeNode * root,const char * casePath)1237 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1238 {
1239 const CaseTreeNode* node = findNode(root, casePath);
1240 return node && !node->hasChildren();
1241 }
1242
createCaseListFilter(const tcu::Archive & archive) const1243 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1244 {
1245 return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1246 }
1247
checkTestGroupName(const char * groupName) const1248 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1249 {
1250 bool result = false;
1251 if (m_casePaths)
1252 result = m_casePaths->matches(groupName, true);
1253 else if (m_caseTree)
1254 result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1255 else
1256 return true;
1257 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1258 result = m_caseFractionMandatoryTests->matches(groupName, true);
1259 return result;
1260 }
1261
checkTestCaseName(const char * caseName) const1262 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1263 {
1264 bool result = false;
1265 if (m_casePaths)
1266 result = m_casePaths->matches(caseName, false);
1267 else if (m_caseTree)
1268 result = tcu::checkTestCaseName(m_caseTree, caseName);
1269 else
1270 return true;
1271 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1272 result = m_caseFractionMandatoryTests->matches(caseName, false);
1273 return result;
1274 }
1275
checkCaseFraction(int i,const std::string & testCaseName) const1276 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1277 {
1278 return m_caseFraction.size() != 2 ||
1279 ((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1280 (m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1281 }
1282
CaseListFilter(void)1283 CaseListFilter::CaseListFilter (void)
1284 : m_caseTree (DE_NULL)
1285 , m_runnerType (tcu::RUNNERTYPE_ANY)
1286 {
1287 }
1288
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1289 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1290 : m_caseTree (DE_NULL)
1291 {
1292 if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1293 {
1294 m_runnerType = RUNNERTYPE_AMBER;
1295 }
1296 else
1297 {
1298 m_runnerType = cmdLine.getOption<opt::RunnerType>();
1299 }
1300
1301 if (cmdLine.hasOption<opt::CaseList>())
1302 {
1303 std::istringstream str(cmdLine.getOption<opt::CaseList>());
1304
1305 m_caseTree = parseCaseList(str, archive);
1306 }
1307 else if (cmdLine.hasOption<opt::CaseListFile>())
1308 {
1309 std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1310 std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1311
1312 if (!in.is_open() || !in.good())
1313 throw Exception("Failed to open case list file '" + caseListFile + "'");
1314
1315 m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1316 }
1317 else if (cmdLine.hasOption<opt::CaseListResource>())
1318 {
1319 // \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1320 // istream adaptor for tcu::Resource.
1321 de::UniquePtr<Resource> caseListResource (archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1322 const int bufferSize = caseListResource->getSize();
1323 std::vector<char> buffer ((size_t)bufferSize);
1324
1325 if (buffer.empty())
1326 throw Exception("Empty case list resource");
1327
1328 caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1329
1330 {
1331 std::istringstream in (std::string(&buffer[0], (size_t)bufferSize));
1332
1333 m_caseTree = parseCaseList(in, archive);
1334 }
1335 }
1336 else if (cmdLine.getOption<opt::StdinCaseList>())
1337 {
1338 m_caseTree = parseCaseList(std::cin, archive);
1339 }
1340 else if (cmdLine.hasOption<opt::CasePath>())
1341 m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1342
1343 if (!cmdLine.getOption<opt::SubProcess>())
1344 m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1345
1346 if (m_caseFraction.size() == 2 &&
1347 (m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1348 throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1349
1350 if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1351 throw Exception("Invalid case fraction. Must have two components.");
1352
1353 if (m_caseFraction.size() == 2)
1354 {
1355 std::string caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1356
1357 if (!caseFractionMandatoryTestsFilename.empty())
1358 {
1359 std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1360 if (!fileStream.is_open() || !fileStream.good())
1361 throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1362
1363 std::vector<std::string> cfPaths;
1364 std::string line;
1365
1366 while (std::getline(fileStream, line))
1367 {
1368 line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1369 cfPaths.push_back(line);
1370 }
1371 if (!cfPaths.empty())
1372 {
1373 m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1374 if (m_caseTree != DE_NULL)
1375 {
1376 fileStream.clear();
1377 fileStream.seekg(0, fileStream.beg);
1378 std::unordered_map<test_case_hash_t, std::string> hashCollisionDetectionMap{};
1379 parseCaseList(m_caseTree, fileStream, false, hashCollisionDetectionMap);
1380 }
1381 }
1382 }
1383 }
1384 }
1385
~CaseListFilter(void)1386 CaseListFilter::~CaseListFilter (void)
1387 {
1388 delete m_caseTree;
1389 }
1390
1391 } // tcu
1392