4 #include "../conversion/stringbuilder.h" 6 #include "../application/argumentparser.h" 7 #include "../application/argumentparserprivate.h" 8 #include "../application/commandlineutils.h" 9 #include "../application/failure.h" 10 #include "../application/fakeqtconfigarguments.h" 12 #include "../io/path.h" 14 #include "resources/config.h" 16 #include <cppunit/TestFixture.h> 17 #include <cppunit/extensions/HelperMacros.h> 35 CPPUNIT_TEST(testArgument);
36 CPPUNIT_TEST(testParsing);
37 CPPUNIT_TEST(testCallbacks);
38 CPPUNIT_TEST(testBashCompletion);
39 CPPUNIT_TEST(testHelp);
40 CPPUNIT_TEST(testSetMainArguments);
41 CPPUNIT_TEST_SUITE_END();
50 void testBashCompletion();
52 void testSetMainArguments();
73 Argument argument(
"test",
't',
"some description");
74 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
false);
76 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
true);
77 Argument subArg(
"sub",
's',
"sub arg");
79 CPPUNIT_ASSERT_EQUAL(subArg.parents().at(0), &argument);
80 CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
83 setenv(
"FOO_ENV_VAR",
"foo",
true);
84 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"foo"));
86 occurrence.
values.emplace_back(
"bar");
87 argument.m_occurrences.emplace_back(move(occurrence));
88 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"bar"));
101 Argument verboseArg(
"verbose",
'v',
"be verbose");
103 Argument fileArg(
"file",
'f',
"specifies the path of the file to be opened");
107 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
110 Argument outputFileArg(
"output-file",
'o',
"specifies the path of the output file");
115 Argument printFieldNamesArg(
"print-field-names",
'\0',
"prints available field names");
116 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
117 Argument notAlbumArg(
"album",
'a',
"should not be confused with album value");
119 displayFileInfoArg.
setSubArguments({ &fileArg, &verboseArg, ¬AlbumArg });
120 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
122 fieldsArg.
setValueNames({
"title",
"album",
"artist",
"trackpos" });
124 Argument displayTagInfoArg(
"get",
'p',
"displays the values of all specified tag fields (displays all fields if none specified)");
126 displayTagInfoArg.
setSubArguments({ &fieldsArg, &filesArg, &verboseArg, ¬AlbumArg });
127 parser.
setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg });
130 const char *argv[] = {
"tageditor",
"get",
"album",
"title",
"diskpos",
"-f",
"somefile" };
134 CPPUNIT_FAIL(
"Exception expected.");
136 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"files\" can not be combined with \"fields\"."));
144 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
145 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
146 CPPUNIT_ASSERT(!strcmp(parser.
executable(),
"tageditor"));
148 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
150 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
151 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
152 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
153 CPPUNIT_ASSERT_THROW(displayTagInfoArg.
values().at(3), out_of_range);
156 const char *argv2[] = {
"tageditor",
"",
"-p",
"album",
"title",
"diskpos",
"",
"--files",
"somefile" };
161 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
162 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
164 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
166 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
167 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
168 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
169 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(3),
""));
170 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(4), out_of_range);
172 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
175 const char *argv3[] = {
"tageditor",
"album",
"title",
"diskpos",
"--files",
"somefile" };
179 CPPUNIT_FAIL(
"Exception expected.");
181 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown."s,
string(e.
what()));
188 streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
193 cerr.rdbuf(regularCerrBuffer);
196 cerr.rdbuf(regularCerrBuffer);
197 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown and will be ignored.\n"s
198 "The specified argument \"title\" is unknown and will be ignored.\n"s
199 "The specified argument \"diskpos\" is unknown and will be ignored.\n"s
200 "The specified argument \"--files\" is unknown and will be ignored.\n"s
201 "The specified argument \"somefile\" is unknown and will be ignored.\n"s,
204 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
205 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
206 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
211 const char *argv4[] = {
"tageditor",
"-i",
"-vf",
"test" };
215 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
216 CPPUNIT_ASSERT(displayFileInfoArg.
isPresent());
218 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
221 CPPUNIT_ASSERT(!strcmp(fileArg.
values().at(0),
"test"));
222 CPPUNIT_ASSERT_THROW(fileArg.
values().at(1), out_of_range);
228 CPPUNIT_FAIL(
"Exception expected.");
230 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
231 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" mustn't be specified more than 1 time."));
238 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
244 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
247 const char *argv5[] = {
"tageditor",
"-i",
"-f",
"test" };
251 CPPUNIT_FAIL(
"Exception expected.");
253 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
254 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" must be specified at least 1 time."));
259 const char *argv10[] = {
"tageditor",
"-pf",
"test" };
262 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
263 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
266 CPPUNIT_ASSERT_EQUAL(filesArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
267 CPPUNIT_ASSERT(!strcmp(filesArg.
values(0).front(),
"test"));
268 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
271 const char *argv6[] = {
"tageditor",
"-g" };
274 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
277 const char *argv7[] = {
"tageditor",
"-f",
"test" };
281 CPPUNIT_FAIL(
"Exception expected.");
283 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
284 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"-f\" is unknown."s,
string(e.
what()));
288 const char *argv11[] = {
"tageditor",
"-if=test" };
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"));
297 const char *argv12[] = {
"tageditor",
"-iftest" };
302 CPPUNIT_ASSERT_EQUAL(fileArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
303 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
306 const char *argv8[] = {
"tageditor" };
309 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
310 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
312 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
315 if (getenv(
"PATH")) {
317 CPPUNIT_ASSERT(!strcmp(fileArg.
firstValue(), getenv(
"PATH")));
323 const char *argv13[] = {
"tageditor",
"get",
"--fields",
"album=test",
"title",
"diskpos",
"--files",
"somefile" };
328 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
329 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
331 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
333 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
334 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
335 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
336 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(3), out_of_range);
338 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
339 CPPUNIT_ASSERT(!notAlbumArg.
isPresent());
342 const char *argv9[] = {
"tageditor",
"-p",
"album",
"title",
"diskpos" };
347 CPPUNIT_FAIL(
"Exception expected.");
349 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
350 CPPUNIT_ASSERT(!strcmp(e.
what(),
351 "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
355 const char *argv14[] = {
"tageditor",
"get",
"fields",
"album=test",
"-f",
"somefile" };
360 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
362 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
368 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
370 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"fields"));
371 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"album=test"));
380 Argument callbackArg(
"with-callback",
't',
"callback test");
383 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), occurrence.
index);
384 CPPUNIT_ASSERT(occurrence.
path.empty());
385 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), occurrence.
values.size());
386 CPPUNIT_ASSERT(!strcmp(occurrence.
values[0],
"val1"));
387 CPPUNIT_ASSERT(!strcmp(occurrence.
values[1],
"val2"));
390 Argument noCallbackArg(
"no-callback",
'l',
"callback test");
395 const char *argv[] = {
"test",
"-t",
"val1",
"val2" };
399 CPPUNIT_ASSERT_EQUAL(i, 42);
404 const char *argv2[] = {
"test",
"-l",
"val1",
"val2" };
411 static bool exitCalled =
false;
421 Argument verboseArg(
"verbose",
'v',
"be verbose");
423 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
426 Argument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg");
427 Argument subArg(
"sub",
'\0',
"sub arg");
429 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
431 displayFileInfoArg.
setSubArguments({ &filesArg, &verboseArg, &subArg });
432 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
436 Argument valuesArg(
"values",
'\0',
"specifies the fields");
440 valuesArg.
setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
441 Argument getArg(
"get",
'g',
"gets tag values");
443 Argument setArg(
"set",
's',
"sets tag values");
446 parser.
setMainArguments({ &helpArg, &displayFileInfoArg, &getArg, &setArg });
449 const char *
const argv1[] = {
"se" };
454 parser.printBashCompletion(1, argv1, 0, reader);
463 parser.printBashCompletion(1, argv1, 0, reader);
467 const char *
const argv2[] = {
"set" };
472 parser.printBashCompletion(1, argv2, 0, reader);
478 const OutputCheck c(
"COMPREPLY=('--files' '--values' )\n");
480 parser.printBashCompletion(1, argv2, 1, reader);
487 const OutputCheck c(
"COMPREPLY=('files' '--values' )\n");
489 parser.printBashCompletion(1, argv2, 1, reader);
496 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
498 parser.printBashCompletion(0,
nullptr, 0, reader);
502 const char *
const argv3[] = {
"get",
"--fields" };
505 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n");
507 parser.printBashCompletion(2, argv3, 2, reader);
511 const char *
const argv4[] = {
"set",
"--values",
"a" };
514 const OutputCheck c(
"COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n");
516 parser.printBashCompletion(3, argv4, 2, reader);
522 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' )\n");
524 parser.printBashCompletion(1, argv3, 2, reader);
529 iniFilePath.resize(iniFilePath.size() - 4);
531 mkvFilePath.resize(mkvFilePath.size() - 17);
533 const char *
const argv5[] = {
"get",
"--files", iniFilePath.c_str() };
536 const OutputCheck c(
"COMPREPLY=('" % mkvFilePath %
" '\"'\"'with quote'\"'\"'.mkv' '" % iniFilePath +
".ini' ); compopt -o filenames\n",
537 "COMPREPLY=('" % iniFilePath %
".ini' '" % mkvFilePath +
" '\"'\"'with quote'\"'\"'.mkv' ); compopt -o filenames\n");
539 parser.printBashCompletion(3, argv5, 2, reader);
543 const char *
const argv6[] = {
"set",
"--" };
546 const OutputCheck c(
"COMPREPLY=('--files' '--values' )\n");
548 parser.printBashCompletion(2, argv6, 1, reader);
552 const char *
const argv7[] = {
"-i",
"--sub",
"--" };
555 const OutputCheck c(
"COMPREPLY=('--files' '--nested-sub' '--verbose' )\n");
557 parser.printBashCompletion(3, argv7, 2, reader);
561 const char *
const argv8[] = {
"set",
"--values",
"t" };
564 const OutputCheck c(
"COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace\n");
566 parser.printBashCompletion(3, argv8, 2, reader);
570 const char *
const argv9[] = {
"-gf" };
575 parser.printBashCompletion(1, argv9, 0, reader);
579 const OutputCheck c([](
const string &actualOutput) { CPPUNIT_ASSERT_EQUAL(0_st, actualOutput.find(
"COMPREPLY=('--fields' ")); });
581 parser.printBashCompletion(1, argv9, 1, reader);
588 const char *
const argv10[] = {
"/some/path/tageditor",
"--bash-completion-for",
"0" };
591 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
594 CPPUNIT_ASSERT(!strcmp(
"/some/path/tageditor", parser.
executable()));
595 CPPUNIT_ASSERT(exitCalled);
598 const char *
const argv11[] = {
"/some/path/tageditor",
"--bash-completion-for",
"ge" };
614 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(4 + 3), indent.
level);
620 OperationArgument verboseArg(
"verbose",
'v',
"be verbose",
"actually not an operation");
622 ConfigValueArgument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg", {
"value1",
"value2" });
624 Argument subArg(
"sub",
'\0',
"sub arg");
627 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
631 Argument envArg(
"env",
'\0',
"env");
642 const char *
const argv[] = {
"app",
"-h" };
644 const OutputCheck c(
"\e[1m" APP_NAME
", version " APP_VERSION
"\n" 645 "\e[0mLinked against: somelib, some other lib\n" 647 "Available arguments:\n" 648 "\e[1m--help, -h\e[0m\n" 649 " shows this information\n" 650 " particularities: mandatory\n" 652 "\e[1mverbose, -v\e[0m\n" 654 " example: actually not an operation\n" 656 "\e[1m--files, -f\e[0m\n" 657 " specifies the path of the file(s) to be opened\n" 660 " particularities: mandatory if parent argument is present\n" 661 " \e[1m--nested-sub\e[0m [value1] [value2] ...\n" 664 "\e[1m--env\e[0m [file] [value 2]\n" 666 " default environment variable: FILES\n" 668 "Project website: " APP_URL
"\n");
680 Argument subArg(
"sub-arg",
's',
"mandatory sub arg");
685 CPPUNIT_ASSERT_MESSAGE(
"clear main args", parser.
mainArguments().empty());
687 CPPUNIT_ASSERT_MESSAGE(
"no default due to required sub arg", !parser.
defaultArgument());
690 CPPUNIT_ASSERT_MESSAGE(
"default if no required sub arg", &helpArg == parser.
defaultArgument());
void testBashCompletion()
Tests bash completion.
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.
void setCombinable(bool value)
Sets whether this argument can be combined.
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.
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.
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.
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.
Contains classes and functions utilizing creating of test applications.
void setValueCompletionBehavior(ValueCompletionBehavior valueCompletionBehaviour)
Sets the items to be considered when generating completion for the values.
#define SET_APPLICATION_INFO
SET_APPLICATION_INFO
void appendValueName(const char *valueName)
Appends a value name.
CPP_UTILITIES_EXPORT std::initializer_list< const char * > dependencyVersions
Specifies the dependency versions the application was linked against (used by ArgumentParser::printHe...
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 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...
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.
const ArgumentVector & mainArguments() const
Returns the main arguments.
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.
The ArgumentReader class internally encapsulates the process of reading command line arguments...
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
void read()
Reads the commands line arguments specified when constructing the object.
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...
The ArgumentParser class provides a means for handling command line arguments.
void testHelp()
Tests –help output.
void setValueNames(std::initializer_list< const char *> valueNames)
Sets the names of the requried values.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.