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/ansiescapecodes.h" 13 #include "../io/path.h" 15 #include "resources/config.h" 17 #include <cppunit/TestFixture.h> 18 #include <cppunit/extensions/HelperMacros.h> 36 CPPUNIT_TEST(testArgument);
37 CPPUNIT_TEST(testParsing);
38 CPPUNIT_TEST(testCallbacks);
39 CPPUNIT_TEST(testBashCompletion);
40 CPPUNIT_TEST(testHelp);
41 CPPUNIT_TEST(testSetMainArguments);
42 CPPUNIT_TEST_SUITE_END();
51 void testBashCompletion();
53 void testSetMainArguments();
74 Argument argument(
"test",
't',
"some description");
75 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
false);
77 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
true);
78 Argument subArg(
"sub",
's',
"sub arg");
80 CPPUNIT_ASSERT_EQUAL(subArg.parents().at(0), &argument);
81 CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
84 setenv(
"FOO_ENV_VAR",
"foo",
true);
85 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"foo"));
87 occurrence.
values.emplace_back(
"bar");
88 argument.m_occurrences.emplace_back(move(occurrence));
89 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"bar"));
102 Argument verboseArg(
"verbose",
'v',
"be verbose");
104 Argument fileArg(
"file",
'f',
"specifies the path of the file to be opened");
108 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
111 Argument outputFileArg(
"output-file",
'o',
"specifies the path of the output file");
116 Argument printFieldNamesArg(
"print-field-names",
'\0',
"prints available field names");
117 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
118 Argument notAlbumArg(
"album",
'a',
"should not be confused with album value");
120 displayFileInfoArg.
setSubArguments({ &fileArg, &verboseArg, ¬AlbumArg });
121 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
123 fieldsArg.
setValueNames({
"title",
"album",
"artist",
"trackpos" });
125 Argument displayTagInfoArg(
"get",
'p',
"displays the values of all specified tag fields (displays all fields if none specified)");
127 displayTagInfoArg.
setSubArguments({ &fieldsArg, &filesArg, &verboseArg, ¬AlbumArg });
128 parser.
setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg });
131 const char *argv[] = {
"tageditor",
"get",
"album",
"title",
"diskpos",
"-f",
"somefile" };
135 CPPUNIT_FAIL(
"Exception expected.");
137 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"files\" can not be combined with \"fields\"."));
145 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
146 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
147 CPPUNIT_ASSERT(!strcmp(parser.
executable(),
"tageditor"));
149 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
151 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
152 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
153 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
154 CPPUNIT_ASSERT_THROW(displayTagInfoArg.
values().at(3), out_of_range);
157 const char *argv2[] = {
"tageditor",
"",
"-p",
"album",
"title",
"diskpos",
"",
"--files",
"somefile" };
162 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
163 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
165 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
167 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
168 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
169 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
170 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(3),
""));
171 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(4), out_of_range);
173 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
176 const char *argv3[] = {
"tageditor",
"album",
"title",
"diskpos",
"--files",
"somefile" };
180 CPPUNIT_FAIL(
"Exception expected.");
182 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown."s,
string(e.
what()));
189 streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
195 cerr.rdbuf(regularCerrBuffer);
198 cerr.rdbuf(regularCerrBuffer);
199 CPPUNIT_ASSERT_EQUAL(
"Warning: The specified argument \"album\" is unknown and will be ignored.\n"s
200 "Warning: The specified argument \"title\" is unknown and will be ignored.\n"s
201 "Warning: The specified argument \"diskpos\" is unknown and will be ignored.\n"s
202 "Warning: The specified argument \"--files\" is unknown and will be ignored.\n"s
203 "Warning: The specified argument \"somefile\" is unknown and will be ignored.\n"s,
206 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
207 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
208 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
213 const char *argv4[] = {
"tageditor",
"-i",
"-vf",
"test" };
217 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
218 CPPUNIT_ASSERT(displayFileInfoArg.
isPresent());
220 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
223 CPPUNIT_ASSERT(!strcmp(fileArg.
values().at(0),
"test"));
224 CPPUNIT_ASSERT_THROW(fileArg.
values().at(1), out_of_range);
230 CPPUNIT_FAIL(
"Exception expected.");
232 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
233 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" mustn't be specified more than 1 time."));
240 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
246 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
249 const char *argv5[] = {
"tageditor",
"-i",
"-f",
"test" };
253 CPPUNIT_FAIL(
"Exception expected.");
255 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
256 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" must be specified at least 1 time."));
261 const char *argv10[] = {
"tageditor",
"-pf",
"test" };
264 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
265 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
268 CPPUNIT_ASSERT_EQUAL(filesArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
269 CPPUNIT_ASSERT(!strcmp(filesArg.
values(0).front(),
"test"));
270 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
273 const char *argv6[] = {
"tageditor",
"-g" };
276 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
279 const char *argv7[] = {
"tageditor",
"-f",
"test" };
283 CPPUNIT_FAIL(
"Exception expected.");
285 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
286 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"-f\" is unknown."s,
string(e.
what()));
290 const char *argv11[] = {
"tageditor",
"-if=test" };
295 CPPUNIT_ASSERT_EQUAL(fileArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
296 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
299 const char *argv12[] = {
"tageditor",
"-iftest" };
304 CPPUNIT_ASSERT_EQUAL(fileArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
305 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
308 const char *argv8[] = {
"tageditor" };
311 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
312 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
314 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
317 if (getenv(
"PATH")) {
319 CPPUNIT_ASSERT(!strcmp(fileArg.
firstValue(), getenv(
"PATH")));
325 const char *argv13[] = {
"tageditor",
"get",
"--fields",
"album=test",
"title",
"diskpos",
"--files",
"somefile" };
330 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
331 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
333 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
335 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
336 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
337 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
338 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(3), out_of_range);
340 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
341 CPPUNIT_ASSERT(!notAlbumArg.
isPresent());
344 const char *argv9[] = {
"tageditor",
"-p",
"album",
"title",
"diskpos" };
349 CPPUNIT_FAIL(
"Exception expected.");
351 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
352 CPPUNIT_ASSERT(!strcmp(e.
what(),
353 "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
357 const char *argv14[] = {
"tageditor",
"get",
"fields",
"album=test",
"-f",
"somefile" };
362 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
364 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
370 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
372 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"fields"));
373 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"album=test"));
382 Argument callbackArg(
"with-callback",
't',
"callback test");
385 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), occurrence.
index);
386 CPPUNIT_ASSERT(occurrence.
path.empty());
387 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), occurrence.
values.size());
388 CPPUNIT_ASSERT(!strcmp(occurrence.
values[0],
"val1"));
389 CPPUNIT_ASSERT(!strcmp(occurrence.
values[1],
"val2"));
392 Argument noCallbackArg(
"no-callback",
'l',
"callback test");
397 const char *argv[] = {
"test",
"-t",
"val1",
"val2" };
401 CPPUNIT_ASSERT_EQUAL(i, 42);
406 const char *argv2[] = {
"test",
"-l",
"val1",
"val2" };
413 static bool exitCalled =
false;
423 Argument verboseArg(
"verbose",
'v',
"be verbose");
425 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
428 Argument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg");
429 Argument subArg(
"sub",
'\0',
"sub arg");
431 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
433 displayFileInfoArg.
setSubArguments({ &filesArg, &verboseArg, &subArg });
434 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
438 Argument valuesArg(
"values",
'\0',
"specifies the fields");
442 valuesArg.
setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
443 Argument getArg(
"get",
'g',
"gets tag values");
445 Argument setArg(
"set",
's',
"sets tag values");
448 parser.
setMainArguments({ &helpArg, &displayFileInfoArg, &getArg, &setArg });
451 const char *
const argv1[] = {
"se" };
456 parser.printBashCompletion(1, argv1, 0, reader);
465 parser.printBashCompletion(1, argv1, 0, reader);
469 const char *
const argv2[] = {
"set" };
474 parser.printBashCompletion(1, argv2, 0, reader);
480 const OutputCheck c(
"COMPREPLY=('--files' '--values' )\n");
482 parser.printBashCompletion(1, argv2, 1, reader);
489 const OutputCheck c(
"COMPREPLY=('files' '--values' )\n");
491 parser.printBashCompletion(1, argv2, 1, reader);
498 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
500 parser.printBashCompletion(0,
nullptr, 0, reader);
504 const char *
const argv3[] = {
"get",
"--fields" };
507 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n");
509 parser.printBashCompletion(2, argv3, 2, reader);
513 const char *
const argv4[] = {
"set",
"--values",
"a" };
516 const OutputCheck c(
"COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n");
518 parser.printBashCompletion(3, argv4, 2, reader);
524 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' )\n");
526 parser.printBashCompletion(1, argv3, 2, reader);
531 iniFilePath.resize(iniFilePath.size() - 4);
533 mkvFilePath.resize(mkvFilePath.size() - 17);
535 const char *
const argv5[] = {
"get",
"--files", iniFilePath.c_str() };
538 const OutputCheck c(
"COMPREPLY=('" % mkvFilePath %
" '\"'\"'with quote'\"'\"'.mkv' '" % iniFilePath +
".ini' ); compopt -o filenames\n",
539 "COMPREPLY=('" % iniFilePath %
".ini' '" % mkvFilePath +
" '\"'\"'with quote'\"'\"'.mkv' ); compopt -o filenames\n");
541 parser.printBashCompletion(3, argv5, 2, reader);
545 const char *
const argv6[] = {
"set",
"--" };
548 const OutputCheck c(
"COMPREPLY=('--files' '--values' )\n");
550 parser.printBashCompletion(2, argv6, 1, reader);
554 const char *
const argv7[] = {
"-i",
"--sub",
"--" };
557 const OutputCheck c(
"COMPREPLY=('--files' '--nested-sub' '--verbose' )\n");
559 parser.printBashCompletion(3, argv7, 2, reader);
563 const char *
const argv8[] = {
"set",
"--values",
"t" };
566 const OutputCheck c(
"COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace\n");
568 parser.printBashCompletion(3, argv8, 2, reader);
572 const char *
const argv9[] = {
"-gf" };
577 parser.printBashCompletion(1, argv9, 0, reader);
581 const OutputCheck c([](
const string &actualOutput) { CPPUNIT_ASSERT_EQUAL(0_st, actualOutput.find(
"COMPREPLY=('--fields' ")); });
583 parser.printBashCompletion(1, argv9, 1, reader);
590 const char *
const argv10[] = {
"/some/path/tageditor",
"--bash-completion-for",
"0" };
593 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n");
596 CPPUNIT_ASSERT(!strcmp(
"/some/path/tageditor", parser.
executable()));
597 CPPUNIT_ASSERT(exitCalled);
600 const char *
const argv11[] = {
"/some/path/tageditor",
"--bash-completion-for",
"ge" };
616 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(4 + 3), indent.
level);
622 OperationArgument verboseArg(
"verbose",
'v',
"be verbose",
"actually not an operation");
624 ConfigValueArgument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg", {
"value1",
"value2" });
626 Argument subArg(
"sub",
'\0',
"sub arg");
629 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
633 Argument envArg(
"env",
'\0',
"env");
644 const char *
const argv[] = {
"app",
"-h" };
646 const OutputCheck c(
"\e[1m" APP_NAME
", version " APP_VERSION
"\n" 647 "\e[0mLinked against: somelib, some other lib\n" 649 "Available arguments:\n" 650 "\e[1m--help, -h\e[0m\n" 651 " shows this information\n" 652 " particularities: mandatory\n" 654 "\e[1mverbose, -v\e[0m\n" 656 " example: actually not an operation\n" 658 "\e[1m--files, -f\e[0m\n" 659 " specifies the path of the file(s) to be opened\n" 662 " particularities: mandatory if parent argument is present\n" 663 " \e[1m--nested-sub\e[0m [value1] [value2] ...\n" 666 "\e[1m--env\e[0m [file] [value 2]\n" 668 " default environment variable: FILES\n" 670 "Project website: " APP_URL
"\n");
683 Argument subArg(
"sub-arg",
's',
"mandatory sub arg");
688 CPPUNIT_ASSERT_MESSAGE(
"clear main args", parser.
mainArguments().empty());
690 CPPUNIT_ASSERT_MESSAGE(
"no default due to required sub arg", !parser.
defaultArgument());
693 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.
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes...
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.