C++ Utilities  4.6.1
Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities
argumentparsertests.cpp
Go to the documentation of this file.
1 #include "./testutils.h"
2 
3 #include "../conversion/stringbuilder.h"
4 
5 #include "../application/argumentparser.h"
6 #include "../application/argumentparserprivate.h"
7 #include "../application/failure.h"
8 #include "../application/fakeqtconfigarguments.h"
9 
10 #include "../io/path.h"
11 
12 #include "resources/config.h"
13 
14 #include <cppunit/extensions/HelperMacros.h>
15 #include <cppunit/TestFixture.h>
16 
17 #include <cstring>
18 #include <cstdlib>
19 
20 using namespace std;
21 using namespace ApplicationUtilities;
22 using namespace ConversionUtilities;
23 
24 using namespace CPPUNIT_NS;
25 
29 class ArgumentParserTests : public TestFixture
30 {
31  CPPUNIT_TEST_SUITE(ArgumentParserTests);
32  CPPUNIT_TEST(testArgument);
33  CPPUNIT_TEST(testParsing);
34  CPPUNIT_TEST(testCallbacks);
35  CPPUNIT_TEST(testBashCompletion);
36  CPPUNIT_TEST_SUITE_END();
37 
38 public:
39  void setUp();
40  void tearDown();
41 
42  void testArgument();
43  void testParsing();
44  void testCallbacks();
45  void testBashCompletion();
46 
47 private:
48  void callback();
49 };
50 
52 
54 {}
55 
57 {}
58 
63 {
64  Argument argument("test", 't', "some description");
65  CPPUNIT_ASSERT_EQUAL(argument.isRequired(), false);
66  argument.setConstraints(1, 10);
67  CPPUNIT_ASSERT_EQUAL(argument.isRequired(), true);
68  Argument subArg("sub", 's', "sub arg");
69  argument.addSubArgument(&subArg);
70  CPPUNIT_ASSERT_EQUAL(subArg.parents().at(0), &argument);
71  CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
72  CPPUNIT_ASSERT(!argument.firstValue());
73  argument.setEnvironmentVariable("PATH");
74  if(getenv("PATH")) {
75  CPPUNIT_ASSERT(argument.firstValue());
76  CPPUNIT_ASSERT(!strcmp(argument.firstValue(), getenv("PATH")));
77  } else {
78  CPPUNIT_ASSERT(!argument.firstValue());
79  }
80 }
81 
86 {
87  // setup parser with some test argument definitions
88  ArgumentParser parser;
90  QT_CONFIG_ARGUMENTS qtConfigArgs;
91  HelpArgument helpArg(parser);
92  Argument verboseArg("verbose", 'v', "be verbose");
93  verboseArg.setCombinable(true);
94  Argument fileArg("file", 'f', "specifies the path of the file to be opened");
95  fileArg.setValueNames({"path"});
96  fileArg.setRequiredValueCount(1);
97  fileArg.setEnvironmentVariable("PATH");
98  Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
99  filesArg.setValueNames({"path 1", "path 2"});
100  filesArg.setRequiredValueCount(-1);
101  Argument outputFileArg("output-file", 'o', "specifies the path of the output file");
102  outputFileArg.setValueNames({"path"});
103  outputFileArg.setRequiredValueCount(1);
104  outputFileArg.setRequired(true);
105  outputFileArg.setCombinable(true);
106  Argument printFieldNamesArg("print-field-names", '\0', "prints available field names");
107  Argument displayFileInfoArg("display-file-info", 'i', "displays general file information");
108  Argument notAlbumArg("album", 'a', "should not be confused with album value");
109  displayFileInfoArg.setDenotesOperation(true);
110  displayFileInfoArg.setSubArguments({&fileArg, &verboseArg, &notAlbumArg});
111  Argument fieldsArg("fields", '\0', "specifies the fields");
112  fieldsArg.setRequiredValueCount(-1);
113  fieldsArg.setValueNames({"title", "album", "artist", "trackpos"});
114  fieldsArg.setImplicit(true);
115  Argument displayTagInfoArg("get", 'p', "displays the values of all specified tag fields (displays all fields if none specified)");
116  displayTagInfoArg.setDenotesOperation(true);
117  displayTagInfoArg.setSubArguments({&fieldsArg, &filesArg, &verboseArg, &notAlbumArg});
118  parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg});
119 
120  // error about uncombinable arguments
121  const char *argv[] = {"tageditor", "get", "album", "title", "diskpos", "-f", "somefile"};
122  // try to parse, this should fail
123  try {
124  parser.parseArgs(7, argv);
125  CPPUNIT_FAIL("Exception expected.");
126  } catch(const Failure &e) {
127  CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"files\" can not be combined with \"fields\"."));
128  }
129 
130  // arguments read correctly after successful parse
131  filesArg.setCombinable(true);
132  parser.resetArgs();
133  parser.parseArgs(7, argv);
134  // check results
135  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
136  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
137  CPPUNIT_ASSERT(!strcmp(parser.executable(), "tageditor"));
138  CPPUNIT_ASSERT(!verboseArg.isPresent());
139  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
140  CPPUNIT_ASSERT(fieldsArg.isPresent());
141  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album"));
142  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
143  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
144  CPPUNIT_ASSERT_THROW(displayTagInfoArg.values().at(3), out_of_range);
145 
146  // skip empty args
147  const char *argv2[] = {"tageditor", "", "-p", "album", "title", "diskpos", "", "--files", "somefile"};
148  // reparse the args
149  parser.resetArgs();
150  parser.parseArgs(9, argv2);
151  // check results again
152  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
153  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
154  CPPUNIT_ASSERT(!verboseArg.isPresent());
155  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
156  CPPUNIT_ASSERT(fieldsArg.isPresent());
157  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album"));
158  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
159  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
160  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(3), ""));
161  CPPUNIT_ASSERT_THROW(fieldsArg.values().at(4), out_of_range);
162  CPPUNIT_ASSERT(filesArg.isPresent());
163  CPPUNIT_ASSERT(!strcmp(filesArg.values().at(0), "somefile"));
164 
165  // error about unknown argument: forget get/-p
166  const char *argv3[] = {"tageditor", "album", "title", "diskpos", "--files", "somefile"};
167  try {
168  parser.resetArgs();
169  parser.parseArgs(6, argv3);
170  CPPUNIT_FAIL("Exception expected.");
171  } catch(const Failure &e) {
172  CPPUNIT_ASSERT(!strcmp(e.what(), "The specified argument \"album\" is unknown and will be ignored."));
173  }
174 
175  // warning about unknown argument
176  parser.setUnknownArgumentBehavior(UnknownArgumentBehavior::Warn);
177  // redirect stderr to check whether warnings are printed correctly
178  stringstream buffer;
179  streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
180  parser.resetArgs();
181  try {
182  parser.parseArgs(6, argv3);
183  } catch(...) {
184  cerr.rdbuf(regularCerrBuffer);
185  throw;
186  }
187  cerr.rdbuf(regularCerrBuffer);
188  CPPUNIT_ASSERT_EQUAL("The specified argument \"album\" is unknown and will be ignored.\n"s
189  "The specified argument \"title\" is unknown and will be ignored.\n"s
190  "The specified argument \"diskpos\" is unknown and will be ignored.\n"s
191  "The specified argument \"--files\" is unknown and will be ignored.\n"s
192  "The specified argument \"somefile\" is unknown and will be ignored.\n"s,
193  buffer.str());
194  // none of the arguments should be present now
195  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
196  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
197  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
198  CPPUNIT_ASSERT(!fieldsArg.isPresent());
199  CPPUNIT_ASSERT(!filesArg.isPresent());
200 
201  // combined abbreviations like "-vf"
202  const char *argv4[] = {"tageditor", "-i", "-vf", "test"};
203  parser.setUnknownArgumentBehavior(UnknownArgumentBehavior::Fail);
204  parser.resetArgs();
205  parser.parseArgs(4, argv4);
206  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
207  CPPUNIT_ASSERT(displayFileInfoArg.isPresent());
208  CPPUNIT_ASSERT(verboseArg.isPresent());
209  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
210  CPPUNIT_ASSERT(!filesArg.isPresent());
211  CPPUNIT_ASSERT(fileArg.isPresent());
212  CPPUNIT_ASSERT(!strcmp(fileArg.values().at(0), "test"));
213  CPPUNIT_ASSERT_THROW(fileArg.values().at(1), out_of_range);
214 
215  // constraint checking: no multiple occurrences (not resetting verboseArg on purpose)
216  displayFileInfoArg.reset(), fileArg.reset();
217  try {
218  parser.parseArgs(4, argv4);
219  CPPUNIT_FAIL("Exception expected.");
220  } catch(const Failure &e) {
221  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
222  CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" mustn't be specified more than 1 time."));
223  }
224 
225  // constraint checking: no contraint (not resetting verboseArg on purpose)
226  displayFileInfoArg.reset(), fileArg.reset();
227  verboseArg.setConstraints(0, -1);
228  parser.parseArgs(4, argv4);
229  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
230 
231  // constraint checking: mandatory argument
232  verboseArg.setRequired(true);
233  parser.resetArgs();
234  parser.parseArgs(4, argv4);
235  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
236 
237  // contraint checking: error about missing mandatory argument
238  const char *argv5[] = {"tageditor", "-i", "-f", "test"};
239  displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
240  try {
241  parser.parseArgs(4, argv5);
242  CPPUNIT_FAIL("Exception expected.");
243  } catch(const Failure &e) {
244  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
245  CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time."));
246  }
247  verboseArg.setRequired(false);
248 
249  // combined abbreviation with nesting "-pf"
250  const char *argv10[] = {"tageditor", "-pf", "test"};
251  parser.resetArgs();
252  parser.parseArgs(3, argv10);
253  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
254  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
255  CPPUNIT_ASSERT(!fileArg.isPresent());
256  CPPUNIT_ASSERT(filesArg.isPresent());
257  CPPUNIT_ASSERT_EQUAL(filesArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
258  CPPUNIT_ASSERT(!strcmp(filesArg.values(0).front(), "test"));
259  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
260 
261  // constraint checking: no complains about missing -i
262  const char *argv6[] = {"tageditor", "-g"};
263  parser.resetArgs();
264  parser.parseArgs(2, argv6);
265  CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
266 
267  // constraint checking: dependend arguments (-f requires -i or -p)
268  const char *argv7[] = {"tageditor", "-f", "test"};
269  parser.resetArgs();
270  try {
271  parser.parseArgs(3, argv7);
272  CPPUNIT_FAIL("Exception expected.");
273  } catch(const Failure &e) {
274  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
275  CPPUNIT_ASSERT(!strcmp(e.what(), "The specified argument \"-f\" is unknown and will be ignored."));
276  }
277 
278  // equation sign syntax
279  const char *argv11[] = {"tageditor", "-if=test"};
280  parser.resetArgs();
281  parser.parseArgs(2, argv11);
282  CPPUNIT_ASSERT(!filesArg.isPresent());
283  CPPUNIT_ASSERT(fileArg.isPresent());
284  CPPUNIT_ASSERT_EQUAL(fileArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
285  CPPUNIT_ASSERT(!strcmp(fileArg.values(0).front(), "test"));
286 
287  // specifying value directly after abbreviation
288  const char *argv12[] = {"tageditor", "-iftest"};
289  parser.resetArgs();
290  parser.parseArgs(2, argv12);
291  CPPUNIT_ASSERT(!filesArg.isPresent());
292  CPPUNIT_ASSERT(fileArg.isPresent());
293  CPPUNIT_ASSERT_EQUAL(fileArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
294  CPPUNIT_ASSERT(!strcmp(fileArg.values(0).front(), "test"));
295 
296  // default argument
297  const char *argv8[] = {"tageditor"};
298  parser.resetArgs();
299  parser.parseArgs(1, argv8);
300  CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
301  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
302  CPPUNIT_ASSERT(!verboseArg.isPresent());
303  CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
304  CPPUNIT_ASSERT(!filesArg.isPresent());
305  CPPUNIT_ASSERT(!fileArg.isPresent());
306  if(getenv("PATH")) {
307  CPPUNIT_ASSERT(fileArg.firstValue());
308  CPPUNIT_ASSERT(!strcmp(fileArg.firstValue(), getenv("PATH")));
309  } else {
310  CPPUNIT_ASSERT(!fileArg.firstValue());
311  }
312 
313  // constraint checking: required value count with sufficient number of provided parameters
314  const char *argv13[] = {"tageditor", "get", "--fields", "album=test", "title", "diskpos", "--files", "somefile"};
315  verboseArg.setRequired(false);
316  parser.resetArgs();
317  parser.parseArgs(8, argv13);
318  // this should still work without complaints
319  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
320  CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
321  CPPUNIT_ASSERT(!verboseArg.isPresent());
322  CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
323  CPPUNIT_ASSERT(fieldsArg.isPresent());
324  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
325  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
326  CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
327  CPPUNIT_ASSERT_THROW(fieldsArg.values().at(3), out_of_range);
328  CPPUNIT_ASSERT(filesArg.isPresent());
329  CPPUNIT_ASSERT(!strcmp(filesArg.values().at(0), "somefile"));
330  CPPUNIT_ASSERT(!notAlbumArg.isPresent());
331 
332  // constraint checking: required value count with insufficient number of provided parameters
333  const char *argv9[] = {"tageditor", "-p", "album", "title", "diskpos"};
334  fieldsArg.setRequiredValueCount(4);
335  parser.resetArgs();
336  try {
337  parser.parseArgs(5, argv9);
338  CPPUNIT_FAIL("Exception expected.");
339  } catch(const Failure &e) {
340  CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
341  CPPUNIT_ASSERT(!strcmp(e.what(), "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
342  }
343 }
344 
349 {
350  ArgumentParser parser;
351  Argument callbackArg("with-callback", 't', "callback test");
352  callbackArg.setRequiredValueCount(2);
353  callbackArg.setCallback([] (const ArgumentOccurrence &occurrence) {
354  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), occurrence.index);
355  CPPUNIT_ASSERT(occurrence.path.empty());
356  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), occurrence.values.size());
357  CPPUNIT_ASSERT(!strcmp(occurrence.values[0], "val1"));
358  CPPUNIT_ASSERT(!strcmp(occurrence.values[1], "val2"));
359  throw 42;
360  });
361  Argument noCallbackArg("no-callback", 'l', "callback test");
362  noCallbackArg.setRequiredValueCount(2);
363  parser.setMainArguments({&callbackArg, &noCallbackArg});
364 
365  // test whether callback is invoked when argument with callback is specified
366  const char *argv[] = {"test", "-t", "val1", "val2"};
367  try {
368  parser.parseArgs(4, argv);
369  } catch(int i) {
370  CPPUNIT_ASSERT_EQUAL(i, 42);
371  }
372 
373  // test whether callback is not invoked when argument with callback is not specified
374  callbackArg.reset();
375  const char *argv2[] = {"test", "-l", "val1", "val2"};
376  parser.parseArgs(4, argv2);
377 }
378 
385 {
386  ArgumentParser parser;
387  HelpArgument helpArg(parser);
388  Argument verboseArg("verbose", 'v', "be verbose");
389  verboseArg.setCombinable(true);
390  Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
391  filesArg.setRequiredValueCount(-1);
392  filesArg.setCombinable(true);
393  Argument nestedSubArg("nested-sub", '\0', "nested sub arg");
394  Argument subArg("sub", '\0', "sub arg");
395  subArg.setSubArguments({&nestedSubArg});
396  Argument displayFileInfoArg("display-file-info", 'i', "displays general file information");
397  displayFileInfoArg.setDenotesOperation(true);
398  displayFileInfoArg.setSubArguments({&filesArg, &verboseArg, &subArg});
399  Argument fieldsArg("fields", '\0', "specifies the fields");
400  fieldsArg.setRequiredValueCount(-1);
401  fieldsArg.setPreDefinedCompletionValues("title album artist trackpos");
402  fieldsArg.setImplicit(true);
403  Argument valuesArg("values", '\0', "specifies the fields");
404  valuesArg.setRequiredValueCount(-1);
405  valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
406  valuesArg.setImplicit(true);
407  valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
408  Argument getArg("get", 'g', "gets tag values");
409  getArg.setSubArguments({&fieldsArg, &filesArg});
410  Argument setArg("set", 's', "sets tag values");
411  setArg.setSubArguments({&valuesArg, &filesArg});
412 
413  parser.setMainArguments({&helpArg, &displayFileInfoArg, &getArg, &setArg});
414 
415  // redirect cout to custom buffer
416  stringstream buffer;
417  streambuf *regularCoutBuffer = cout.rdbuf(buffer.rdbuf());
418 
419  try {
420  // fail due to operation flags not set
421  const char *const argv1[] = {"se"};
422  ArgumentReader reader(parser, argv1, argv1 + 1, true);
423  reader.read();
424  parser.printBashCompletion(1, argv1, 0, reader);
425  cout.rdbuf(regularCoutBuffer);
426  CPPUNIT_ASSERT_EQUAL("COMPREPLY=()\n"s, buffer.str());
427 
428  // correct operation arg flags
429  buffer.str(string());
430  cout.rdbuf(buffer.rdbuf());
431  getArg.setDenotesOperation(true), setArg.setDenotesOperation(true);
432  reader.reset(argv1, argv1 + 1).read();
433  parser.printBashCompletion(1, argv1, 0, reader);
434  cout.rdbuf(regularCoutBuffer);
435  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('set' )\n"s, buffer.str());
436 
437  // argument at current cursor position already specified -> the completion should just return the argument
438  const char *const argv2[] = {"set"};
439  buffer.str(string());
440  cout.rdbuf(buffer.rdbuf());
441  parser.resetArgs();
442  reader.reset(argv2, argv2 + 1).read();
443  parser.printBashCompletion(1, argv2, 0, reader);
444  cout.rdbuf(regularCoutBuffer);
445  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('set' )\n"s, buffer.str());
446 
447  // advance the cursor position -> the completion should propose the next argument
448  buffer.str(string());
449  cout.rdbuf(buffer.rdbuf());
450  parser.resetArgs();
451  reader.reset(argv2, argv2 + 1).read();
452  parser.printBashCompletion(1, argv2, 1, reader);
453  cout.rdbuf(regularCoutBuffer);
454  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('--files' '--values' )\n"s, buffer.str());
455 
456  // specifying no args should propose all main arguments
457  buffer.str(string());
458  cout.rdbuf(buffer.rdbuf());
459  parser.resetArgs();
460  reader.reset(nullptr, nullptr).read();
461  parser.printBashCompletion(0, nullptr, 0, reader);
462  cout.rdbuf(regularCoutBuffer);
463  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n"s, buffer.str());
464 
465  // pre-defined values
466  const char *const argv3[] = {"get", "--fields"};
467  buffer.str(string());
468  cout.rdbuf(buffer.rdbuf());
469  parser.resetArgs();
470  reader.reset(argv3, argv3 + 2).read();
471  parser.printBashCompletion(2, argv3, 2, reader);
472  cout.rdbuf(regularCoutBuffer);
473  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n"s, buffer.str());
474 
475  // pre-defined values with equation sign, one letter already present
476  const char *const argv4[] = {"set", "--values", "a"};
477  buffer.str(string());
478  cout.rdbuf(buffer.rdbuf());
479  parser.resetArgs();
480  reader.reset(argv4, argv4 + 3).read();
481  parser.printBashCompletion(3, argv4, 2, reader);
482  cout.rdbuf(regularCoutBuffer);
483  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n"s, buffer.str());
484 
485  // file names
486  string iniFilePath = TestUtilities::testFilePath("test.ini");
487  iniFilePath.resize(iniFilePath.size() - 4);
488  string mkvFilePath = TestUtilities::testFilePath("test 'with quote'.mkv");
489  mkvFilePath.resize(mkvFilePath.size() - 17);
491  const char *const argv5[] = {"get", "--files", iniFilePath.c_str()};
492  buffer.str(string());
493  cout.rdbuf(buffer.rdbuf());
494  parser.resetArgs();
495  reader.reset(argv5, argv5 + 3).read();
496  parser.printBashCompletion(3, argv5, 2, reader);
497  cout.rdbuf(regularCoutBuffer);
498  // order for file names is not specified
499  const string res(buffer.str());
500  if(res.find(".mkv") < res.find(".ini")) {
501  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('" % mkvFilePath % " '\"'\"'with quote'\"'\"'.mkv' '" % iniFilePath + ".ini' ); compopt -o filenames\n", buffer.str());
502  } else {
503  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('" % iniFilePath % ".ini' '" % mkvFilePath + " '\"'\"'with quote'\"'\"'.mkv' ); compopt -o filenames\n", buffer.str());
504  }
505 
506  // sub arguments
507  const char *const argv6[] = {"set", "--"};
508  buffer.str(string());
509  cout.rdbuf(buffer.rdbuf());
510  parser.resetArgs();
511  reader.reset(argv6, argv6 + 2).read();
512  parser.printBashCompletion(2, argv6, 1, reader);
513  cout.rdbuf(regularCoutBuffer);
514  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('--files' '--values' )\n"s, buffer.str());
515 
516  // nested sub arguments
517  const char *const argv7[] = {"-i", "--sub", "--"};
518  buffer.str(string());
519  cout.rdbuf(buffer.rdbuf());
520  parser.resetArgs();
521  reader.reset(argv7, argv7 + 3).read();
522  parser.printBashCompletion(3, argv7, 2, reader);
523  cout.rdbuf(regularCoutBuffer);
524  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('--files' '--nested-sub' '--verbose' )\n"s, buffer.str());
525 
526  // started pre-defined values with equation sign, one letter already present, last value matches
527  const char *const argv8[] = {"set", "--values", "t"};
528  buffer.str(string());
529  cout.rdbuf(buffer.rdbuf());
530  parser.resetArgs();
531  reader.reset(argv8, argv8 + 3).read();
532  parser.printBashCompletion(3, argv8, 2, reader);
533  cout.rdbuf(regularCoutBuffer);
534  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace\n"s, buffer.str());
535 
536  // combined abbreviations
537  const char *const argv9[] = {"-gf"};
538  buffer.str(string());
539  cout.rdbuf(buffer.rdbuf());
540  parser.resetArgs();
541  reader.reset(argv9, argv9 + 1).read();
542  parser.printBashCompletion(1, argv9, 0, reader);
543  cout.rdbuf(regularCoutBuffer);
544  CPPUNIT_ASSERT_EQUAL("COMPREPLY=('-gf' )\n"s, buffer.str());
545 
546  buffer.str(string());
547  cout.rdbuf(buffer.rdbuf());
548  parser.resetArgs();
549  reader.reset(argv9, argv9 + 1).read();
550  parser.printBashCompletion(1, argv9, 1, reader);
551  cout.rdbuf(regularCoutBuffer);
552  CPPUNIT_ASSERT_EQUAL(static_cast<string::size_type>(0), buffer.str().find("COMPREPLY=('--fields' "));
553 
554  } catch(...) {
555  cout.rdbuf(regularCoutBuffer);
556  throw;
557  }
558 }
void testBashCompletion()
Tests bash completion.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
#define QT_CONFIG_ARGUMENTS
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.
void setCombinable(bool value)
Sets whether this argument can be combined.
void setUnknownArgumentBehavior(UnknownArgumentBehavior behavior)
Sets how unknown arguments are treated.
virtual const char * what() const USE_NOTHROW
Returns a C-style character string describing the cause of the Failure.
Definition: failure.cpp:37
Contains currently only ArgumentParser and related classes.
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
STL namespace.
void testArgument()
Tests the behaviour of the argument class.
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.
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 ArgumentParserTests class tests the ArgumentParser and Argument classes.
void setConstraints(std::size_t minOccurrences, std::size_t maxOccurrences)
Sets the allowed number of occurrences.
void setValueCompletionBehavior(ValueCompletionBehavior valueCompletionBehaviour)
Sets the items to be considered when generating completion for the values.
#define SET_APPLICATION_INFO
SET_APPLICATION_INFO
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.
const char * executable() const
Returns the name of the current executable.
void read()
Reads the commands line arguments specified when constructing the object.
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 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).
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
void setEnvironmentVariable(const char *environmentVariable)
Sets the environment variable queried when firstValue() is called.
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
The HelpArgument class prints help information for an argument parser when present (–help...
CPPUNIT_TEST_SUITE_REGISTRATION(ArgumentParserTests)
The Failure class is thrown by an ArgumentParser when a parsing error occurs.
Definition: failure.h:11
void setDenotesOperation(bool denotesOperation)
Sets whether the argument denotes the operation.
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.
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
CPP_UTILITIES_EXPORT std::string testFilePath(const std::string &name)
Convenience function which returns the full path of the test file with the specified name...
Definition: testutils.h:92
The ArgumentParser class provides a means for handling command line arguments.
void setValueNames(std::initializer_list< const char *> valueNames)
Sets the names of the requried values.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.