C++ Utilities  4.15.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
argumentparsertests.cpp
Go to the documentation of this file.
1 #include "./outputcheck.h"
2 #include "./testutils.h"
3 
4 #include "../conversion/stringbuilder.h"
5 #include "../conversion/stringconversion.h"
6 
7 #include "../application/argumentparser.h"
8 #include "../application/argumentparserprivate.h"
9 #include "../application/commandlineutils.h"
10 #include "../application/failure.h"
11 #include "../application/fakeqtconfigarguments.h"
12 
13 #include "../io/ansiescapecodes.h"
14 #include "../io/path.h"
15 
16 #include "resources/config.h"
17 
18 #include <cppunit/TestFixture.h>
19 #include <cppunit/extensions/HelperMacros.h>
20 
21 #include <cstdlib>
22 #include <cstring>
23 
24 using namespace std;
25 using namespace ApplicationUtilities;
26 using namespace ConversionUtilities;
27 using namespace TestUtilities;
28 using namespace TestUtilities::Literals;
29 
30 using namespace CPPUNIT_NS;
31 
35 class ArgumentParserTests : public TestFixture {
36  CPPUNIT_TEST_SUITE(ArgumentParserTests);
37  CPPUNIT_TEST(testArgument);
38  CPPUNIT_TEST(testParsing);
39  CPPUNIT_TEST(testCallbacks);
40  CPPUNIT_TEST(testBashCompletion);
41  CPPUNIT_TEST(testHelp);
42  CPPUNIT_TEST(testSetMainArguments);
43  CPPUNIT_TEST(testNoColorArgument);
44  CPPUNIT_TEST(testValueConversion);
45  CPPUNIT_TEST_SUITE_END();
46 
47 public:
48  void setUp();
49  void tearDown();
50 
51  void testArgument();
52  void testParsing();
53  void testCallbacks();
54  void testBashCompletion();
55  void testHelp();
56  void testSetMainArguments();
57  void testNoColorArgument();
58  void testValueConversion();
59 
60 private:
61  void callback();
62 };
63 
65 
67 {
68 }
69 
71 {
72 }
73 
78 {
79  Argument argument("test", 't', "some description");
80  CPPUNIT_ASSERT_EQUAL(argument.isRequired(), false);
81  argument.setConstraints(1, 10);
82  CPPUNIT_ASSERT_EQUAL(argument.isRequired(), true);
83  Argument subArg("sub", 's', "sub arg");
84  argument.addSubArgument(&subArg);
85  CPPUNIT_ASSERT_EQUAL(subArg.parents().at(0), &argument);
86  CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
87  CPPUNIT_ASSERT(!argument.firstValue());
88  argument.setEnvironmentVariable("FOO_ENV_VAR");
89  setenv("FOO_ENV_VAR", "foo", true);
90  CPPUNIT_ASSERT(!strcmp(argument.firstValue(), "foo"));
91  ArgumentOccurrence occurrence(0, vector<Argument *>(), nullptr);
92  occurrence.values.emplace_back("bar");
93  argument.m_occurrences.emplace_back(move(occurrence));
94  CPPUNIT_ASSERT(!strcmp(argument.firstValue(), "bar"));
95 }
96 
101 {
102  // setup parser with some test argument definitions
103  ArgumentParser parser;
105  QT_CONFIG_ARGUMENTS qtConfigArgs;
106  HelpArgument helpArg(parser);
107  Argument verboseArg("verbose", 'v', "be verbose");
108  verboseArg.setCombinable(true);
109  Argument fileArg("file", 'f', "specifies the path of the file to be opened");
110  fileArg.setValueNames({ "path" });
111  fileArg.setRequiredValueCount(1);
112  fileArg.setEnvironmentVariable("PATH");
113  Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
114  filesArg.setValueNames({ "path 1", "path 2" });
115  filesArg.setRequiredValueCount(Argument::varValueCount);
116  Argument outputFileArg("output-file", 'o', "specifies the path of the output file");
117  outputFileArg.setValueNames({ "path" });
118  outputFileArg.setRequiredValueCount(1);
119  outputFileArg.setRequired(true);
120  outputFileArg.setCombinable(true);
121  Argument printFieldNamesArg("print-field-names", '\0', "prints available field names");
122  Argument displayFileInfoArg("display-file-info", 'i', "displays general file information");
123  Argument notAlbumArg("album", 'a', "should not be confused with album value");
124  displayFileInfoArg.setDenotesOperation(true);
125  displayFileInfoArg.setSubArguments({ &fileArg, &verboseArg, &notAlbumArg });
126  Argument fieldsArg("fields", '\0', "specifies the fields");
127  fieldsArg.setRequiredValueCount(Argument::varValueCount);
128  fieldsArg.setValueNames({ "title", "album", "artist", "trackpos" });
129  fieldsArg.setImplicit(true);
130  Argument displayTagInfoArg("get", 'p', "displays the values of all specified tag fields (displays all fields if none specified)");
131  displayTagInfoArg.setDenotesOperation(true);
132  displayTagInfoArg.setSubArguments({ &fieldsArg, &filesArg, &verboseArg, &notAlbumArg });
133  NoColorArgument noColorArg;
134  parser.setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg, &noColorArg });
135 
136  // no args present
137  parser.parseArgs(0, nullptr);
138  CPPUNIT_ASSERT(!parser.executable());
139  CPPUNIT_ASSERT(!parser.specifiedOperation());
140  CPPUNIT_ASSERT_EQUAL(0u, parser.actualArgumentCount());
141 
142  // error about uncombinable arguments
143  const char *argv[] = { "tageditor", "get", "album", "title", "diskpos", "-f", "somefile" };
144  // try to parse, this should fail
145  try {
146  parser.parseArgs(7, argv);
147  CPPUNIT_FAIL("Exception expected.");
148  } catch (const Failure &e) {
149  CPPUNIT_ASSERT_EQUAL("The argument \"files\" can not be combined with \"fields\"."s, string(e.what()));
150  // test printing btw
151  stringstream ss;
152  ss << e;
153  CPPUNIT_ASSERT_EQUAL(
154  "Error: Unable to parse arguments: The argument \"files\" can not be combined with \"fields\".\nSee --help for available commands.\n"s,
155  ss.str());
156  }
157  CPPUNIT_ASSERT(parser.isUncombinableMainArgPresent());
158 
159  // arguments read correctly after successful parse
160  filesArg.setCombinable(true);
161  parser.resetArgs();
162  parser.parseArgs(7, argv);
163  // check results
164  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
165  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
166  CPPUNIT_ASSERT(!strcmp(parser.executable(), "tageditor"));
167  CPPUNIT_ASSERT(!verboseArg.isPresent());
168  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
169  CPPUNIT_ASSERT(fieldsArg.isPresent());
170  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album"));
171  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
172  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
173  CPPUNIT_ASSERT_THROW(displayTagInfoArg.values().at(3), out_of_range);
174  CPPUNIT_ASSERT_EQUAL(&displayTagInfoArg, parser.specifiedOperation());
175 
176  // skip empty args
177  const char *argv2[] = { "tageditor", "", "-p", "album", "title", "diskpos", "", "--files", "somefile" };
178  // reparse the args
179  parser.resetArgs();
180  parser.parseArgs(9, argv2);
181  // check results again
182  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
183  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
184  CPPUNIT_ASSERT(!verboseArg.isPresent());
185  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
186  CPPUNIT_ASSERT(fieldsArg.isPresent());
187  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album"));
188  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
189  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
190  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(3), ""));
191  CPPUNIT_ASSERT_THROW(fieldsArg.values().at(4), out_of_range);
192  CPPUNIT_ASSERT(filesArg.isPresent());
193  CPPUNIT_ASSERT(!strcmp(filesArg.values().at(0), "somefile"));
194 
195  // error about unknown argument: forget get/-p
196  const char *argv3[] = { "tageditor", "album", "title", "diskpos", "--files", "somefile" };
197  try {
198  parser.resetArgs();
199  parser.parseArgs(6, argv3);
200  CPPUNIT_FAIL("Exception expected.");
201  } catch (const Failure &e) {
202  CPPUNIT_ASSERT_EQUAL("The specified argument \"album\" is unknown.\nDid you mean get or --help?"s, string(e.what()));
203  }
204 
205  // error about unknown argument: mistake in final argument
206  const char *argv18[] = { "tageditor", "get", "album", "title", "diskpos", "--verbose", "--fi" };
207  try {
208  parser.resetArgs();
209  parser.parseArgs(7, argv18);
210  CPPUNIT_FAIL("Exception expected.");
211  } catch (const Failure &e) {
212  CPPUNIT_ASSERT_EQUAL("The specified argument \"--fi\" is unknown.\nDid you mean --files or --no-color?"s, string(e.what()));
213  }
214 
215  // warning about unknown argument
216  parser.setUnknownArgumentBehavior(UnknownArgumentBehavior::Warn);
217  // redirect stderr to check whether warnings are printed correctly
218  stringstream buffer;
219  streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
220  parser.resetArgs();
221  EscapeCodes::enabled = false;
222  try {
223  parser.parseArgs(6, argv3);
224  } catch (...) {
225  cerr.rdbuf(regularCerrBuffer);
226  throw;
227  }
228  cerr.rdbuf(regularCerrBuffer);
229  CPPUNIT_ASSERT_EQUAL("Warning: The specified argument \"album\" is unknown and will be ignored.\n"s
230  "Warning: The specified argument \"title\" is unknown and will be ignored.\n"s
231  "Warning: The specified argument \"diskpos\" is unknown and will be ignored.\n"s
232  "Warning: The specified argument \"--files\" is unknown and will be ignored.\n"s
233  "Warning: The specified argument \"somefile\" is unknown and will be ignored.\n"s,
234  buffer.str());
235  // none of the arguments should be present now
236  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
237  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
238  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
239  CPPUNIT_ASSERT(!fieldsArg.isPresent());
240  CPPUNIT_ASSERT(!filesArg.isPresent());
241 
242  // combined abbreviations like "-vf"
243  const char *argv4[] = { "tageditor", "-i", "-vf", "test" };
244  parser.setUnknownArgumentBehavior(UnknownArgumentBehavior::Fail);
245  parser.resetArgs();
246  parser.parseArgs(4, argv4);
247  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
248  CPPUNIT_ASSERT(displayFileInfoArg.isPresent());
249  CPPUNIT_ASSERT(verboseArg.isPresent());
250  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
251  CPPUNIT_ASSERT(!filesArg.isPresent());
252  CPPUNIT_ASSERT(fileArg.isPresent());
253  CPPUNIT_ASSERT(!strcmp(fileArg.values().at(0), "test"));
254  CPPUNIT_ASSERT_THROW(fileArg.values().at(1), out_of_range);
255 
256  // constraint checking: no multiple occurrences (not resetting verboseArg on purpose)
257  displayFileInfoArg.reset(), fileArg.reset();
258  try {
259  parser.parseArgs(4, argv4);
260  CPPUNIT_FAIL("Exception expected.");
261  } catch (const Failure &e) {
262  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
263  CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" mustn't be specified more than 1 time."));
264  }
265 
266  // constraint checking: no contraint (not resetting verboseArg on purpose)
267  displayFileInfoArg.reset(), fileArg.reset();
268  verboseArg.setConstraints(0, Argument::varValueCount);
269  parser.parseArgs(4, argv4);
270  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
271 
272  // constraint checking: mandatory argument
273  verboseArg.setRequired(true);
274  parser.resetArgs();
275  parser.parseArgs(4, argv4);
276  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
277 
278  // contraint checking: error about missing mandatory argument
279  const char *argv5[] = { "tageditor", "-i", "-f", "test" };
280  displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
281  try {
282  parser.parseArgs(4, argv5);
283  CPPUNIT_FAIL("Exception expected.");
284  } catch (const Failure &e) {
285  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
286  CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time."));
287  }
288  verboseArg.setRequired(false);
289 
290  // combined abbreviation with nesting "-pf"
291  const char *argv10[] = { "tageditor", "-pf", "test" };
292  parser.resetArgs();
293  parser.parseArgs(3, argv10);
294  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
295  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
296  CPPUNIT_ASSERT(!fileArg.isPresent());
297  CPPUNIT_ASSERT(filesArg.isPresent());
298  CPPUNIT_ASSERT_EQUAL(1_st, filesArg.values(0).size());
299  CPPUNIT_ASSERT(!strcmp(filesArg.values(0).front(), "test"));
300  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
301 
302  // constraint checking: no complains about missing -i
303  const char *argv6[] = { "tageditor", "-g" };
304  parser.resetArgs();
305  parser.parseArgs(2, argv6);
306  CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
307 
308  // constraint checking: dependend arguments (-f requires -i or -p)
309  const char *argv7[] = { "tageditor", "-f", "test" };
310  parser.resetArgs();
311  try {
312  parser.parseArgs(3, argv7);
313  CPPUNIT_FAIL("Exception expected.");
314  } catch (const Failure &e) {
315  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
316  CPPUNIT_ASSERT_EQUAL("The specified argument \"-f\" is unknown.\nDid you mean get or --help?"s, string(e.what()));
317  }
318 
319  // equation sign syntax
320  const char *argv11[] = { "tageditor", "-if=test-v" };
321  parser.resetArgs();
322  parser.parseArgs(2, argv11);
323  CPPUNIT_ASSERT(!filesArg.isPresent());
324  CPPUNIT_ASSERT(fileArg.isPresent());
325  CPPUNIT_ASSERT(!verboseArg.isPresent());
326  CPPUNIT_ASSERT_EQUAL(1_st, fileArg.values(0).size());
327  CPPUNIT_ASSERT_EQUAL("test-v"s, string(fileArg.values(0).front()));
328  const char *argv15[] = { "tageditor", "-i", "--file=test", "-v" };
329  parser.resetArgs();
330  parser.parseArgs(4, argv15);
331  CPPUNIT_ASSERT(!filesArg.isPresent());
332  CPPUNIT_ASSERT(fileArg.isPresent());
333  CPPUNIT_ASSERT(verboseArg.isPresent());
334  CPPUNIT_ASSERT_EQUAL(1_st, fileArg.values(0).size());
335  CPPUNIT_ASSERT_EQUAL("test"s, string(fileArg.values(0).front()));
336 
337  // specifying value directly after abbreviation
338  const char *argv12[] = { "tageditor", "-iftest" };
339  parser.resetArgs();
340  parser.parseArgs(2, argv12);
341  CPPUNIT_ASSERT(!filesArg.isPresent());
342  CPPUNIT_ASSERT(fileArg.isPresent());
343  CPPUNIT_ASSERT_EQUAL(1_st, fileArg.values(0).size());
344  CPPUNIT_ASSERT(!strcmp(fileArg.values(0).front(), "test"));
345 
346  // specifying top-level argument after abbreviation
347  const char *argv17[] = { "tageditor", "-if=test-v", "--no-color" };
348  parser.resetArgs();
349  parser.parseArgs(3, argv17);
350  CPPUNIT_ASSERT(!filesArg.isPresent());
351  CPPUNIT_ASSERT(fileArg.isPresent());
352  CPPUNIT_ASSERT(!verboseArg.isPresent());
353  CPPUNIT_ASSERT(noColorArg.isPresent());
354  CPPUNIT_ASSERT_EQUAL(1_st, fileArg.values(0).size());
355  CPPUNIT_ASSERT_EQUAL("test-v"s, string(fileArg.values(0).front()));
356 
357  // default argument
358  const char *argv8[] = { "tageditor" };
359  parser.resetArgs();
360  parser.parseArgs(1, argv8);
361  CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
362  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
363  CPPUNIT_ASSERT(!verboseArg.isPresent());
364  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
365  CPPUNIT_ASSERT(!filesArg.isPresent());
366  CPPUNIT_ASSERT(!fileArg.isPresent());
367  if (getenv("PATH")) {
368  CPPUNIT_ASSERT(fileArg.firstValue());
369  CPPUNIT_ASSERT(!strcmp(fileArg.firstValue(), getenv("PATH")));
370  } else {
371  CPPUNIT_ASSERT(!fileArg.firstValue());
372  }
373 
374  // constraint checking: required value count with sufficient number of provided parameters
375  const char *argv13[] = { "tageditor", "get", "--fields", "album=test", "title", "diskpos", "--files", "somefile" };
376  verboseArg.setRequired(false);
377  parser.resetArgs();
378  parser.parseArgs(8, argv13);
379  // this should still work without complaints
380  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
381  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
382  CPPUNIT_ASSERT(!verboseArg.isPresent());
383  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
384  CPPUNIT_ASSERT(fieldsArg.isPresent());
385  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
386  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
387  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
388  CPPUNIT_ASSERT_THROW(fieldsArg.values().at(3), out_of_range);
389  CPPUNIT_ASSERT(filesArg.isPresent());
390  CPPUNIT_ASSERT(!strcmp(filesArg.values().at(0), "somefile"));
391  CPPUNIT_ASSERT(!notAlbumArg.isPresent());
392 
393  // constraint checking: required value count with insufficient number of provided parameters
394  const char *argv9[] = { "tageditor", "-p", "album", "title", "diskpos" };
395  fieldsArg.setRequiredValueCount(4);
396  parser.resetArgs();
397  try {
398  parser.parseArgs(5, argv9);
399  CPPUNIT_FAIL("Exception expected.");
400  } catch (const Failure &e) {
401  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
402  CPPUNIT_ASSERT_EQUAL(
403  "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"s,
404  string(e.what()));
405  }
406 
407  // constraint checking: truncated argument not wrongly detected
408  const char *argv16[] = { "tageditor", "--hel", "-p", "album", "title", "diskpos" };
409  fieldsArg.setRequiredValueCount(Argument::varValueCount);
410  parser.resetArgs();
411  try {
412  parser.parseArgs(6, argv16);
413  CPPUNIT_FAIL("Exception expected.");
414  } catch (const Failure &e) {
415  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
416  CPPUNIT_ASSERT_EQUAL("The specified argument \"--hel\" is unknown.\nDid you mean --help or get?"s, string(e.what()));
417  }
418 
419  // nested operations
420  const char *argv14[] = { "tageditor", "get", "fields", "album=test", "-f", "somefile" };
421  parser.resetArgs();
422  fieldsArg.setDenotesOperation(true);
423  parser.parseArgs(6, argv14);
424  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
425  CPPUNIT_ASSERT(fieldsArg.isPresent());
426  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
427 
428  // implicit flag still works when argument doesn't denote operation
429  parser.resetArgs();
430  fieldsArg.setDenotesOperation(false);
431  parser.parseArgs(6, argv14);
432  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
433  CPPUNIT_ASSERT(fieldsArg.isPresent());
434  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "fields"));
435  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "album=test"));
436 }
437 
442 {
443  ArgumentParser parser;
444  Argument callbackArg("with-callback", 't', "callback test");
445  callbackArg.setRequiredValueCount(2);
446  callbackArg.setCallback([](const ArgumentOccurrence &occurrence) {
447  CPPUNIT_ASSERT_EQUAL(0_st, occurrence.index);
448  CPPUNIT_ASSERT(occurrence.path.empty());
449  CPPUNIT_ASSERT_EQUAL(2_st, occurrence.values.size());
450  CPPUNIT_ASSERT(!strcmp(occurrence.values[0], "val1"));
451  CPPUNIT_ASSERT(!strcmp(occurrence.values[1], "val2"));
452  throw 42;
453  });
454  Argument noCallbackArg("no-callback", 'l', "callback test");
455  noCallbackArg.setRequiredValueCount(2);
456  parser.setMainArguments({ &callbackArg, &noCallbackArg });
457 
458  // test whether callback is invoked when argument with callback is specified
459  const char *argv[] = { "test", "-t", "val1", "val2" };
460  try {
461  parser.parseArgs(4, argv);
462  } catch (int i) {
463  CPPUNIT_ASSERT_EQUAL(i, 42);
464  }
465 
466  // test whether callback is not invoked when argument with callback is not specified
467  callbackArg.reset();
468  const char *argv2[] = { "test", "-l", "val1", "val2" };
469  parser.parseArgs(4, argv2);
470 }
471 
475 static bool exitCalled = false;
476 
482 {
483  ArgumentParser parser;
484  HelpArgument helpArg(parser);
485  Argument verboseArg("verbose", 'v', "be verbose");
486  verboseArg.setCombinable(true);
487  Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
488  filesArg.setRequiredValueCount(Argument::varValueCount);
489  filesArg.setCombinable(true);
490  Argument nestedSubArg("nested-sub", '\0', "nested sub arg");
491  Argument subArg("sub", '\0', "sub arg");
492  subArg.setSubArguments({ &nestedSubArg });
493  Argument displayFileInfoArg("display-file-info", 'i', "displays general file information");
494  displayFileInfoArg.setDenotesOperation(true);
495  displayFileInfoArg.setSubArguments({ &filesArg, &verboseArg, &subArg });
496  Argument fieldsArg("fields", '\0', "specifies the fields");
497  fieldsArg.setRequiredValueCount(Argument::varValueCount);
498  fieldsArg.setPreDefinedCompletionValues("title album artist trackpos");
499  fieldsArg.setImplicit(true);
500  Argument valuesArg("values", '\0', "specifies the fields");
501  valuesArg.setRequiredValueCount(Argument::varValueCount);
502  valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
503  valuesArg.setImplicit(false);
504  valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
505  Argument selectorsArg("selectors", '\0', "has some more pre-defined values");
506  selectorsArg.setRequiredValueCount(Argument::varValueCount);
507  selectorsArg.setPreDefinedCompletionValues("tag=id3v1 tag=id3v2 tag=matroska target=file target=track");
508  selectorsArg.setImplicit(false);
509  selectorsArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues);
510  selectorsArg.setCallback(
511  [&selectorsArg](const ArgumentOccurrence &) { selectorsArg.setPreDefinedCompletionValues("tag=matroska tag=mp4 tag=vorbis"); });
512  Argument getArg("get", 'g', "gets tag values");
513  getArg.setSubArguments({ &fieldsArg, &filesArg });
514  Argument setArg("set", 's', "sets tag values");
515  setArg.setSubArguments({ &valuesArg, &filesArg, &selectorsArg });
516 
517  parser.setMainArguments({ &helpArg, &displayFileInfoArg, &getArg, &setArg });
518 
519  // fail due to operation flags not set
520  const char *const argv1[] = { "se" };
521  ArgumentReader reader(parser, argv1, argv1 + 1, true);
522  {
523  const OutputCheck c("COMPREPLY=()\n");
524  CPPUNIT_ASSERT(reader.read());
525  parser.printBashCompletion(1, argv1, 0, reader);
526  }
527 
528  // correct operation arg flags
529  getArg.setDenotesOperation(true);
530  setArg.setDenotesOperation(true);
531  {
532  const OutputCheck c("COMPREPLY=('set' )\n");
533  reader.reset(argv1, argv1 + 1).read();
534  parser.printBashCompletion(1, argv1, 0, reader);
535  }
536 
537  // argument at current cursor position already specified -> the completion should just return the argument
538  const char *const argv2[] = { "set" };
539  parser.resetArgs();
540  {
541  const OutputCheck c("COMPREPLY=('set' )\n");
542  reader.reset(argv2, argv2 + 1).read();
543  parser.printBashCompletion(1, argv2, 0, reader);
544  }
545 
546  // advance the cursor position -> the completion should propose the next argument
547  parser.resetArgs();
548  {
549  const OutputCheck c("COMPREPLY=('--files' '--selectors' '--values' )\n");
550  reader.reset(argv2, argv2 + 1).read();
551  parser.printBashCompletion(1, argv2, 1, reader);
552  }
553 
554  // nested operations should be proposed as operations
555  parser.resetArgs();
556  filesArg.setDenotesOperation(true);
557  {
558  const OutputCheck c("COMPREPLY=('files' '--selectors' '--values' )\n");
559  reader.reset(argv2, argv2 + 1).read();
560  parser.printBashCompletion(1, argv2, 1, reader);
561  }
562 
563  // specifying no args should propose all main arguments
564  parser.resetArgs();
565  filesArg.setDenotesOperation(false);
566  {
567  const OutputCheck c("COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
568  reader.reset(nullptr, nullptr).read();
569  parser.printBashCompletion(0, nullptr, 0, reader);
570  }
571 
572  // pre-defined values
573  const char *const argv3[] = { "get", "--fields" };
574  parser.resetArgs();
575  {
576  const OutputCheck c("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n");
577  reader.reset(argv3, argv3 + 2).read();
578  parser.printBashCompletion(2, argv3, 2, reader);
579  }
580 
581  // pre-defined values with equation sign, one letter already present
582  const char *const argv4[] = { "set", "--values", "a" };
583  parser.resetArgs();
584  {
585  const OutputCheck c("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n");
586  reader.reset(argv4, argv4 + 3).read();
587  parser.printBashCompletion(3, argv4, 2, reader);
588  }
589 
590  // pre-defined values containing equation sign, equation sign already present
591  const char *const argv12[] = { "set", "--selectors", "tag=id3" };
592  parser.resetArgs();
593  {
594  const OutputCheck c("COMPREPLY=('tag=id3v1' 'tag=id3v2' )\n");
595  reader.reset(argv12, argv12 + 3).read();
596  parser.printBashCompletion(3, argv12, 2, reader);
597  }
598 
599  // recombining pre-defined values containing equation sign, equation sign already present
600  const char *const argv13[] = { "set", "--selectors", "tag", "=", "id3" };
601  parser.resetArgs();
602  {
603  const OutputCheck c("COMPREPLY=('id3v1' 'id3v2' )\n");
604  reader.reset(argv13, argv13 + 5).read();
605  parser.printBashCompletion(5, argv13, 4, reader);
606  }
607  parser.resetArgs();
608  {
609  const OutputCheck c("COMPREPLY=('id3v1' 'id3v2' 'matroska' )\n");
610  reader.reset(argv13, argv13 + 5).read();
611  parser.printBashCompletion(5, argv13, 3, reader);
612  }
613 
614  // computing pre-defined values just in time using callback
615  selectorsArg.setValueCompletionBehavior(selectorsArg.valueCompletionBehaviour() | ValueCompletionBehavior::InvokeCallback);
616  parser.resetArgs();
617  {
618  const OutputCheck c("COMPREPLY=('matroska' 'mp4' 'vorbis' )\n");
619  reader.reset(argv13, argv13 + 5).read();
620  parser.printBashCompletion(5, argv13, 3, reader);
621  }
622 
623  // pre-defined values for implicit argument
624  parser.resetArgs();
625  {
626  const OutputCheck c("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' )\n");
627  reader.reset(argv3, argv3 + 1).read();
628  parser.printBashCompletion(1, argv3, 2, reader);
629  }
630 
631  // file names
632  string iniFilePath = TestUtilities::testFilePath("test.ini");
633  iniFilePath.resize(iniFilePath.size() - 4);
634  string mkvFilePath = TestUtilities::testFilePath("test 'with quote'.mkv");
635  mkvFilePath.resize(mkvFilePath.size() - 17);
636  parser.resetArgs();
637  const char *const argv5[] = { "get", "--files", iniFilePath.c_str() };
638  {
639  // order for file names is not specified
640  const OutputCheck c("COMPREPLY=('" % mkvFilePath % " '\"'\"'with quote'\"'\"'.mkv' '" % iniFilePath + ".ini' ); compopt -o filenames\n",
641  "COMPREPLY=('" % iniFilePath % ".ini' '" % mkvFilePath + " '\"'\"'with quote'\"'\"'.mkv' ); compopt -o filenames\n");
642  reader.reset(argv5, argv5 + 3).read();
643  parser.printBashCompletion(3, argv5, 2, reader);
644  }
645 
646  // sub arguments
647  const char *const argv6[] = { "set", "--" };
648  parser.resetArgs();
649  {
650  const OutputCheck c("COMPREPLY=('--files' '--selectors' '--values' )\n");
651  reader.reset(argv6, argv6 + 2).read();
652  parser.printBashCompletion(2, argv6, 1, reader);
653  }
654 
655  // nested sub arguments
656  const char *const argv7[] = { "-i", "--sub", "--" };
657  parser.resetArgs();
658  {
659  const OutputCheck c("COMPREPLY=('--files' '--nested-sub' '--verbose' )\n");
660  reader.reset(argv7, argv7 + 3).read();
661  parser.printBashCompletion(3, argv7, 2, reader);
662  }
663 
664  // started pre-defined values with equation sign, one letter already present, last value matches
665  const char *const argv8[] = { "set", "--values", "t" };
666  parser.resetArgs();
667  {
668  const OutputCheck c("COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace\n");
669  reader.reset(argv8, argv8 + 3).read();
670  parser.printBashCompletion(3, argv8, 2, reader);
671  }
672 
673  // combined abbreviations
674  const char *const argv9[] = { "-gf" };
675  parser.resetArgs();
676  {
677  const OutputCheck c("COMPREPLY=('-gf' )\n");
678  reader.reset(argv9, argv9 + 1).read();
679  parser.printBashCompletion(1, argv9, 0, reader);
680  }
681  parser.resetArgs();
682  {
683  const OutputCheck c([](const string &actualOutput) { CPPUNIT_ASSERT_EQUAL(0_st, actualOutput.find("COMPREPLY=('--fields' ")); });
684  reader.reset(argv9, argv9 + 1).read();
685  parser.printBashCompletion(1, argv9, 1, reader);
686  }
687 
688  // override exit function to prevent readArgs() from terminating the test run
689  exitFunction = [](int) { exitCalled = true; };
690 
691  // call completion via readArgs() with current word index
692  const char *const argv10[] = { "/some/path/tageditor", "--bash-completion-for", "0" };
693  parser.resetArgs();
694  {
695  const OutputCheck c("COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
696  parser.readArgs(3, argv10);
697  }
698  CPPUNIT_ASSERT(!strcmp("/some/path/tageditor", parser.executable()));
699  CPPUNIT_ASSERT(exitCalled);
700 
701  // call completion via readArgs() without current word index
702  const char *const argv11[] = { "/some/path/tageditor", "--bash-completion-for", "ge" };
703  parser.resetArgs();
704  {
705  const OutputCheck c("COMPREPLY=('get' )\n");
706  parser.readArgs(3, argv11);
707  }
708 }
709 
714 {
715  // identation
716  Indentation indent;
717  indent = indent + 3;
718  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(4 + 3), indent.level);
719 
720  // setup parser
721  ArgumentParser parser;
722  HelpArgument helpArg(parser);
723  helpArg.setRequired(true);
724  OperationArgument verboseArg("verbose", 'v', "be verbose", "actually not an operation");
725  verboseArg.setCombinable(true);
726  ConfigValueArgument nestedSubArg("nested-sub", '\0', "nested sub arg", { "value1", "value2" });
727  nestedSubArg.setRequiredValueCount(Argument::varValueCount);
728  Argument subArg("foo", 'f', "dummy");
729  subArg.setName("sub");
730  subArg.setAbbreviation('\0');
731  subArg.setDescription("sub arg");
732  subArg.setExample("sub arg example");
733  subArg.setRequired(true);
734  subArg.addSubArgument(&nestedSubArg);
735  Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
736  filesArg.setCombinable(true);
737  filesArg.addSubArgument(&subArg);
738  filesArg.setSubArguments({ &subArg }); // test re-assignment btw
739  Argument envArg("env", '\0', "env");
740  envArg.setEnvironmentVariable("FILES");
741  envArg.setRequiredValueCount(2);
742  envArg.appendValueName("file");
743  parser.addMainArgument(&helpArg);
744  parser.addMainArgument(&verboseArg);
745  parser.addMainArgument(&filesArg);
746  parser.addMainArgument(&envArg);
747  dependencyVersions2 = { "somelib", "some other lib" };
748 
749  // parse args and assert output
750  const char *const argv[] = { "app", "-h" };
751  {
752  const OutputCheck c("\e[1m" APP_NAME ", version " APP_VERSION "\n"
753  "\e[0mLinked against: somelib, some other lib\n"
754  "\n\e[0m"
755  "Available operations:\n"
756  "\e[1mverbose, -v\e[0m\n"
757  " be verbose\n"
758  " example: actually not an operation\n"
759  "\n"
760  "Available top-level options:\n"
761  "\e[1m--files, -f\e[0m\n"
762  " specifies the path of the file(s) to be opened\n"
763  " \e[1m--sub\e[0m\n"
764  " sub arg\n"
765  " particularities: mandatory if parent argument is present\n"
766  " \e[1m--nested-sub\e[0m [value1] [value2] ...\n"
767  " nested sub arg\n"
768  " example: sub arg example\n"
769  "\n"
770  "\e[1m--env\e[0m [file] [value 2]\n"
771  " env\n"
772  " default environment variable: FILES\n"
773  "\n"
774  "Project website: " APP_URL "\n");
775  EscapeCodes::enabled = true;
776  parser.parseArgs(2, argv);
777  }
778 
779  verboseArg.setDenotesOperation(false);
780  {
781  const OutputCheck c(APP_NAME ", version " APP_VERSION "\n"
782  "Linked against: somelib, some other lib\n"
783  "\n"
784  "Available arguments:\n"
785  "--verbose, -v\n"
786  " be verbose\n"
787  " example: actually not an operation\n"
788  "\n"
789  "--files, -f\n"
790  " specifies the path of the file(s) to be opened\n"
791  " --sub\n"
792  " sub arg\n"
793  " particularities: mandatory if parent argument is present\n"
794  " --nested-sub [value1] [value2] ...\n"
795  " nested sub arg\n"
796  " example: sub arg example\n"
797  "\n"
798  "--env [file] [value 2]\n"
799  " env\n"
800  " default environment variable: FILES\n"
801  "\n"
802  "Project website: " APP_URL "\n");
803  EscapeCodes::enabled = false;
804  parser.resetArgs();
805  parser.parseArgs(2, argv);
806  }
807 }
808 
813 {
814  ArgumentParser parser;
815  HelpArgument helpArg(parser);
816  Argument subArg("sub-arg", 's', "mandatory sub arg");
817  subArg.setRequired(true);
818  helpArg.addSubArgument(&subArg);
819  parser.addMainArgument(&helpArg);
820  parser.setMainArguments({});
821  CPPUNIT_ASSERT_MESSAGE("clear main args", parser.mainArguments().empty());
822  parser.setMainArguments({ &helpArg });
823  CPPUNIT_ASSERT_MESSAGE("no default due to required sub arg", !parser.defaultArgument());
824  subArg.setConstraints(0, 20);
825  parser.setMainArguments({ &helpArg });
826  CPPUNIT_ASSERT_MESSAGE("default if no required sub arg", &helpArg == parser.defaultArgument());
827 }
828 
833 {
834  // assume escape codes are enabled by default
835  EscapeCodes::enabled = true;
836 
837  // ensure the initialization is not skipped
838  NoColorArgument::s_instance = nullptr;
839 
840  {
841  unsetenv("ENABLE_ESCAPE_CODES");
842  NoColorArgument noColorArg;
843  noColorArg.apply();
844  CPPUNIT_ASSERT_MESSAGE("default used if not present", EscapeCodes::enabled);
845  noColorArg.m_occurrences.emplace_back(0);
846  noColorArg.apply();
847  CPPUNIT_ASSERT_MESSAGE("default negated if present", !EscapeCodes::enabled);
848  const NoColorArgument secondInstance;
849  CPPUNIT_ASSERT_EQUAL_MESSAGE("s_instance not altered by 2nd instance", &noColorArg, NoColorArgument::s_instance);
850  }
851  {
852  setenv("ENABLE_ESCAPE_CODES", " 0 ", 1);
853  const NoColorArgument noColorArg;
854  CPPUNIT_ASSERT(!EscapeCodes::enabled);
855  }
856  {
857  setenv("ENABLE_ESCAPE_CODES", " 1 ", 1);
858  const NoColorArgument noColorArg;
859  CPPUNIT_ASSERT(EscapeCodes::enabled);
860  }
861 }
862 
863 template <typename ValueTuple> void checkConvertedValues(const std::string &message, const ValueTuple &values)
864 {
865  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, "foo"s, get<0>(values));
866  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, 42u, get<1>(values));
867  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, 7.5, get<2>(values));
868  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, -42, get<3>(values));
869 }
870 
875 {
876  // convert values directly from ArgumentOccurrence
877  ArgumentOccurrence occurrence(0);
878  occurrence.values = { "foo", "42", "7.5", "-42" };
879  checkConvertedValues("values from ArgumentOccurrence::convertValues", occurrence.convertValues<string, unsigned int, double, int>());
880  static_assert(std::is_same<std::tuple<>, decltype(occurrence.convertValues<>())>::value, "specifying no types yields empty tuple");
881 
882  // convert values via Argument's API
883  Argument arg("test", '\0');
884  arg.m_occurrences = { occurrence, occurrence };
885  checkConvertedValues("values from Argument::convertValues", arg.valuesAs<string, unsigned int, double, int>());
886  checkConvertedValues("values from Argument::convertValues(1)", arg.valuesAs<string, unsigned int, double, int>(1));
887  const auto allValues = arg.allValuesAs<string, unsigned int, double, int>();
888  CPPUNIT_ASSERT_EQUAL(2_st, allValues.size());
889  for (const auto &values : allValues) {
890  checkConvertedValues("values from Argument::convertAllValues", values);
891  }
892  static_assert(std::is_same<std::tuple<>, decltype(arg.valuesAs<>())>::value, "specifying no types yields empty tuple");
893 
894  // error handling
895  try {
896  occurrence.convertValues<string, unsigned int, double, int, int>();
897  CPPUNIT_FAIL("Expected exception");
898  } catch (const Failure &failure) {
899  CPPUNIT_ASSERT_EQUAL("Expected 5 top-level values to be present but only 4 have been specified."s, string(failure.what()));
900  }
901  try {
902  occurrence.convertValues<int>();
903  CPPUNIT_FAIL("Expected exception");
904  } catch (const Failure &failure) {
905  CPPUNIT_ASSERT_EQUAL(
906  "Conversion of top-level value \"foo\" to type \"i\" failed: The character \"f\" is no valid digit."s, string(failure.what()));
907  }
908  occurrence.path = { &arg };
909  try {
910  occurrence.convertValues<string, unsigned int, double, int, int>();
911  CPPUNIT_FAIL("Expected exception");
912  } catch (const Failure &failure) {
913  CPPUNIT_ASSERT_EQUAL("Expected 5 values for argument --test to be present but only 4 have been specified."s, string(failure.what()));
914  }
915  try {
916  occurrence.convertValues<int>();
917  CPPUNIT_FAIL("Expected exception");
918  } catch (const Failure &failure) {
919  CPPUNIT_ASSERT_EQUAL("Conversion of value \"foo\" (for argument --test) to type \"i\" failed: The character \"f\" is no valid digit."s,
920  string(failure.what()));
921  }
922 }
void testNoColorArgument()
Tests whether NocolorArgument toggles escape codes correctly.
void testBashCompletion()
Tests bash completion.
std::tuple< RemainingTargetTypes... > convertValues() const
Converts the present values to the specified target types.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
#define QT_CONFIG_ARGUMENTS
The ConfigValueArgument class is an Argument where setCombinable() is true by default.
Argument * defaultArgument() const
Returns the default argument.
void setImplicit(bool value)
Sets whether the argument is an implicit argument.
std::size_t index
The index of the occurrence.
void testParsing()
Tests parsing command line arguments.
static void apply()
Sets EscapeCodes::enabled according to the presense of the first instantiation of NoColorArgument...
void checkConvertedValues(const std::string &message, const ValueTuple &values)
void setCombinable(bool value)
Sets whether this argument can be combined.
ValueCompletionBehavior valueCompletionBehaviour() const
Returns the items to be considered when generating completion for the values.
void setUnknownArgumentBehavior(UnknownArgumentBehavior behavior)
Sets how unknown arguments are treated.
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
virtual const char * what() const USE_NOTHROW
Returns a C-style character string describing the cause of the Failure.
Definition: failure.cpp:44
Contains currently only ArgumentParser and related classes.
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
STL namespace.
void testArgument()
Tests the behaviour of the argument class.
void testSetMainArguments()
Tests some corner cases in setMainArguments() which are not already checked in the other tests...
bool isRequired() const
Returns an indication whether the argument is mandatory.
void testCallbacks()
Tests whether callbacks are called correctly.
void setRequired(bool required)
Sets whether this argument is mandatory or not.
Contains literals to ease asserting with CPPUNIT_ASSERT_EQUAL.
Definition: testutils.h:251
void parseArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
The Indentation class allows printing indentation conveniently, eg.
The NoColorArgument class allows to specify whether use of escape codes or similar technique to provi...
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
The ArgumentParserTests class tests the ArgumentParser and Argument classes.
The OperationArgument class is an Argument where denotesOperation() is true by default.
void setConstraints(std::size_t minOccurrences, std::size_t maxOccurrences)
Sets the allowed number of occurrences.
void testValueConversion()
Tests value conversion provided by Argument and ArgumentOccurrence.
void setAbbreviation(char abbreviation)
Sets the abbreviation of the argument.
Contains classes and functions utilizing creating of test applications.
Definition: testutils.h:12
void setDescription(const char *description)
Sets the description of the argument.
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes...
constexpr int i
Definition: traitstests.cpp:97
void setValueCompletionBehavior(ValueCompletionBehavior valueCompletionBehaviour)
Sets the items to be considered when generating completion for the values.
#define SET_APPLICATION_INFO
Sets application meta data (including SET_DEPENDENCY_INFO) used by ArgumentParser::printHelp().
void appendValueName(const char *valueName)
Appends a value name.
const std::vector< const char * > & values(std::size_t occurrence=0) const
Returns the parameter values for the specified occurrence of argument.
Contains several functions providing conversions between different data types.
CPP_UTILITIES_EXPORT void(* exitFunction)(int)
Specifies a function quit the application.
const char * executable() const
Returns the name of the current executable.
The Argument class is a wrapper for command line argument information.
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
void setExample(const char *example)
Sets the a usage example for the argument.
void setPreDefinedCompletionValues(const char *preDefinedCompletionValues)
Assignes the values to be used when generating completion for the values.
ApplicationUtilities::ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).
The StandardOutputCheck class asserts whether the (standard) output written in the enclosing code blo...
Definition: outputcheck.h:19
bool read()
Reads the commands line arguments specified when constructing the object.
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
void setEnvironmentVariable(const char *environmentVariable)
Sets the environment variable queried when firstValue() is called.
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
The HelpArgument class prints help information for an argument parser when present (–help...
CPPUNIT_TEST_SUITE_REGISTRATION(ArgumentParserTests)
The Failure class is thrown by an ArgumentParser when a parsing error occurs.
Definition: failure.h:12
CPP_UTILITIES_EXPORT std::vector< const char * > dependencyVersions2
Specifies the dependency versions the application was linked against (used by ArgumentParser::printHe...
const ArgumentVector & mainArguments() const
Returns the main arguments.
void setDenotesOperation(bool denotesOperation)
Sets whether the argument denotes the operation.
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
void reset()
Resets occurrences (indices, values and paths).
void setRequiredValueCount(std::size_t requiredValueCount)
Sets the number of values which are required to be given for this argument.
The ArgumentReader class internally encapsulates the process of reading command line arguments...
std::tuple< TargetType... > valuesAs(std::size_t occurrence=0) const
Converts the present values for the specified occurrence to the specified target types.
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
unsigned int actualArgumentCount() const
Returns the actual number of arguments that could be found when parsing.
void setName(const char *name)
Sets the name of the argument.
std::vector< std::tuple< TargetType... > > allValuesAs() const
Converts the present values for all occurrence to the specified target types.
CPP_UTILITIES_EXPORT std::string testFilePath(const std::string &name)
Convenience function which returns the full path of the test file with the specified name...
Definition: testutils.h:105
The ArgumentParser class provides a means for handling command line arguments.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
void testHelp()
Tests –help output.
void setValueNames(std::initializer_list< const char *> valueNames)
Sets the names of the requried values.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.