1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Command line parsing.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuCommandLine.hpp"
25 #include "tcuPlatform.hpp"
26 #include "tcuTestCase.hpp"
27 #include "tcuResource.hpp"
28 #include "deFilePath.hpp"
29 #include "deStringUtil.hpp"
30 #include "deString.h"
31 #include "deInt32.h"
32 #include "deCommandLine.h"
33 #include "qpTestLog.h"
34 #include "qpDebugOut.h"
35
36 #include <string>
37 #include <vector>
38 #include <sstream>
39 #include <fstream>
40 #include <iostream>
41 #include <algorithm>
42
43 using std::string;
44 using std::vector;
45
46 // OOM tests are enabled by default only on platforms that don't do memory overcommit (Win32)
47 #if (DE_OS == DE_OS_WIN32)
48 # define TEST_OOM_DEFAULT "enable"
49 #else
50 # define TEST_OOM_DEFAULT "disable"
51 #endif
52
53 namespace tcu
54 {
55
56 namespace opt
57 {
58
59 DE_DECLARE_COMMAND_LINE_OPT(CasePath, std::string);
60 DE_DECLARE_COMMAND_LINE_OPT(CaseList, std::string);
61 DE_DECLARE_COMMAND_LINE_OPT(CaseListFile, std::string);
62 DE_DECLARE_COMMAND_LINE_OPT(CaseListResource, std::string);
63 DE_DECLARE_COMMAND_LINE_OPT(StdinCaseList, bool);
64 DE_DECLARE_COMMAND_LINE_OPT(LogFilename, std::string);
65 DE_DECLARE_COMMAND_LINE_OPT(RunMode, tcu::RunMode);
66 DE_DECLARE_COMMAND_LINE_OPT(ExportFilenamePattern, std::string);
67 DE_DECLARE_COMMAND_LINE_OPT(WatchDog, bool);
68 DE_DECLARE_COMMAND_LINE_OPT(CrashHandler, bool);
69 DE_DECLARE_COMMAND_LINE_OPT(BaseSeed, int);
70 DE_DECLARE_COMMAND_LINE_OPT(TestIterationCount, int);
71 DE_DECLARE_COMMAND_LINE_OPT(Visibility, WindowVisibility);
72 DE_DECLARE_COMMAND_LINE_OPT(SurfaceWidth, int);
73 DE_DECLARE_COMMAND_LINE_OPT(SurfaceHeight, int);
74 DE_DECLARE_COMMAND_LINE_OPT(SurfaceType, tcu::SurfaceType);
75 DE_DECLARE_COMMAND_LINE_OPT(ScreenRotation, tcu::ScreenRotation);
76 DE_DECLARE_COMMAND_LINE_OPT(GLContextType, std::string);
77 DE_DECLARE_COMMAND_LINE_OPT(GLConfigID, int);
78 DE_DECLARE_COMMAND_LINE_OPT(GLConfigName, std::string);
79 DE_DECLARE_COMMAND_LINE_OPT(GLContextFlags, std::string);
80 DE_DECLARE_COMMAND_LINE_OPT(CLPlatformID, int);
81 DE_DECLARE_COMMAND_LINE_OPT(CLDeviceIDs, std::vector<int>);
82 DE_DECLARE_COMMAND_LINE_OPT(CLBuildOptions, std::string);
83 DE_DECLARE_COMMAND_LINE_OPT(EGLDisplayType, std::string);
84 DE_DECLARE_COMMAND_LINE_OPT(EGLWindowType, std::string);
85 DE_DECLARE_COMMAND_LINE_OPT(EGLPixmapType, std::string);
86 DE_DECLARE_COMMAND_LINE_OPT(LogImages, bool);
87 DE_DECLARE_COMMAND_LINE_OPT(LogShaderSources, bool);
88 DE_DECLARE_COMMAND_LINE_OPT(LogDecompiledSpirv, bool);
89 DE_DECLARE_COMMAND_LINE_OPT(LogEmptyLoginfo, bool);
90 DE_DECLARE_COMMAND_LINE_OPT(TestOOM, bool);
91 DE_DECLARE_COMMAND_LINE_OPT(ArchiveDir, std::string);
92 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceID, int);
93 DE_DECLARE_COMMAND_LINE_OPT(VKDeviceGroupID, int);
94 DE_DECLARE_COMMAND_LINE_OPT(LogFlush, bool);
95 DE_DECLARE_COMMAND_LINE_OPT(Validation, bool);
96 DE_DECLARE_COMMAND_LINE_OPT(PrintValidationErrors, bool);
97 DE_DECLARE_COMMAND_LINE_OPT(ShaderCache, bool);
98 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename, std::string);
99 DE_DECLARE_COMMAND_LINE_OPT(Optimization, int);
100 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv, bool);
101 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate, bool);
102 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheIPC, bool);
103 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc, bool);
104 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction, std::vector<int>);
105 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests, std::string);
106 DE_DECLARE_COMMAND_LINE_OPT(WaiverFile, std::string);
107 DE_DECLARE_COMMAND_LINE_OPT(RunnerType, tcu::TestRunnerType);
108 DE_DECLARE_COMMAND_LINE_OPT(TerminateOnFail, bool);
109 DE_DECLARE_COMMAND_LINE_OPT(SubProcess, bool);
110 DE_DECLARE_COMMAND_LINE_OPT(SubprocessTestCount, int);
111 DE_DECLARE_COMMAND_LINE_OPT(SubprocessConfigFile, std::string);
112 DE_DECLARE_COMMAND_LINE_OPT(ServerAddress, std::string);
113 DE_DECLARE_COMMAND_LINE_OPT(CommandPoolMinSize, int);
114 DE_DECLARE_COMMAND_LINE_OPT(CommandBufferMinSize, int);
115 DE_DECLARE_COMMAND_LINE_OPT(CommandDefaultSize, int);
116 DE_DECLARE_COMMAND_LINE_OPT(PipelineDefaultSize, int);
117 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath, std::string);
118 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir, std::string);
119 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs, std::string);
120 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile, std::string);
121 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile, std::string);
122 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerFilePrefix, std::string);
123 DE_DECLARE_COMMAND_LINE_OPT(VkLibraryPath, std::string);
124 DE_DECLARE_COMMAND_LINE_OPT(ApplicationParametersInputFile, std::string);
125
126
parseIntList(const char * src,std::vector<int> * dst)127 static void parseIntList (const char* src, std::vector<int>* dst)
128 {
129 std::istringstream str (src);
130 std::string val;
131
132 while (std::getline(str, val, ','))
133 {
134 int intVal = 0;
135 de::cmdline::parseType(val.c_str(), &intVal);
136 dst->push_back(intVal);
137 }
138 }
139
registerOptions(de::cmdline::Parser & parser)140 void registerOptions (de::cmdline::Parser& parser)
141 {
142 using de::cmdline::Option;
143 using de::cmdline::NamedValue;
144
145 static const NamedValue<bool> s_enableNames[] =
146 {
147 { "enable", true },
148 { "disable", false }
149 };
150 static const NamedValue<tcu::RunMode> s_runModes[] =
151 {
152 { "execute", RUNMODE_EXECUTE },
153 { "xml-caselist", RUNMODE_DUMP_XML_CASELIST },
154 { "txt-caselist", RUNMODE_DUMP_TEXT_CASELIST },
155 { "stdout-caselist",RUNMODE_DUMP_STDOUT_CASELIST },
156 { "amber-verify", RUNMODE_VERIFY_AMBER_COHERENCY}
157 };
158 static const NamedValue<WindowVisibility> s_visibilites[] =
159 {
160 { "windowed", WINDOWVISIBILITY_WINDOWED },
161 { "fullscreen", WINDOWVISIBILITY_FULLSCREEN },
162 { "hidden", WINDOWVISIBILITY_HIDDEN }
163 };
164 static const NamedValue<tcu::SurfaceType> s_surfaceTypes[] =
165 {
166 { "window", SURFACETYPE_WINDOW },
167 { "pixmap", SURFACETYPE_OFFSCREEN_NATIVE },
168 { "pbuffer", SURFACETYPE_OFFSCREEN_GENERIC },
169 { "fbo", SURFACETYPE_FBO }
170 };
171 static const NamedValue<tcu::ScreenRotation> s_screenRotations[] =
172 {
173 { "unspecified", SCREENROTATION_UNSPECIFIED },
174 { "0", SCREENROTATION_0 },
175 { "90", SCREENROTATION_90 },
176 { "180", SCREENROTATION_180 },
177 { "270", SCREENROTATION_270 }
178 };
179 static const NamedValue<tcu::TestRunnerType> s_runnerTypes[] =
180 {
181 { "any", tcu::RUNNERTYPE_ANY },
182 { "none", tcu::RUNNERTYPE_NONE },
183 { "amber", tcu::RUNNERTYPE_AMBER },
184 };
185
186 parser
187 << Option<CasePath> ("n", "deqp-case", "Test case(s) to run, supports wildcards (e.g. dEQP-GLES2.info.*)")
188 << Option<CaseList> (DE_NULL, "deqp-caselist", "Case list to run in trie format (e.g. {dEQP-GLES2{info{version,renderer}}})")
189 << Option<CaseListFile> (DE_NULL, "deqp-caselist-file", "Read case list (in trie format) from given file")
190 << Option<CaseListResource> (DE_NULL, "deqp-caselist-resource", "Read case list (in trie format) from given file located application's assets")
191 << Option<StdinCaseList> (DE_NULL, "deqp-stdin-caselist", "Read case list (in trie format) from stdin")
192 << Option<LogFilename> (DE_NULL, "deqp-log-filename", "Write test results to given file", "TestResults.qpa")
193 << Option<RunMode> (DE_NULL, "deqp-runmode", "Execute tests, write list of test cases into a file, or verify amber capability coherency",
194 s_runModes, "execute")
195 << Option<ExportFilenamePattern> (DE_NULL, "deqp-caselist-export-file", "Set the target file name pattern for caselist export", "${packageName}-cases.${typeExtension}")
196 << Option<WatchDog> (DE_NULL, "deqp-watchdog", "Enable test watchdog", s_enableNames, "disable")
197 << Option<CrashHandler> (DE_NULL, "deqp-crashhandler", "Enable crash handling", s_enableNames, "disable")
198 << Option<BaseSeed> (DE_NULL, "deqp-base-seed", "Base seed for test cases that use randomization", "0")
199 << Option<TestIterationCount> (DE_NULL, "deqp-test-iteration-count", "Iteration count for cases that support variable number of iterations", "0")
200 << Option<Visibility> (DE_NULL, "deqp-visibility", "Default test window visibility", s_visibilites, "windowed")
201 << Option<SurfaceWidth> (DE_NULL, "deqp-surface-width", "Use given surface width if possible", "-1")
202 << Option<SurfaceHeight> (DE_NULL, "deqp-surface-height", "Use given surface height if possible", "-1")
203 << Option<SurfaceType> (DE_NULL, "deqp-surface-type", "Use given surface type", s_surfaceTypes, "window")
204 << Option<ScreenRotation> (DE_NULL, "deqp-screen-rotation", "Screen rotation for platforms that support it", s_screenRotations, "0")
205 << Option<GLContextType> (DE_NULL, "deqp-gl-context-type", "OpenGL context type for platforms that support multiple")
206 << Option<GLConfigID> (DE_NULL, "deqp-gl-config-id", "OpenGL (ES) render config ID (EGL config id on EGL platforms)", "-1")
207 << Option<GLConfigName> (DE_NULL, "deqp-gl-config-name", "Symbolic OpenGL (ES) render config name")
208 << Option<GLContextFlags> (DE_NULL, "deqp-gl-context-flags", "OpenGL context flags (comma-separated, supports debug and robust)")
209 << Option<CLPlatformID> (DE_NULL, "deqp-cl-platform-id", "Execute tests on given OpenCL platform (IDs start from 1)", "1")
210 << Option<CLDeviceIDs> (DE_NULL, "deqp-cl-device-ids", "Execute tests on given CL devices (comma-separated, IDs start from 1)", parseIntList, "")
211 << Option<CLBuildOptions> (DE_NULL, "deqp-cl-build-options", "Extra build options for OpenCL compiler")
212 << Option<EGLDisplayType> (DE_NULL, "deqp-egl-display-type", "EGL native display type")
213 << Option<EGLWindowType> (DE_NULL, "deqp-egl-window-type", "EGL native window type")
214 << Option<EGLPixmapType> (DE_NULL, "deqp-egl-pixmap-type", "EGL native pixmap type")
215 << Option<VKDeviceID> (DE_NULL, "deqp-vk-device-id", "Vulkan device ID (IDs start from 1)", "1")
216 << Option<VKDeviceGroupID> (DE_NULL, "deqp-vk-device-group-id", "Vulkan device Group ID (IDs start from 1)", "1")
217 << Option<LogImages> (DE_NULL, "deqp-log-images", "Enable or disable logging of result images", s_enableNames, "enable")
218 << Option<LogShaderSources> (DE_NULL, "deqp-log-shader-sources", "Enable or disable logging of shader sources", s_enableNames, "enable")
219 << Option<LogDecompiledSpirv> (DE_NULL, "deqp-log-decompiled-spirv", "Enable or disable logging of decompiled spir-v", s_enableNames, "enable")
220 << Option<LogEmptyLoginfo> (DE_NULL, "deqp-log-empty-loginfo", "Logging of empty shader compile/link log info", s_enableNames, "enable")
221 << Option<TestOOM> (DE_NULL, "deqp-test-oom", "Run tests that exhaust memory on purpose", s_enableNames, TEST_OOM_DEFAULT)
222 << Option<ArchiveDir> (DE_NULL, "deqp-archive-dir", "Path to test resource files", ".")
223 << Option<LogFlush> (DE_NULL, "deqp-log-flush", "Enable or disable log file fflush", s_enableNames, "enable")
224 << Option<Validation> (DE_NULL, "deqp-validation", "Enable or disable test case validation", s_enableNames, "disable")
225 << Option<PrintValidationErrors> (DE_NULL, "deqp-print-validation-errors", "Print validation errors to standard error")
226 << Option<Optimization> (DE_NULL, "deqp-optimization-recipe", "Shader optimization recipe (0=disabled, 1=performance, 2=size)", "0")
227 << Option<OptimizeSpirv> (DE_NULL, "deqp-optimize-spirv", "Apply optimization to spir-v shaders as well", s_enableNames, "disable")
228 << Option<ShaderCache> (DE_NULL, "deqp-shadercache", "Enable or disable shader cache", s_enableNames, "enable")
229 << Option<ShaderCacheFilename> (DE_NULL, "deqp-shadercache-filename", "Write shader cache to given file", "shadercache.bin")
230 << Option<ShaderCacheTruncate> (DE_NULL, "deqp-shadercache-truncate", "Truncate shader cache before running tests", s_enableNames, "enable")
231 << Option<ShaderCacheIPC> (DE_NULL, "deqp-shadercache-ipc", "Should shader cache use inter process comms", s_enableNames, "disable")
232 << Option<RenderDoc> (DE_NULL, "deqp-renderdoc", "Enable RenderDoc frame markers", s_enableNames, "disable")
233 << Option<CaseFraction> (DE_NULL, "deqp-fraction", "Run a fraction of the test cases (e.g. N,M means run group%M==N)", parseIntList, "")
234 << Option<CaseFractionMandatoryTests> (DE_NULL, "deqp-fraction-mandatory-caselist-file", "Case list file that must be run for each fraction", "")
235 << Option<WaiverFile> (DE_NULL, "deqp-waiver-file", "Read waived tests from given file", "")
236 << Option<RunnerType> (DE_NULL, "deqp-runner-type", "Filter test cases based on runner", s_runnerTypes, "any")
237 << Option<TerminateOnFail> (DE_NULL, "deqp-terminate-on-fail", "Terminate the run on first failure", s_enableNames, "disable")
238 << Option<SubProcess> (DE_NULL, "deqp-subprocess", "Inform app that it works as subprocess (Vulkan SC only, do not use manually)", s_enableNames, "disable")
239 << Option<SubprocessTestCount> (DE_NULL, "deqp-subprocess-test-count", "Define default number of tests performed in subprocess for specific test cases(Vulkan SC only)", "65536")
240 << Option<SubprocessConfigFile> (DE_NULL, "deqp-subprocess-cfg-file", "Config file defining number of tests performed in subprocess for specific test branches (Vulkan SC only)", "")
241 << Option<ServerAddress> (DE_NULL, "deqp-server-address", "Server address (host:port) responsible for shader compilation (Vulkan SC only)", "")
242 << Option<CommandPoolMinSize> (DE_NULL, "deqp-command-pool-min-size", "Define minimum size of the command pool (in bytes) to use (Vulkan SC only)","0")
243 << Option<CommandBufferMinSize> (DE_NULL, "deqp-command-buffer-min-size", "Define minimum size of the command buffer (in bytes) to use (Vulkan SC only)", "0")
244 << Option<CommandDefaultSize> (DE_NULL, "deqp-command-default-size", "Define default single command size (in bytes) to use (Vulkan SC only)", "256")
245 << Option<PipelineDefaultSize> (DE_NULL, "deqp-pipeline-default-size", "Define default pipeline size (in bytes) to use (Vulkan SC only)", "16384")
246 << Option<PipelineCompilerPath> (DE_NULL, "deqp-pipeline-compiler", "Path to offline pipeline compiler (Vulkan SC only)", "")
247 << Option<PipelineCompilerDataDir> (DE_NULL, "deqp-pipeline-dir", "Offline pipeline data directory (Vulkan SC only)", "")
248 << Option<PipelineCompilerArgs> (DE_NULL, "deqp-pipeline-args", "Additional compiler parameters (Vulkan SC only)", "")
249 << Option<PipelineCompilerOutputFile> (DE_NULL, "deqp-pipeline-file", "Output file with pipeline cache (Vulkan SC only, do not use manually)", "")
250 << Option<PipelineCompilerLogFile> (DE_NULL, "deqp-pipeline-logfile", "Log file for pipeline compiler (Vulkan SC only, do not use manually)", "")
251 << Option<PipelineCompilerFilePrefix> (DE_NULL, "deqp-pipeline-prefix", "Prefix for input pipeline compiler files (Vulkan SC only, do not use manually)", "")
252 << Option<VkLibraryPath> (DE_NULL, "deqp-vk-library-path", "Path to Vulkan library (e.g. loader library vulkan-1.dll)", "")
253 << Option<ApplicationParametersInputFile> (DE_NULL, "deqp-app-params-input-file", "File that provides a default set of application parameters");
254 }
255
registerLegacyOptions(de::cmdline::Parser & parser)256 void registerLegacyOptions (de::cmdline::Parser& parser)
257 {
258 using de::cmdline::Option;
259
260 parser
261 << Option<GLConfigID> (DE_NULL, "deqp-egl-config-id", "Legacy name for --deqp-gl-config-id", "-1")
262 << Option<GLConfigName> (DE_NULL, "deqp-egl-config-name", "Legacy name for --deqp-gl-config-name");
263 }
264
265 } // opt
266
267 // \todo [2014-02-13 pyry] This could be useful elsewhere as well.
268 class DebugOutStreambuf : public std::streambuf
269 {
270 public:
271 DebugOutStreambuf (void);
272 ~DebugOutStreambuf (void);
273
274 protected:
275 std::streamsize xsputn (const char* s, std::streamsize count);
276 int overflow (int ch = -1);
277
278 private:
279 void flushLine (void);
280
281 std::ostringstream m_curLine;
282 };
283
DebugOutStreambuf(void)284 DebugOutStreambuf::DebugOutStreambuf (void)
285 {
286 }
287
~DebugOutStreambuf(void)288 DebugOutStreambuf::~DebugOutStreambuf (void)
289 {
290 if (m_curLine.tellp() != std::streampos(0))
291 flushLine();
292 }
293
xsputn(const char * s,std::streamsize count)294 std::streamsize DebugOutStreambuf::xsputn (const char* s, std::streamsize count)
295 {
296 for (std::streamsize pos = 0; pos < count; pos++)
297 {
298 m_curLine.put(s[pos]);
299
300 if (s[pos] == '\n')
301 flushLine();
302 }
303
304 return count;
305 }
306
overflow(int ch)307 int DebugOutStreambuf::overflow (int ch)
308 {
309 if (ch == -1)
310 return -1;
311 else
312 {
313 DE_ASSERT((ch & 0xff) == ch);
314 const char chVal = (char)(deUint8)(ch & 0xff);
315 return xsputn(&chVal, 1) == 1 ? ch : -1;
316 }
317 }
318
flushLine(void)319 void DebugOutStreambuf::flushLine (void)
320 {
321 qpPrint(m_curLine.str().c_str());
322 m_curLine.str("");
323 }
324
325 class CaseTreeNode
326 {
327 public:
CaseTreeNode(const std::string & name)328 CaseTreeNode (const std::string& name) : m_name(name) {}
329 ~CaseTreeNode (void);
330
getName(void) const331 const std::string& getName (void) const { return m_name; }
hasChildren(void) const332 bool hasChildren (void) const { return !m_children.empty(); }
333
334 bool hasChild (const std::string& name) const;
335 const CaseTreeNode* getChild (const std::string& name) const;
336 CaseTreeNode* getChild (const std::string& name);
337
addChild(CaseTreeNode * child)338 void addChild (CaseTreeNode* child) { m_children.push_back(child); }
339
340 private:
341 CaseTreeNode (const CaseTreeNode&);
342 CaseTreeNode& operator= (const CaseTreeNode&);
343
344 enum { NOT_FOUND = -1 };
345
346 // \todo [2014-10-30 pyry] Speed up with hash / sorting
347 int findChildNdx (const std::string& name) const;
348
349 std::string m_name;
350 std::vector<CaseTreeNode*> m_children;
351 };
352
~CaseTreeNode(void)353 CaseTreeNode::~CaseTreeNode (void)
354 {
355 for (vector<CaseTreeNode*>::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
356 delete *i;
357 }
358
findChildNdx(const std::string & name) const359 int CaseTreeNode::findChildNdx (const std::string& name) const
360 {
361 for (int ndx = 0; ndx < (int)m_children.size(); ++ndx)
362 {
363 if (m_children[ndx]->getName() == name)
364 return ndx;
365 }
366 return NOT_FOUND;
367 }
368
hasChild(const std::string & name) const369 inline bool CaseTreeNode::hasChild (const std::string& name) const
370 {
371 return findChildNdx(name) != NOT_FOUND;
372 }
373
getChild(const std::string & name) const374 inline const CaseTreeNode* CaseTreeNode::getChild (const std::string& name) const
375 {
376 const int ndx = findChildNdx(name);
377 return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
378 }
379
getChild(const std::string & name)380 inline CaseTreeNode* CaseTreeNode::getChild (const std::string& name)
381 {
382 const int ndx = findChildNdx(name);
383 return ndx == NOT_FOUND ? DE_NULL : m_children[ndx];
384 }
385
getCurrentComponentLen(const char * path)386 static int getCurrentComponentLen (const char* path)
387 {
388 int ndx = 0;
389 for (; path[ndx] != 0 && path[ndx] != '.'; ++ndx);
390 return ndx;
391 }
392
findNode(const CaseTreeNode * root,const char * path)393 static const CaseTreeNode* findNode (const CaseTreeNode* root, const char* path)
394 {
395 const CaseTreeNode* curNode = root;
396 const char* curPath = path;
397 int curLen = getCurrentComponentLen(curPath);
398
399 for (;;)
400 {
401 curNode = curNode->getChild(std::string(curPath, curPath+curLen));
402
403 if (!curNode)
404 break;
405
406 curPath += curLen;
407
408 if (curPath[0] == 0)
409 break;
410 else
411 {
412 DE_ASSERT(curPath[0] == '.');
413 curPath += 1;
414 curLen = getCurrentComponentLen(curPath);
415 }
416 }
417
418 return curNode;
419 }
420
parseCaseTrie(CaseTreeNode * root,std::istream & in)421 static void parseCaseTrie (CaseTreeNode* root, std::istream& in)
422 {
423 vector<CaseTreeNode*> nodeStack;
424 string curName;
425 bool expectNode = true;
426
427 if (in.get() != '{')
428 throw std::invalid_argument("Malformed case trie");
429
430 nodeStack.push_back(root);
431
432 while (!nodeStack.empty())
433 {
434 const int curChr = in.get();
435
436 if (curChr == std::char_traits<char>::eof() || curChr == 0)
437 throw std::invalid_argument("Unterminated case tree");
438
439 if (curChr == '{' || curChr == ',' || curChr == '}')
440 {
441 if (!curName.empty() && expectNode)
442 {
443 CaseTreeNode* const newChild = new CaseTreeNode(curName);
444
445 try
446 {
447 nodeStack.back()->addChild(newChild);
448 }
449 catch (...)
450 {
451 delete newChild;
452 throw;
453 }
454
455 if (curChr == '{')
456 nodeStack.push_back(newChild);
457
458 curName.clear();
459 }
460 else if (curName.empty() == expectNode)
461 throw std::invalid_argument(expectNode ? "Empty node name" : "Missing node separator");
462
463 if (curChr == '}')
464 {
465 expectNode = false;
466 nodeStack.pop_back();
467
468 // consume trailing new line
469 if (nodeStack.empty())
470 {
471 if (in.peek() == '\r')
472 in.get();
473 if (in.peek() == '\n')
474 in.get();
475 }
476 }
477 else
478 expectNode = true;
479 }
480 else if (isValidTestCaseNameChar((char)curChr))
481 curName += (char)curChr;
482 else
483 throw std::invalid_argument("Illegal character in node name");
484 }
485 }
486
parseSimpleCaseList(vector<CaseTreeNode * > & nodeStack,std::istream & in,bool reportDuplicates)487 static void parseSimpleCaseList (vector<CaseTreeNode*>& nodeStack, std::istream& in, bool reportDuplicates)
488 {
489 // \note Algorithm assumes that cases are sorted by groups, but will
490 // function fine, albeit more slowly, if that is not the case.
491 int stackPos = 0;
492 string curName;
493
494 for (;;)
495 {
496 const int curChr = in.get();
497
498 if (curChr == std::char_traits<char>::eof() || curChr == 0 || curChr == '\n' || curChr == '\r')
499 {
500 if (curName.empty())
501 throw std::invalid_argument("Empty test case name");
502
503 if (!nodeStack[stackPos]->hasChild(curName))
504 {
505 CaseTreeNode* const newChild = new CaseTreeNode(curName);
506
507 try
508 {
509 nodeStack[stackPos]->addChild(newChild);
510 }
511 catch (...)
512 {
513 delete newChild;
514 throw;
515 }
516 }
517 else if (reportDuplicates)
518 throw std::invalid_argument("Duplicate test case");
519
520 curName.clear();
521 stackPos = 0;
522
523 if (curChr == '\r' && in.peek() == '\n')
524 in.get();
525
526 {
527 const int nextChr = in.peek();
528
529 if (nextChr == std::char_traits<char>::eof() || nextChr == 0)
530 break;
531 }
532 }
533 else if (curChr == '.')
534 {
535 if (curName.empty())
536 throw std::invalid_argument("Empty test group name");
537
538 if ((int)nodeStack.size() <= stackPos+1)
539 nodeStack.resize(nodeStack.size()*2, DE_NULL);
540
541 if (!nodeStack[stackPos+1] || nodeStack[stackPos+1]->getName() != curName)
542 {
543 CaseTreeNode* curGroup = nodeStack[stackPos]->getChild(curName);
544
545 if (!curGroup)
546 {
547 curGroup = new CaseTreeNode(curName);
548
549 try
550 {
551 nodeStack[stackPos]->addChild(curGroup);
552 }
553 catch (...)
554 {
555 delete curGroup;
556 throw;
557 }
558 }
559
560 nodeStack[stackPos+1] = curGroup;
561
562 if ((int)nodeStack.size() > stackPos+2)
563 nodeStack[stackPos+2] = DE_NULL; // Invalidate rest of entries
564 }
565
566 DE_ASSERT(nodeStack[stackPos+1]->getName() == curName);
567
568 curName.clear();
569 stackPos += 1;
570 }
571 else if (isValidTestCaseNameChar((char)curChr))
572 curName += (char)curChr;
573 else
574 throw std::invalid_argument("Illegal character in test case name");
575 }
576 }
577
parseCaseList(CaseTreeNode * root,std::istream & in,bool reportDuplicates)578 static void parseCaseList (CaseTreeNode* root, std::istream& in, bool reportDuplicates)
579 {
580 vector<CaseTreeNode*> nodeStack(8, root);
581 parseSimpleCaseList(nodeStack, in, reportDuplicates);
582 }
583
parseGroupFile(CaseTreeNode * root,std::istream & inGroupList,const tcu::Archive & archive,bool reportDuplicates)584 static void parseGroupFile(CaseTreeNode* root, std::istream& inGroupList, const tcu::Archive& archive, bool reportDuplicates)
585 {
586 // read whole file and remove all '\r'
587 std::string buffer(std::istreambuf_iterator<char>(inGroupList), {});
588 buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
589
590 vector<CaseTreeNode*> nodeStack(8, root);
591 std::stringstream namesStream(buffer);
592 std::string fileName;
593
594 while (std::getline(namesStream, fileName))
595 {
596 de::FilePath groupPath (fileName);
597 de::UniquePtr<Resource> groupResource (archive.getResource(groupPath.normalize().getPath()));
598 const int groupBufferSize (groupResource->getSize());
599 std::vector<char> groupBuffer (static_cast<size_t>(groupBufferSize));
600
601 groupResource->read(reinterpret_cast<deUint8*>(&groupBuffer[0]), groupBufferSize);
602 if (groupBuffer.empty())
603 throw Exception("Empty case list resource");
604
605 std::istringstream groupIn(std::string(groupBuffer.begin(), groupBuffer.end()));
606 parseSimpleCaseList(nodeStack, groupIn, reportDuplicates);
607 }
608 }
609
parseCaseList(std::istream & in,const tcu::Archive & archive,const char * path=DE_NULL)610 static CaseTreeNode* parseCaseList (std::istream& in, const tcu::Archive& archive, const char* path = DE_NULL)
611 {
612 CaseTreeNode* const root = new CaseTreeNode("");
613 try
614 {
615 if (in.peek() == '{')
616 parseCaseTrie(root, in);
617 else
618 {
619 // if we are reading cases from file determine if we are
620 // reading group file or plain list of cases; this is done by
621 // reading single line and checking if it ends with ".txt"
622 bool readGroupFile = false;
623 if (path)
624 {
625 // read the first line and make sure it doesn't contain '\r'
626 std::string line;
627 std::getline(in, line);
628 line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
629
630 const std::string ending = ".txt";
631 readGroupFile = (line.length() > ending.length()) &&
632 std::equal(ending.rbegin(), ending.rend(), line.rbegin());
633
634 // move to the beginning of the file to parse first line too
635 in.seekg(0, in.beg);
636 }
637
638 if (readGroupFile)
639 parseGroupFile(root, in, archive, true);
640 else
641 parseCaseList(root, in, true);
642 }
643
644 {
645 const int curChr = in.get();
646 if (curChr != std::char_traits<char>::eof() && curChr != 0)
647 throw std::invalid_argument("Trailing characters at end of case list");
648 }
649
650 return root;
651 }
652 catch (...)
653 {
654 delete root;
655 throw;
656 }
657 }
658
659 class CasePaths
660 {
661 public:
662 CasePaths(const string& pathList);
663 CasePaths(const vector<string>& pathList);
664 bool matches(const string& caseName, bool allowPrefix = false) const;
665
666 private:
667 const vector<string> m_casePatterns;
668 };
669
CasePaths(const string & pathList)670 CasePaths::CasePaths (const string& pathList)
671 : m_casePatterns(de::splitString(pathList, ','))
672 {
673 }
674
CasePaths(const vector<string> & pathList)675 CasePaths::CasePaths(const vector<string>& pathList)
676 : m_casePatterns(pathList)
677 {
678 }
679
680 // 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)681 bool matchWildcards(string::const_iterator patternStart,
682 string::const_iterator patternEnd,
683 string::const_iterator pathStart,
684 string::const_iterator pathEnd,
685 bool allowPrefix)
686 {
687 string::const_iterator pattern = patternStart;
688 string::const_iterator path = pathStart;
689
690 while (pattern != patternEnd && path != pathEnd && *pattern == *path)
691 {
692 ++pattern;
693 ++path;
694 }
695
696 if (pattern == patternEnd)
697 return (path == pathEnd);
698 else if (*pattern == '*')
699 {
700 string::const_iterator patternNext = pattern + 1;
701 if (patternNext != patternEnd) {
702 for (; path != pathEnd; ++path)
703 {
704 if (*patternNext == *path)
705 if (matchWildcards(patternNext, patternEnd, path, pathEnd, allowPrefix))
706 return true;
707 }
708 }
709
710 if (matchWildcards(pattern + 1, patternEnd, pathEnd, pathEnd, allowPrefix))
711 return true;
712 }
713 else if (path == pathEnd && allowPrefix)
714 return true;
715
716 return false;
717 }
718
719 #if defined(TCU_HIERARCHICAL_CASEPATHS)
720 // Match a list of pattern components to a list of path components. A pattern
721 // component may contain *-wildcards. A pattern component "**" matches zero or
722 // 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)723 static bool patternMatches(vector<string>::const_iterator patternStart,
724 vector<string>::const_iterator patternEnd,
725 vector<string>::const_iterator pathStart,
726 vector<string>::const_iterator pathEnd,
727 bool allowPrefix)
728 {
729 vector<string>::const_iterator pattern = patternStart;
730 vector<string>::const_iterator path = pathStart;
731
732 while (pattern != patternEnd && path != pathEnd && *pattern != "**" &&
733 (*pattern == *path || matchWildcards(pattern->begin(), pattern->end(),
734 path->begin(), path->end(), false)))
735 {
736 ++pattern;
737 ++path;
738 }
739
740 if (path == pathEnd && (allowPrefix || pattern == patternEnd))
741 return true;
742 else if (pattern != patternEnd && *pattern == "**")
743 {
744 for (; path != pathEnd; ++path)
745 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
746 return true;
747 if (patternMatches(pattern + 1, patternEnd, path, pathEnd, allowPrefix))
748 return true;
749 }
750
751 return false;
752 }
753 #endif
754
matches(const string & caseName,bool allowPrefix) const755 bool CasePaths::matches (const string& caseName, bool allowPrefix) const
756 {
757 #if defined(TCU_HIERARCHICAL_CASEPATHS)
758 const vector<string> components = de::splitString(caseName, '.');
759 #endif
760
761 for (size_t ndx = 0; ndx < m_casePatterns.size(); ++ndx)
762 {
763 #if defined(TCU_HIERARCHICAL_CASEPATHS)
764 const vector<string> patternComponents = de::splitString(m_casePatterns[ndx], '.');
765
766 if (patternMatches(patternComponents.begin(), patternComponents.end(),
767 components.begin(), components.end(), allowPrefix))
768 return true;
769 #else
770 if (matchWildcards(m_casePatterns[ndx].begin(), m_casePatterns[ndx].end(),
771 caseName.begin(), caseName.end(), allowPrefix))
772 return true;
773 #endif
774 }
775
776 return false;
777 }
778
779 /*--------------------------------------------------------------------*//*!
780 * \brief Construct command line
781 * \note CommandLine is not fully initialized until parse() has been called.
782 *//*--------------------------------------------------------------------*/
CommandLine(void)783 CommandLine::CommandLine (void)
784 : m_appName(), m_logFlags(0)
785 {
786 }
787
788 /*--------------------------------------------------------------------*//*!
789 * \brief Construct command line from standard argc, argv pair.
790 *
791 * Calls parse() with given arguments
792 * \param archive application's assets
793 * \param argc Number of arguments
794 * \param argv Command line arguments
795 *//*--------------------------------------------------------------------*/
CommandLine(int argc,const char * const * argv)796 CommandLine::CommandLine (int argc, const char* const* argv)
797 : m_appName(argv[0]), m_logFlags (0)
798 {
799 if (argc > 1)
800 {
801 int loop = 1; // skip application name
802 while (true)
803 {
804 m_initialCmdLine += std::string(argv[loop++]);
805 if (loop >= argc)
806 break;
807 m_initialCmdLine += " ";
808 }
809 }
810
811 if (!parse(argc, argv))
812 throw Exception("Failed to parse command line");
813 }
814
815 /*--------------------------------------------------------------------*//*!
816 * \brief Construct command line from string.
817 *
818 * Calls parse() with given argument.
819 * \param archive application's assets
820 * \param cmdLine Full command line string.
821 *//*--------------------------------------------------------------------*/
CommandLine(const std::string & cmdLine)822 CommandLine::CommandLine (const std::string& cmdLine)
823 : m_appName(), m_initialCmdLine (cmdLine)
824 {
825 if (!parse(cmdLine))
826 throw Exception("Failed to parse command line");
827 }
828
~CommandLine(void)829 CommandLine::~CommandLine (void)
830 {
831 }
832
clear(void)833 void CommandLine::clear (void)
834 {
835 m_cmdLine.clear();
836 m_logFlags = 0;
837 }
838
getCommandLine(void) const839 const de::cmdline::CommandLine& CommandLine::getCommandLine (void) const
840 {
841 return m_cmdLine;
842 }
843
getApplicationName(void) const844 const std::string& CommandLine::getApplicationName(void) const
845 {
846 return m_appName;
847 }
848
getInitialCmdLine(void) const849 const std::string& CommandLine::getInitialCmdLine(void) const
850 {
851 return m_initialCmdLine;
852 }
853
registerExtendedOptions(de::cmdline::Parser & parser)854 void CommandLine::registerExtendedOptions (de::cmdline::Parser& parser)
855 {
856 DE_UNREF(parser);
857 }
858
859 /*--------------------------------------------------------------------*//*!
860 * \brief Parse command line from standard argc, argv pair.
861 * \note parse() must be called exactly once.
862 * \param argc Number of arguments
863 * \param argv Command line arguments
864 *//*--------------------------------------------------------------------*/
parse(int argc,const char * const * argv)865 bool CommandLine::parse (int argc, const char* const* argv)
866 {
867 DebugOutStreambuf sbuf;
868 std::ostream debugOut (&sbuf);
869 de::cmdline::Parser parser;
870
871 opt::registerOptions(parser);
872 opt::registerLegacyOptions(parser);
873 registerExtendedOptions(parser);
874
875 clear();
876
877 if (!parser.parse(argc-1, argv+1, &m_cmdLine, std::cerr))
878 {
879 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
880 parser.help(debugOut);
881
882 clear();
883 return false;
884 }
885
886 if (!m_cmdLine.getOption<opt::LogImages>())
887 m_logFlags |= QP_TEST_LOG_EXCLUDE_IMAGES;
888
889 if (!m_cmdLine.getOption<opt::LogShaderSources>())
890 m_logFlags |= QP_TEST_LOG_EXCLUDE_SHADER_SOURCES;
891
892 if (!m_cmdLine.getOption<opt::LogFlush>())
893 m_logFlags |= QP_TEST_LOG_NO_FLUSH;
894
895 if (!m_cmdLine.getOption<opt::LogEmptyLoginfo>())
896 m_logFlags |= QP_TEST_LOG_EXCLUDE_EMPTY_LOGINFO;
897
898 if (m_cmdLine.getOption<opt::SubProcess>())
899 m_logFlags |= QP_TEST_LOG_NO_INITIAL_OUTPUT;
900
901 if ((m_cmdLine.hasOption<opt::CasePath>()?1:0) +
902 (m_cmdLine.hasOption<opt::CaseList>()?1:0) +
903 (m_cmdLine.hasOption<opt::CaseListFile>()?1:0) +
904 (m_cmdLine.hasOption<opt::CaseListResource>()?1:0) +
905 (m_cmdLine.getOption<opt::StdinCaseList>()?1:0) > 1)
906 {
907 debugOut << "ERROR: multiple test case list options given!\n" << std::endl;
908 clear();
909 return false;
910 }
911
912 if (m_cmdLine.getArgs().size() > 0)
913 {
914 debugOut << "ERROR: arguments not starting with '-' or '--' are not supported by this application!\n" << std::endl;
915
916 debugOut << "\n" << de::FilePath(argv[0]).getBaseName() << " [options]\n\n";
917 parser.help(debugOut);
918
919 clear();
920 return false;
921 }
922
923 return true;
924 }
925
926 /*--------------------------------------------------------------------*//*!
927 * \brief Parse command line from string.
928 * \note parse() must be called exactly once.
929 * \param cmdLine Full command line string.
930 *//*--------------------------------------------------------------------*/
parse(const std::string & cmdLine)931 bool CommandLine::parse (const std::string& cmdLine)
932 {
933 deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str());
934 if (!parsedCmdLine)
935 throw std::bad_alloc();
936
937 bool isOk = false;
938 try
939 {
940 isOk = parse(parsedCmdLine->numArgs, parsedCmdLine->args);
941 }
942 catch (...)
943 {
944 deCommandLine_destroy(parsedCmdLine);
945 throw;
946 }
947
948 deCommandLine_destroy(parsedCmdLine);
949 return isOk;
950 }
951
getLogFileName(void) const952 const char* CommandLine::getLogFileName (void) const { return m_cmdLine.getOption<opt::LogFilename>().c_str(); }
getLogFlags(void) const953 deUint32 CommandLine::getLogFlags (void) const { return m_logFlags; }
getRunMode(void) const954 RunMode CommandLine::getRunMode (void) const { return m_cmdLine.getOption<opt::RunMode>(); }
getCaseListExportFile(void) const955 const char* CommandLine::getCaseListExportFile (void) const { return m_cmdLine.getOption<opt::ExportFilenamePattern>().c_str(); }
getVisibility(void) const956 WindowVisibility CommandLine::getVisibility (void) const { return m_cmdLine.getOption<opt::Visibility>(); }
isWatchDogEnabled(void) const957 bool CommandLine::isWatchDogEnabled (void) const { return m_cmdLine.getOption<opt::WatchDog>(); }
isCrashHandlingEnabled(void) const958 bool CommandLine::isCrashHandlingEnabled (void) const { return m_cmdLine.getOption<opt::CrashHandler>(); }
getBaseSeed(void) const959 int CommandLine::getBaseSeed (void) const { return m_cmdLine.getOption<opt::BaseSeed>(); }
getTestIterationCount(void) const960 int CommandLine::getTestIterationCount (void) const { return m_cmdLine.getOption<opt::TestIterationCount>(); }
getSurfaceWidth(void) const961 int CommandLine::getSurfaceWidth (void) const { return m_cmdLine.getOption<opt::SurfaceWidth>(); }
getSurfaceHeight(void) const962 int CommandLine::getSurfaceHeight (void) const { return m_cmdLine.getOption<opt::SurfaceHeight>(); }
getSurfaceType(void) const963 SurfaceType CommandLine::getSurfaceType (void) const { return m_cmdLine.getOption<opt::SurfaceType>(); }
getScreenRotation(void) const964 ScreenRotation CommandLine::getScreenRotation (void) const { return m_cmdLine.getOption<opt::ScreenRotation>(); }
getGLConfigId(void) const965 int CommandLine::getGLConfigId (void) const { return m_cmdLine.getOption<opt::GLConfigID>(); }
getCLPlatformId(void) const966 int CommandLine::getCLPlatformId (void) const { return m_cmdLine.getOption<opt::CLPlatformID>(); }
getCLDeviceIds(void) const967 const std::vector<int>& CommandLine::getCLDeviceIds (void) const { return m_cmdLine.getOption<opt::CLDeviceIDs>(); }
getVKDeviceId(void) const968 int CommandLine::getVKDeviceId (void) const { return m_cmdLine.getOption<opt::VKDeviceID>(); }
getVKDeviceGroupId(void) const969 int CommandLine::getVKDeviceGroupId (void) const { return m_cmdLine.getOption<opt::VKDeviceGroupID>(); }
isValidationEnabled(void) const970 bool CommandLine::isValidationEnabled (void) const { return m_cmdLine.getOption<opt::Validation>(); }
printValidationErrors(void) const971 bool CommandLine::printValidationErrors (void) const { return m_cmdLine.getOption<opt::PrintValidationErrors>(); }
isLogDecompiledSpirvEnabled(void) const972 bool CommandLine::isLogDecompiledSpirvEnabled (void) const { return m_cmdLine.getOption<opt::LogDecompiledSpirv>(); }
isOutOfMemoryTestEnabled(void) const973 bool CommandLine::isOutOfMemoryTestEnabled (void) const { return m_cmdLine.getOption<opt::TestOOM>(); }
isShadercacheEnabled(void) const974 bool CommandLine::isShadercacheEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCache>(); }
getShaderCacheFilename(void) const975 const char* CommandLine::getShaderCacheFilename (void) const { return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str(); }
isShaderCacheTruncateEnabled(void) const976 bool CommandLine::isShaderCacheTruncateEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheTruncate>(); }
isShaderCacheIPCEnabled(void) const977 bool CommandLine::isShaderCacheIPCEnabled (void) const { return m_cmdLine.getOption<opt::ShaderCacheIPC>(); }
getOptimizationRecipe(void) const978 int CommandLine::getOptimizationRecipe (void) const { return m_cmdLine.getOption<opt::Optimization>(); }
isSpirvOptimizationEnabled(void) const979 bool CommandLine::isSpirvOptimizationEnabled (void) const { return m_cmdLine.getOption<opt::OptimizeSpirv>(); }
isRenderDocEnabled(void) const980 bool CommandLine::isRenderDocEnabled (void) const { return m_cmdLine.getOption<opt::RenderDoc>(); }
getWaiverFileName(void) const981 const char* CommandLine::getWaiverFileName (void) const { return m_cmdLine.getOption<opt::WaiverFile>().c_str(); }
getCaseFraction(void) const982 const std::vector<int>& CommandLine::getCaseFraction (void) const { return m_cmdLine.getOption<opt::CaseFraction>(); }
getCaseFractionMandatoryTests(void) const983 const char* CommandLine::getCaseFractionMandatoryTests (void) const { return m_cmdLine.getOption<opt::CaseFractionMandatoryTests>().c_str(); }
getArchiveDir(void) const984 const char* CommandLine::getArchiveDir (void) const { return m_cmdLine.getOption<opt::ArchiveDir>().c_str(); }
getRunnerType(void) const985 tcu::TestRunnerType CommandLine::getRunnerType (void) const { return m_cmdLine.getOption<opt::RunnerType>(); }
isTerminateOnFailEnabled(void) const986 bool CommandLine::isTerminateOnFailEnabled (void) const { return m_cmdLine.getOption<opt::TerminateOnFail>(); }
isSubProcess(void) const987 bool CommandLine::isSubProcess (void) const { return m_cmdLine.getOption<opt::SubProcess>(); }
getSubprocessTestCount(void) const988 int CommandLine::getSubprocessTestCount (void) const { return m_cmdLine.getOption<opt::SubprocessTestCount>(); }
getCommandPoolMinSize(void) const989 int CommandLine::getCommandPoolMinSize (void) const { return m_cmdLine.getOption<opt::CommandPoolMinSize>(); }
getCommandBufferMinSize(void) const990 int CommandLine::getCommandBufferMinSize (void) const { return m_cmdLine.getOption<opt::CommandBufferMinSize>(); }
getCommandDefaultSize(void) const991 int CommandLine::getCommandDefaultSize (void) const { return m_cmdLine.getOption<opt::CommandDefaultSize>(); }
getPipelineDefaultSize(void) const992 int CommandLine::getPipelineDefaultSize (void) const { return m_cmdLine.getOption<opt::PipelineDefaultSize>(); }
993
getGLContextType(void) const994 const char* CommandLine::getGLContextType (void) const
995 {
996 if (m_cmdLine.hasOption<opt::GLContextType>())
997 return m_cmdLine.getOption<opt::GLContextType>().c_str();
998 else
999 return DE_NULL;
1000 }
getGLConfigName(void) const1001 const char* CommandLine::getGLConfigName (void) const
1002 {
1003 if (m_cmdLine.hasOption<opt::GLConfigName>())
1004 return m_cmdLine.getOption<opt::GLConfigName>().c_str();
1005 else
1006 return DE_NULL;
1007 }
1008
getGLContextFlags(void) const1009 const char* CommandLine::getGLContextFlags (void) const
1010 {
1011 if (m_cmdLine.hasOption<opt::GLContextFlags>())
1012 return m_cmdLine.getOption<opt::GLContextFlags>().c_str();
1013 else
1014 return DE_NULL;
1015 }
1016
getCLBuildOptions(void) const1017 const char* CommandLine::getCLBuildOptions (void) const
1018 {
1019 if (m_cmdLine.hasOption<opt::CLBuildOptions>())
1020 return m_cmdLine.getOption<opt::CLBuildOptions>().c_str();
1021 else
1022 return DE_NULL;
1023 }
1024
getEGLDisplayType(void) const1025 const char* CommandLine::getEGLDisplayType (void) const
1026 {
1027 if (m_cmdLine.hasOption<opt::EGLDisplayType>())
1028 return m_cmdLine.getOption<opt::EGLDisplayType>().c_str();
1029 else
1030 return DE_NULL;
1031 }
1032
getEGLWindowType(void) const1033 const char* CommandLine::getEGLWindowType (void) const
1034 {
1035 if (m_cmdLine.hasOption<opt::EGLWindowType>())
1036 return m_cmdLine.getOption<opt::EGLWindowType>().c_str();
1037 else
1038 return DE_NULL;
1039 }
1040
getEGLPixmapType(void) const1041 const char* CommandLine::getEGLPixmapType (void) const
1042 {
1043 if (m_cmdLine.hasOption<opt::EGLPixmapType>())
1044 return m_cmdLine.getOption<opt::EGLPixmapType>().c_str();
1045 else
1046 return DE_NULL;
1047 }
1048
getSubprocessConfigFile(void) const1049 const char* CommandLine::getSubprocessConfigFile (void) const
1050 {
1051 if (m_cmdLine.hasOption<opt::SubprocessConfigFile>())
1052 return m_cmdLine.getOption<opt::SubprocessConfigFile>().c_str();
1053 else
1054 return DE_NULL;
1055 }
1056
1057
getServerAddress(void) const1058 const char* CommandLine::getServerAddress (void) const
1059 {
1060 if (m_cmdLine.hasOption<opt::ServerAddress>())
1061 return m_cmdLine.getOption<opt::ServerAddress>().c_str();
1062 else
1063 return DE_NULL;
1064 }
1065
getPipelineCompilerPath(void) const1066 const char* CommandLine::getPipelineCompilerPath(void) const
1067 {
1068 if (m_cmdLine.hasOption<opt::PipelineCompilerPath>())
1069 return m_cmdLine.getOption<opt::PipelineCompilerPath>().c_str();
1070 else
1071 return DE_NULL;
1072 }
1073
getPipelineCompilerDataDir(void) const1074 const char* CommandLine::getPipelineCompilerDataDir(void) const
1075 {
1076 if (m_cmdLine.hasOption<opt::PipelineCompilerDataDir>())
1077 return m_cmdLine.getOption<opt::PipelineCompilerDataDir>().c_str();
1078 else
1079 return DE_NULL;
1080 }
1081
getPipelineCompilerArgs(void) const1082 const char* CommandLine::getPipelineCompilerArgs(void) const
1083 {
1084 if (m_cmdLine.hasOption<opt::PipelineCompilerArgs>())
1085 return m_cmdLine.getOption<opt::PipelineCompilerArgs>().c_str();
1086 else
1087 return DE_NULL;
1088 }
1089
getPipelineCompilerOutputFile(void) const1090 const char* CommandLine::getPipelineCompilerOutputFile(void) const
1091 {
1092 if (m_cmdLine.hasOption<opt::PipelineCompilerOutputFile>())
1093 return m_cmdLine.getOption<opt::PipelineCompilerOutputFile>().c_str();
1094 else
1095 return DE_NULL;
1096 }
1097
getPipelineCompilerLogFile(void) const1098 const char* CommandLine::getPipelineCompilerLogFile(void) const
1099 {
1100 if (m_cmdLine.hasOption<opt::PipelineCompilerLogFile>())
1101 return m_cmdLine.getOption<opt::PipelineCompilerLogFile>().c_str();
1102 else
1103 return DE_NULL;
1104 }
1105
getPipelineCompilerFilePrefix(void) const1106 const char* CommandLine::getPipelineCompilerFilePrefix(void) const
1107 {
1108 if (m_cmdLine.hasOption<opt::PipelineCompilerFilePrefix>())
1109 return m_cmdLine.getOption<opt::PipelineCompilerFilePrefix>().c_str();
1110 else
1111 return DE_NULL;
1112 }
1113
getVkLibraryPath(void) const1114 const char* CommandLine::getVkLibraryPath(void) const
1115 {
1116 if (m_cmdLine.hasOption<opt::VkLibraryPath>())
1117 return (m_cmdLine.getOption<opt::VkLibraryPath>() != "") ? m_cmdLine.getOption<opt::VkLibraryPath>().c_str() : DE_NULL;
1118 else
1119 return DE_NULL;
1120 }
1121
getAppParamsInputFilePath(void) const1122 const char* CommandLine::getAppParamsInputFilePath(void) const
1123 {
1124 if (m_cmdLine.hasOption<opt::ApplicationParametersInputFile>())
1125 return m_cmdLine.getOption<opt::ApplicationParametersInputFile>().c_str();
1126 else
1127 return DE_NULL;
1128 }
1129
checkTestGroupName(const CaseTreeNode * root,const char * groupPath)1130 static bool checkTestGroupName (const CaseTreeNode* root, const char* groupPath)
1131 {
1132 const CaseTreeNode* node = findNode(root, groupPath);
1133 return node && node->hasChildren();
1134 }
1135
checkTestCaseName(const CaseTreeNode * root,const char * casePath)1136 static bool checkTestCaseName (const CaseTreeNode* root, const char* casePath)
1137 {
1138 const CaseTreeNode* node = findNode(root, casePath);
1139 return node && !node->hasChildren();
1140 }
1141
createCaseListFilter(const tcu::Archive & archive) const1142 de::MovePtr<CaseListFilter> CommandLine::createCaseListFilter (const tcu::Archive& archive) const
1143 {
1144 return de::MovePtr<CaseListFilter>(new CaseListFilter(m_cmdLine, archive));
1145 }
1146
checkTestGroupName(const char * groupName) const1147 bool CaseListFilter::checkTestGroupName (const char* groupName) const
1148 {
1149 bool result = false;
1150 if (m_casePaths)
1151 result = m_casePaths->matches(groupName, true);
1152 else if (m_caseTree)
1153 result = ( groupName[0] == 0 || tcu::checkTestGroupName(m_caseTree, groupName) );
1154 else
1155 return true;
1156 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1157 result = m_caseFractionMandatoryTests->matches(groupName, true);
1158 return result;
1159 }
1160
checkTestCaseName(const char * caseName) const1161 bool CaseListFilter::checkTestCaseName (const char* caseName) const
1162 {
1163 bool result = false;
1164 if (m_casePaths)
1165 result = m_casePaths->matches(caseName, false);
1166 else if (m_caseTree)
1167 result = tcu::checkTestCaseName(m_caseTree, caseName);
1168 else
1169 return true;
1170 if (!result && m_caseFractionMandatoryTests.get() != DE_NULL)
1171 result = m_caseFractionMandatoryTests->matches(caseName, false);
1172 return result;
1173 }
1174
checkCaseFraction(int i,const std::string & testCaseName) const1175 bool CaseListFilter::checkCaseFraction (int i, const std::string& testCaseName) const
1176 {
1177 return m_caseFraction.size() != 2 ||
1178 ((i % m_caseFraction[1]) == m_caseFraction[0]) ||
1179 (m_caseFractionMandatoryTests.get()!=DE_NULL && m_caseFractionMandatoryTests->matches(testCaseName));
1180 }
1181
CaseListFilter(void)1182 CaseListFilter::CaseListFilter (void)
1183 : m_caseTree (DE_NULL)
1184 , m_runnerType (tcu::RUNNERTYPE_ANY)
1185 {
1186 }
1187
CaseListFilter(const de::cmdline::CommandLine & cmdLine,const tcu::Archive & archive)1188 CaseListFilter::CaseListFilter (const de::cmdline::CommandLine& cmdLine, const tcu::Archive& archive)
1189 : m_caseTree (DE_NULL)
1190 {
1191 if (cmdLine.getOption<opt::RunMode>() == RUNMODE_VERIFY_AMBER_COHERENCY)
1192 {
1193 m_runnerType = RUNNERTYPE_AMBER;
1194 }
1195 else
1196 {
1197 m_runnerType = cmdLine.getOption<opt::RunnerType>();
1198 }
1199
1200 if (cmdLine.hasOption<opt::CaseList>())
1201 {
1202 std::istringstream str(cmdLine.getOption<opt::CaseList>());
1203
1204 m_caseTree = parseCaseList(str, archive);
1205 }
1206 else if (cmdLine.hasOption<opt::CaseListFile>())
1207 {
1208 std::string caseListFile = cmdLine.getOption<opt::CaseListFile>();
1209 std::ifstream in(caseListFile.c_str(), std::ios_base::binary);
1210
1211 if (!in.is_open() || !in.good())
1212 throw Exception("Failed to open case list file '" + caseListFile + "'");
1213
1214 m_caseTree = parseCaseList(in, archive, caseListFile.c_str());
1215 }
1216 else if (cmdLine.hasOption<opt::CaseListResource>())
1217 {
1218 // \todo [2016-11-14 pyry] We are cloning potentially large buffers here. Consider writing
1219 // istream adaptor for tcu::Resource.
1220 de::UniquePtr<Resource> caseListResource (archive.getResource(cmdLine.getOption<opt::CaseListResource>().c_str()));
1221 const int bufferSize = caseListResource->getSize();
1222 std::vector<char> buffer ((size_t)bufferSize);
1223
1224 if (buffer.empty())
1225 throw Exception("Empty case list resource");
1226
1227 caseListResource->read(reinterpret_cast<deUint8*>(&buffer[0]), bufferSize);
1228
1229 {
1230 std::istringstream in (std::string(&buffer[0], (size_t)bufferSize));
1231
1232 m_caseTree = parseCaseList(in, archive);
1233 }
1234 }
1235 else if (cmdLine.getOption<opt::StdinCaseList>())
1236 {
1237 m_caseTree = parseCaseList(std::cin, archive);
1238 }
1239 else if (cmdLine.hasOption<opt::CasePath>())
1240 m_casePaths = de::MovePtr<const CasePaths>(new CasePaths(cmdLine.getOption<opt::CasePath>()));
1241
1242 if (!cmdLine.getOption<opt::SubProcess>())
1243 m_caseFraction = cmdLine.getOption<opt::CaseFraction>();
1244
1245 if (m_caseFraction.size() == 2 &&
1246 (m_caseFraction[0] < 0 || m_caseFraction[1] <= 0 || m_caseFraction[0] >= m_caseFraction[1] ))
1247 throw Exception("Invalid case fraction. First element must be non-negative and less than second element. Second element must be greater than 0.");
1248
1249 if (m_caseFraction.size() != 0 && m_caseFraction.size() != 2)
1250 throw Exception("Invalid case fraction. Must have two components.");
1251
1252 if (m_caseFraction.size() == 2)
1253 {
1254 std::string caseFractionMandatoryTestsFilename = cmdLine.getOption<opt::CaseFractionMandatoryTests>();
1255
1256 if (!caseFractionMandatoryTestsFilename.empty())
1257 {
1258 std::ifstream fileStream(caseFractionMandatoryTestsFilename.c_str(), std::ios_base::binary);
1259 if (!fileStream.is_open() || !fileStream.good())
1260 throw Exception("Failed to open case fraction mandatory test list: '" + caseFractionMandatoryTestsFilename + "'");
1261
1262 std::vector<std::string> cfPaths;
1263 std::string line;
1264
1265 while (std::getline(fileStream, line))
1266 {
1267 line.erase(std::remove(std::begin(line), std::end(line), '\r'), std::end(line));
1268 cfPaths.push_back(line);
1269 }
1270 if (!cfPaths.empty())
1271 {
1272 m_caseFractionMandatoryTests = de::MovePtr<const CasePaths>(new CasePaths(cfPaths));
1273 if (m_caseTree != DE_NULL)
1274 {
1275 fileStream.clear();
1276 fileStream.seekg(0, fileStream.beg);
1277 parseCaseList(m_caseTree, fileStream, false);
1278 }
1279 }
1280 }
1281 }
1282 }
1283
~CaseListFilter(void)1284 CaseListFilter::~CaseListFilter (void)
1285 {
1286 delete m_caseTree;
1287 }
1288
1289 } // tcu
1290