Merge "Report tests using Draw*BaseVertex as NotSupported" am: f96636fdfa
[platform/upstream/VK-GL-CTS.git] / framework / delibs / decpp / deCommandLine.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements C++ Base Library
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 parser.
22  *//*--------------------------------------------------------------------*/
23
24 #include "deCommandLine.hpp"
25
26 #include <set>
27 #include <sstream>
28 #include <cstring>
29 #include <stdexcept>
30 #include <algorithm>
31
32 namespace de
33 {
34 namespace cmdline
35 {
36
37 namespace
38 {
39 struct Help { typedef bool ValueType; };
40 }
41
42 namespace detail
43 {
44
45 inline const char* getNamedValueName (const void* namedValue)
46 {
47         return static_cast<const NamedValue<deUint8>*>(namedValue)->name;
48 }
49
50 using std::set;
51
52 TypedFieldMap::TypedFieldMap (void)
53 {
54 }
55
56 TypedFieldMap::~TypedFieldMap (void)
57 {
58         clear();
59 }
60
61 void TypedFieldMap::clear (void)
62 {
63         for (Map::const_iterator iter = m_fields.begin(); iter != m_fields.end(); ++iter)
64         {
65                 if (iter->second.value)
66                         iter->second.destructor(iter->second.value);
67         }
68         m_fields.clear();
69 }
70
71 bool TypedFieldMap::contains (const std::type_info* key) const
72 {
73         return m_fields.find(key) != m_fields.end();
74 }
75
76 const TypedFieldMap::Entry& TypedFieldMap::get (const std::type_info* key) const
77 {
78         Map::const_iterator pos = m_fields.find(key);
79         if (pos != m_fields.end())
80                 return pos->second;
81         else
82                 throw std::out_of_range("Value not set");
83 }
84
85 void TypedFieldMap::set (const std::type_info* key, const Entry& value)
86 {
87         Map::iterator pos = m_fields.find(key);
88
89         if (pos != m_fields.end())
90         {
91                 pos->second.destructor(pos->second.value);
92                 pos->second.value = DE_NULL;
93
94                 pos->second = value;
95         }
96         else
97                 m_fields.insert(std::make_pair(key, value));
98 }
99
100 Parser::Parser (void)
101 {
102         addOption(Option<Help>("h", "help", "Show this help"));
103 }
104
105 Parser::~Parser (void)
106 {
107 }
108
109 void Parser::addOption (const OptInfo& option)
110 {
111         m_options.push_back(option);
112 }
113
114 bool Parser::parse (int numArgs, const char* const* args, CommandLine* dst, std::ostream& err) const
115 {
116         typedef map<string, const OptInfo*> OptMap;
117         typedef set<const OptInfo*> OptSet;
118
119         OptMap  shortOptMap;
120         OptMap  longOptMap;
121         OptSet  seenOpts;
122         bool    allOk                   = true;
123
124         DE_ASSERT(dst->m_args.empty() && dst->m_options.empty());
125
126         for (vector<OptInfo>::const_iterator optIter = m_options.begin(); optIter != m_options.end(); optIter++)
127         {
128                 const OptInfo& opt = *optIter;
129
130                 DE_ASSERT(opt.shortName || opt.longName);
131
132                 if (opt.shortName)
133                 {
134                         DE_ASSERT(shortOptMap.find(opt.shortName) == shortOptMap.end());
135                         shortOptMap[opt.shortName] = &opt;
136                 }
137
138                 if (opt.longName)
139                 {
140                         DE_ASSERT(longOptMap.find(opt.longName) == longOptMap.end());
141                         longOptMap[opt.longName] = &opt;
142                 }
143
144                 // Set default values.
145                 if (opt.defaultValue)
146                         opt.dispatchParse(&opt, opt.defaultValue, &dst->m_options);
147                 else if (opt.setDefault)
148                         opt.setDefault(&dst->m_options);
149         }
150
151         DE_ASSERT(!dst->m_options.get<Help>());
152
153         for (int argNdx = 0; argNdx < numArgs; argNdx++)
154         {
155                 const char*             arg             = args[argNdx];
156                 int                             argLen  = (int)strlen(arg);
157
158                 if (arg[0] == '-' && arg[1] == '-' && arg[2] == 0)
159                 {
160                         // End of option list (--)
161                         for (int optNdx = argNdx+1; optNdx < numArgs; optNdx++)
162                                 dst->m_args.push_back(args[optNdx]);
163                         break;
164                 }
165                 else if (arg[0] == '-')
166                 {
167                         const bool                              isLongName      = arg[1] == '-';
168                         const char*                             nameStart       = arg + (isLongName ? 2 : 1);
169                         const char*                             nameEnd         = std::find(nameStart, arg+argLen, '=');
170                         const bool                              hasImmValue     = nameEnd != (arg+argLen);
171                         const OptMap&                   optMap          = isLongName ? longOptMap : shortOptMap;
172                         OptMap::const_iterator  optPos          = optMap.find(string(nameStart, nameEnd));
173                         const OptInfo*                  opt                     = optPos != optMap.end() ? optPos->second : DE_NULL;
174
175                         if (!opt)
176                         {
177                                 err << "Unrecognized command line option '" << arg << "'\n";
178                                 allOk = false;
179                                 continue;
180                         }
181
182                         if (seenOpts.find(opt) != seenOpts.end())
183                         {
184                                 err << "Command line option '--" << opt->longName << "' specified multiple times\n";
185                                 allOk = false;
186                                 continue;
187                         }
188
189                         seenOpts.insert(opt);
190
191                         if (opt->isFlag)
192                         {
193                                 if (!hasImmValue)
194                                 {
195                                         opt->dispatchParse(opt, DE_NULL, &dst->m_options);
196                                 }
197                                 else
198                                 {
199                                         err << "No value expected for command line option '--" << opt->longName << "'\n";
200                                         allOk = false;
201                                 }
202                         }
203                         else
204                         {
205                                 const bool      hasValue        = hasImmValue || (argNdx+1 < numArgs);
206
207                                 if (hasValue)
208                                 {
209                                         const char*     value   = hasValue ? (hasImmValue ? nameEnd+1 : args[argNdx+1]) : DE_NULL;
210
211                                         if (!hasImmValue)
212                                                 argNdx += 1; // Skip value
213
214                                         try
215                                         {
216                                                 opt->dispatchParse(opt, value, &dst->m_options);
217                                         }
218                                         catch (const std::exception& e)
219                                         {
220                                                 err << "Got error parsing command line option '--" << opt->longName << "': " << e.what() << "\n";
221                                                 allOk = false;
222                                         }
223                                 }
224                                 else
225                                 {
226                                         err << "Expected value for command line option '--" << opt->longName << "'\n";
227                                         allOk = false;
228                                 }
229                         }
230                 }
231                 else
232                 {
233                         // Not an option
234                         dst->m_args.push_back(arg);
235                 }
236         }
237
238         // Help specified?
239         if (dst->m_options.get<Help>())
240                 allOk = false;
241
242         return allOk;
243 }
244
245 void Parser::help (std::ostream& str) const
246 {
247         for (vector<OptInfo>::const_iterator optIter = m_options.begin(); optIter != m_options.end(); ++optIter)
248         {
249                 const OptInfo& opt = *optIter;
250
251                 str << "  ";
252                 if (opt.shortName)
253                         str << "-" << opt.shortName;
254
255                 if (opt.shortName && opt.longName)
256                         str << ", ";
257
258                 if (opt.longName)
259                         str << "--" << opt.longName;
260
261                 if (opt.namedValues)
262                 {
263                         str << "=[";
264
265                         for (const void* curValue = opt.namedValues; curValue != opt.namedValuesEnd; curValue = (const void*)((deUintptr)curValue + opt.namedValueStride))
266                         {
267                                 if (curValue != opt.namedValues)
268                                         str << "|";
269                                 str << getNamedValueName(curValue);
270                         }
271
272                         str << "]";
273                 }
274                 else if (!opt.isFlag)
275                         str << "=<value>";
276
277                 str << "\n";
278
279                 if (opt.description)
280                         str << "    " << opt.description << "\n";
281
282                 if (opt.defaultValue)
283                         str << "    default: '" << opt.defaultValue << "'\n";
284
285                 str << "\n";
286         }
287 }
288
289 void CommandLine::clear (void)
290 {
291         m_options.clear();
292         m_args.clear();
293 }
294
295 const void* findNamedValueMatch (const char* src, const void* namedValues, const void* namedValuesEnd, size_t stride)
296 {
297         std::string srcStr(src);
298
299         for (const void* curValue = namedValues; curValue != namedValuesEnd; curValue = (const void*)((deUintptr)curValue + stride))
300         {
301                 if (srcStr == getNamedValueName(curValue))
302                         return curValue;
303         }
304
305         throw std::invalid_argument("unrecognized value '" + srcStr + "'");
306 }
307
308 } // detail
309
310 // Default / parsing functions
311
312 template<>
313 void getTypeDefault (bool* dst)
314 {
315         *dst = false;
316 }
317
318 template<>
319 void parseType<bool> (const char*, bool* dst)
320 {
321         *dst = true;
322 }
323
324 template<>
325 void parseType<std::string> (const char* src, std::string* dst)
326 {
327         *dst = src;
328 }
329
330 template<>
331 void parseType<int> (const char* src, int* dst)
332 {
333         std::istringstream str(src);
334         str >> *dst;
335         if (str.bad() || !str.eof())
336                 throw std::invalid_argument("invalid integer literal");
337 }
338
339 // Tests
340
341 DE_DECLARE_COMMAND_LINE_OPT(TestStringOpt,              std::string);
342 DE_DECLARE_COMMAND_LINE_OPT(TestStringDefOpt,   std::string);
343 DE_DECLARE_COMMAND_LINE_OPT(TestIntOpt,                 int);
344 DE_DECLARE_COMMAND_LINE_OPT(TestBoolOpt,                bool);
345 DE_DECLARE_COMMAND_LINE_OPT(TestNamedOpt,               deUint64);
346
347 void selfTest (void)
348 {
349         // Parsing with no options.
350         {
351                 Parser parser;
352
353                 {
354                         std::ostringstream      err;
355                         CommandLine                     cmdLine;
356                         const bool                      parseOk         = parser.parse(0, DE_NULL, &cmdLine, err);
357
358                         DE_TEST_ASSERT(parseOk && err.str().empty());
359                 }
360
361                 {
362                         const char*                     args[]          = { "-h" };
363                         std::ostringstream      err;
364                         CommandLine                     cmdLine;
365                         const bool                      parseOk         = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
366
367                         DE_TEST_ASSERT(!parseOk);
368                         DE_TEST_ASSERT(err.str().empty()); // No message about -h
369                 }
370
371                 {
372                         const char*                     args[]          = { "--help" };
373                         std::ostringstream      err;
374                         CommandLine                     cmdLine;
375                         const bool                      parseOk         = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
376
377                         DE_TEST_ASSERT(!parseOk);
378                         DE_TEST_ASSERT(err.str().empty()); // No message about -h
379                 }
380
381                 {
382                         const char*                     args[]          = { "foo", "bar", "baz baz" };
383                         std::ostringstream      err;
384                         CommandLine                     cmdLine;
385                         const bool                      parseOk         = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
386
387                         DE_TEST_ASSERT(parseOk && err.str().empty());
388                         DE_TEST_ASSERT(cmdLine.getArgs().size() == DE_LENGTH_OF_ARRAY(args));
389
390                         for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(args); ndx++)
391                                 DE_TEST_ASSERT(cmdLine.getArgs()[ndx] == args[ndx]);
392                 }
393         }
394
395         // Parsing with options.
396         {
397                 Parser parser;
398
399                 static const NamedValue<deUint64> s_namedValues[] =
400                 {
401                         { "zero",       0               },
402                         { "one",        1               },
403                         { "huge",       ~0ull   }
404                 };
405
406                 parser << Option<TestStringOpt>         ("s",   "string",       "String option")
407                            << Option<TestStringDefOpt>  ("x",   "xyz",          "String option w/ default value",       "foo")
408                            << Option<TestIntOpt>                ("i",   "int",          "Int option")
409                            << Option<TestBoolOpt>               ("b",   "bool",         "Test boolean flag")
410                            << Option<TestNamedOpt>              ("n",   "named",        "Test named opt",       DE_ARRAY_BEGIN(s_namedValues),  DE_ARRAY_END(s_namedValues),    "one");
411
412                 {
413                         std::ostringstream err;
414                         DE_TEST_ASSERT(err.str().empty());
415                         parser.help(err);
416                         DE_TEST_ASSERT(!err.str().empty());
417                 }
418
419                 // Default values
420                 {
421                         CommandLine                     cmdLine;
422                         std::ostringstream      err;
423                         bool                            parseOk = parser.parse(0, DE_NULL, &cmdLine, err);
424
425                         DE_TEST_ASSERT(parseOk);
426                         DE_TEST_ASSERT(err.str().empty());
427
428                         DE_TEST_ASSERT(!cmdLine.hasOption<TestStringOpt>());
429                         DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
430                         DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == 1);
431                         DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>() == false);
432                         DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
433                 }
434
435                 // Basic parsing
436                 {
437                         const char*                     args[]  = { "-s", "test value", "-b", "-i=9", "--named=huge" };
438                         CommandLine                     cmdLine;
439                         std::ostringstream      err;
440                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
441
442                         DE_TEST_ASSERT(parseOk);
443                         DE_TEST_ASSERT(err.str().empty());
444
445                         DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "test value");
446                         DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 9);
447                         DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
448                         DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == ~0ull);
449                         DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
450                 }
451
452                 // End of argument list (--)
453                 {
454                         const char*                     args[]  = { "--string=foo", "-b", "--", "--int=2", "-b" };
455                         CommandLine                     cmdLine;
456                         std::ostringstream      err;
457                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
458
459                         DE_TEST_ASSERT(parseOk);
460                         DE_TEST_ASSERT(err.str().empty());
461
462                         DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "foo");
463                         DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
464                         DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
465
466                         DE_TEST_ASSERT(cmdLine.getArgs().size() == 2);
467                         DE_TEST_ASSERT(cmdLine.getArgs()[0] == "--int=2");
468                         DE_TEST_ASSERT(cmdLine.getArgs()[1] == "-b");
469                 }
470
471                 // Value --
472                 {
473                         const char*                     args[]  = { "--string", "--", "-b", "foo" };
474                         CommandLine                     cmdLine;
475                         std::ostringstream      err;
476                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
477
478                         DE_TEST_ASSERT(parseOk);
479                         DE_TEST_ASSERT(err.str().empty());
480
481                         DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "--");
482                         DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
483                         DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
484
485                         DE_TEST_ASSERT(cmdLine.getArgs().size() == 1);
486                         DE_TEST_ASSERT(cmdLine.getArgs()[0] == "foo");
487                 }
488
489                 // Invalid flag usage
490                 {
491                         const char*                     args[]  = { "-b=true" };
492                         CommandLine                     cmdLine;
493                         std::ostringstream      err;
494                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
495
496                         DE_TEST_ASSERT(!parseOk);
497                         DE_TEST_ASSERT(!err.str().empty());
498                 }
499
500                 // Invalid named option
501                 {
502                         const char*                     args[]  = { "-n=two" };
503                         CommandLine                     cmdLine;
504                         std::ostringstream      err;
505                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
506
507                         DE_TEST_ASSERT(!parseOk);
508                         DE_TEST_ASSERT(!err.str().empty());
509                 }
510
511                 // Unrecognized option (-x)
512                 {
513                         const char*                     args[]  = { "-x" };
514                         CommandLine                     cmdLine;
515                         std::ostringstream      err;
516                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
517
518                         DE_TEST_ASSERT(!parseOk);
519                         DE_TEST_ASSERT(!err.str().empty());
520                 }
521
522                 // Unrecognized option (--xxx)
523                 {
524                         const char*                     args[]  = { "--xxx" };
525                         CommandLine                     cmdLine;
526                         std::ostringstream      err;
527                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
528
529                         DE_TEST_ASSERT(!parseOk);
530                         DE_TEST_ASSERT(!err.str().empty());
531                 }
532
533                 // Invalid int value
534                 {
535                         const char*                     args[]  = { "--int", "1x" };
536                         CommandLine                     cmdLine;
537                         std::ostringstream      err;
538                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
539
540                         DE_TEST_ASSERT(!parseOk);
541                         DE_TEST_ASSERT(!err.str().empty());
542                 }
543
544                 // Arg specified multiple times
545                 {
546                         const char*                     args[]  = { "-s=2", "-s=3" };
547                         CommandLine                     cmdLine;
548                         std::ostringstream      err;
549                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
550
551                         DE_TEST_ASSERT(!parseOk);
552                         DE_TEST_ASSERT(!err.str().empty());
553                 }
554
555                 // Missing value
556                 {
557                         const char*                     args[]  = { "--int" };
558                         CommandLine                     cmdLine;
559                         std::ostringstream      err;
560                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
561
562                         DE_TEST_ASSERT(!parseOk);
563                         DE_TEST_ASSERT(!err.str().empty());
564                 }
565
566                 // Empty value --arg=
567                 {
568                         const char*                     args[]  = { "--string=", "-b", "-x", "" };
569                         CommandLine                     cmdLine;
570                         std::ostringstream      err;
571                         bool                            parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
572
573                         DE_TEST_ASSERT(parseOk);
574                         DE_TEST_ASSERT(err.str().empty());
575                         DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "");
576                         DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "");
577                         DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
578                 }
579         }
580 }
581
582 } // cmdline
583 } // de