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