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