3 #include "../conversion/stringbuilder.h" 5 #include "../application/argumentparser.h" 6 #include "../application/argumentparserprivate.h" 7 #include "../application/commandlineutils.h" 8 #include "../application/failure.h" 9 #include "../application/fakeqtconfigarguments.h" 11 #include "../io/path.h" 13 #include "resources/config.h" 15 #include <cppunit/TestFixture.h> 16 #include <cppunit/extensions/HelperMacros.h> 41 const function<void(const string &output)> m_customCheck;
42 const string m_expectedOutput;
43 const string m_alternativeOutput;
44 stringstream m_buffer;
45 streambuf *
const m_regularCoutBuffer;
52 : m_expectedOutput(expectedOutput)
54 , m_regularCoutBuffer(cout.rdbuf(m_buffer.rdbuf()))
62 : m_expectedOutput(expectedOutput)
63 , m_alternativeOutput(alternativeOutput)
65 , m_regularCoutBuffer(cout.rdbuf(m_buffer.rdbuf()))
73 : m_customCheck(customCheck)
75 , m_regularCoutBuffer(cout.rdbuf(m_buffer.rdbuf()))
84 cout.rdbuf(m_regularCoutBuffer);
86 m_customCheck(m_buffer.str());
89 if (m_alternativeOutput.empty()) {
90 CPPUNIT_ASSERT_EQUAL(m_expectedOutput, m_buffer.str());
93 const string actualOutput(m_buffer.str());
94 if (m_expectedOutput != actualOutput && m_alternativeOutput != actualOutput) {
95 CPPUNIT_FAIL(
"Output is not either \"" % m_expectedOutput %
"\" or \"" % m_alternativeOutput %
"\". Got instead:\n" + actualOutput);
104 CPPUNIT_TEST(testArgument);
105 CPPUNIT_TEST(testParsing);
106 CPPUNIT_TEST(testCallbacks);
107 CPPUNIT_TEST(testBashCompletion);
108 CPPUNIT_TEST(testHelp);
109 CPPUNIT_TEST(testSetMainArguments);
110 CPPUNIT_TEST_SUITE_END();
118 void testCallbacks();
119 void testBashCompletion();
121 void testSetMainArguments();
142 Argument argument(
"test",
't',
"some description");
143 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
false);
145 CPPUNIT_ASSERT_EQUAL(argument.
isRequired(),
true);
146 Argument subArg(
"sub",
's',
"sub arg");
148 CPPUNIT_ASSERT_EQUAL(subArg.parents().at(0), &argument);
149 CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
152 setenv(
"FOO_ENV_VAR",
"foo",
true);
153 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"foo"));
155 occurrence.
values.emplace_back(
"bar");
156 argument.m_occurrences.emplace_back(move(occurrence));
157 CPPUNIT_ASSERT(!strcmp(argument.
firstValue(),
"bar"));
170 Argument verboseArg(
"verbose",
'v',
"be verbose");
172 Argument fileArg(
"file",
'f',
"specifies the path of the file to be opened");
176 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
179 Argument outputFileArg(
"output-file",
'o',
"specifies the path of the output file");
184 Argument printFieldNamesArg(
"print-field-names",
'\0',
"prints available field names");
185 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
186 Argument notAlbumArg(
"album",
'a',
"should not be confused with album value");
188 displayFileInfoArg.
setSubArguments({ &fileArg, &verboseArg, ¬AlbumArg });
189 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
191 fieldsArg.
setValueNames({
"title",
"album",
"artist",
"trackpos" });
193 Argument displayTagInfoArg(
"get",
'p',
"displays the values of all specified tag fields (displays all fields if none specified)");
195 displayTagInfoArg.
setSubArguments({ &fieldsArg, &filesArg, &verboseArg, ¬AlbumArg });
196 parser.
setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg });
199 const char *argv[] = {
"tageditor",
"get",
"album",
"title",
"diskpos",
"-f",
"somefile" };
203 CPPUNIT_FAIL(
"Exception expected.");
205 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"files\" can not be combined with \"fields\"."));
213 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
214 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
215 CPPUNIT_ASSERT(!strcmp(parser.
executable(),
"tageditor"));
217 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
219 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
220 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
221 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
222 CPPUNIT_ASSERT_THROW(displayTagInfoArg.
values().at(3), out_of_range);
225 const char *argv2[] = {
"tageditor",
"",
"-p",
"album",
"title",
"diskpos",
"",
"--files",
"somefile" };
230 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
231 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
233 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
235 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album"));
236 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
237 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
238 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(3),
""));
239 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(4), out_of_range);
241 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
244 const char *argv3[] = {
"tageditor",
"album",
"title",
"diskpos",
"--files",
"somefile" };
248 CPPUNIT_FAIL(
"Exception expected.");
250 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown."s,
string(e.
what()));
257 streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
262 cerr.rdbuf(regularCerrBuffer);
265 cerr.rdbuf(regularCerrBuffer);
266 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown and will be ignored.\n"s
267 "The specified argument \"title\" is unknown and will be ignored.\n"s
268 "The specified argument \"diskpos\" is unknown and will be ignored.\n"s
269 "The specified argument \"--files\" is unknown and will be ignored.\n"s
270 "The specified argument \"somefile\" is unknown and will be ignored.\n"s,
273 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
274 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
275 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
280 const char *argv4[] = {
"tageditor",
"-i",
"-vf",
"test" };
284 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
285 CPPUNIT_ASSERT(displayFileInfoArg.
isPresent());
287 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
290 CPPUNIT_ASSERT(!strcmp(fileArg.
values().at(0),
"test"));
291 CPPUNIT_ASSERT_THROW(fileArg.
values().at(1), out_of_range);
297 CPPUNIT_FAIL(
"Exception expected.");
299 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
300 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" mustn't be specified more than 1 time."));
307 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
313 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
316 const char *argv5[] = {
"tageditor",
"-i",
"-f",
"test" };
320 CPPUNIT_FAIL(
"Exception expected.");
322 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
323 CPPUNIT_ASSERT(!strcmp(e.
what(),
"The argument \"verbose\" must be specified at least 1 time."));
328 const char *argv10[] = {
"tageditor",
"-pf",
"test" };
331 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
332 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
335 CPPUNIT_ASSERT_EQUAL(filesArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
336 CPPUNIT_ASSERT(!strcmp(filesArg.
values(0).front(),
"test"));
337 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
340 const char *argv6[] = {
"tageditor",
"-g" };
343 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
346 const char *argv7[] = {
"tageditor",
"-f",
"test" };
350 CPPUNIT_FAIL(
"Exception expected.");
352 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
353 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"-f\" is unknown."s,
string(e.
what()));
357 const char *argv11[] = {
"tageditor",
"-if=test" };
362 CPPUNIT_ASSERT_EQUAL(fileArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
363 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
366 const char *argv12[] = {
"tageditor",
"-iftest" };
371 CPPUNIT_ASSERT_EQUAL(fileArg.
values(0).size(),
static_cast<vector<const char *>::size_type
>(1));
372 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
375 const char *argv8[] = {
"tageditor" };
378 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
379 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
381 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
384 if (getenv(
"PATH")) {
386 CPPUNIT_ASSERT(!strcmp(fileArg.
firstValue(), getenv(
"PATH")));
392 const char *argv13[] = {
"tageditor",
"get",
"--fields",
"album=test",
"title",
"diskpos",
"--files",
"somefile" };
397 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
398 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
400 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
402 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
403 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"title"));
404 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(2),
"diskpos"));
405 CPPUNIT_ASSERT_THROW(fieldsArg.
values().at(3), out_of_range);
407 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
408 CPPUNIT_ASSERT(!notAlbumArg.
isPresent());
411 const char *argv9[] = {
"tageditor",
"-p",
"album",
"title",
"diskpos" };
416 CPPUNIT_FAIL(
"Exception expected.");
418 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
419 CPPUNIT_ASSERT(!strcmp(e.
what(),
420 "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
424 const char *argv14[] = {
"tageditor",
"get",
"fields",
"album=test",
"-f",
"somefile" };
429 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
431 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
437 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
439 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"fields"));
440 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"album=test"));
449 Argument callbackArg(
"with-callback",
't',
"callback test");
452 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), occurrence.
index);
453 CPPUNIT_ASSERT(occurrence.
path.empty());
454 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), occurrence.
values.size());
455 CPPUNIT_ASSERT(!strcmp(occurrence.
values[0],
"val1"));
456 CPPUNIT_ASSERT(!strcmp(occurrence.
values[1],
"val2"));
459 Argument noCallbackArg(
"no-callback",
'l',
"callback test");
464 const char *argv[] = {
"test",
"-t",
"val1",
"val2" };
468 CPPUNIT_ASSERT_EQUAL(i, 42);
473 const char *argv2[] = {
"test",
"-l",
"val1",
"val2" };
480 static bool exitCalled =
false;
490 Argument verboseArg(
"verbose",
'v',
"be verbose");
492 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
495 Argument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg");
496 Argument subArg(
"sub",
'\0',
"sub arg");
498 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
500 displayFileInfoArg.
setSubArguments({ &filesArg, &verboseArg, &subArg });
501 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
505 Argument valuesArg(
"values",
'\0',
"specifies the fields");
509 valuesArg.
setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
510 Argument getArg(
"get",
'g',
"gets tag values");
512 Argument setArg(
"set",
's',
"sets tag values");
515 parser.
setMainArguments({ &helpArg, &displayFileInfoArg, &getArg, &setArg });
518 const char *
const argv1[] = {
"se" };
523 parser.printBashCompletion(1, argv1, 0, reader);
532 parser.printBashCompletion(1, argv1, 0, reader);
536 const char *
const argv2[] = {
"set" };
541 parser.printBashCompletion(1, argv2, 0, reader);
549 parser.printBashCompletion(1, argv2, 1, reader);
558 parser.printBashCompletion(1, argv2, 1, reader);
567 parser.printBashCompletion(0,
nullptr, 0, reader);
571 const char *
const argv3[] = {
"get",
"--fields" };
574 const StandardOutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n");
576 parser.printBashCompletion(2, argv3, 2, reader);
580 const char *
const argv4[] = {
"set",
"--values",
"a" };
585 parser.printBashCompletion(3, argv4, 2, reader);
591 const StandardOutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' )\n");
593 parser.printBashCompletion(1, argv3, 2, reader);
598 iniFilePath.resize(iniFilePath.size() - 4);
600 mkvFilePath.resize(mkvFilePath.size() - 17);
602 const char *
const argv5[] = {
"get",
"--files", iniFilePath.c_str() };
606 "COMPREPLY=('" % mkvFilePath %
" '\"'\"'with quote'\"'\"'.mkv' '" % iniFilePath +
".ini' ); compopt -o filenames\n",
607 "COMPREPLY=('" % iniFilePath %
".ini' '" % mkvFilePath +
" '\"'\"'with quote'\"'\"'.mkv' ); compopt -o filenames\n");
609 parser.printBashCompletion(3, argv5, 2, reader);
613 const char *
const argv6[] = {
"set",
"--" };
618 parser.printBashCompletion(2, argv6, 1, reader);
622 const char *
const argv7[] = {
"-i",
"--sub",
"--" };
627 parser.printBashCompletion(3, argv7, 2, reader);
631 const char *
const argv8[] = {
"set",
"--values",
"t" };
636 parser.printBashCompletion(3, argv8, 2, reader);
640 const char *
const argv9[] = {
"-gf" };
645 parser.printBashCompletion(1, argv9, 0, reader);
649 const StandardOutputCheck c([](
const string &actualOutput) { CPPUNIT_ASSERT_EQUAL(0_st, actualOutput.find(
"COMPREPLY=('--fields' ")); });
651 parser.printBashCompletion(1, argv9, 1, reader);
658 const char *
const argv10[] = {
"/some/path/tageditor",
"--bash-completion-for",
"0" };
664 CPPUNIT_ASSERT(!strcmp(
"/some/path/tageditor", parser.
executable()));
665 CPPUNIT_ASSERT(exitCalled);
668 const char *
const argv11[] = {
"/some/path/tageditor",
"--bash-completion-for",
"ge" };
684 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(4 + 3), indent.
level);
690 OperationArgument verboseArg(
"verbose",
'v',
"be verbose",
"actually not an operation");
692 ConfigValueArgument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg", {
"value1",
"value2" });
694 Argument subArg(
"sub",
'\0',
"sub arg");
697 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
701 Argument envArg(
"env",
'\0',
"env");
712 const char *
const argv[] = {
"app",
"-h" };
715 "\e[0mLinked against: somelib, some other lib\n" 717 "Available arguments:\n" 718 "\e[1m--help, -h\e[0m\n" 719 " shows this information\n" 720 " particularities: mandatory\n" 722 "\e[1m--verbose, -v\e[0m\n" 725 "usage: actually not an operation\n" 727 "\e[1m--files, -f\e[0m\n" 728 " specifies the path of the file(s) to be opened\n" 731 " particularities: mandatory if parent argument is present\n" 732 " \e[1m--nested-sub\e[0m [value1] [value2] ...\n" 735 "\e[1m--env\e[0m [file] [value 2]\n" 737 " default environment variable: FILES\n" 739 "Project website: " APP_URL
"\n");
751 Argument subArg(
"sub-arg",
's',
"mandatory sub arg");
756 CPPUNIT_ASSERT_MESSAGE(
"clear main args", parser.
mainArguments().empty());
758 CPPUNIT_ASSERT_MESSAGE(
"no default due to required sub arg", !parser.
defaultArgument());
761 CPPUNIT_ASSERT_MESSAGE(
"default if no required sub arg", &helpArg == parser.
defaultArgument());
~StandardOutputCheck()
Asserts the buffered standard output and restores the regular behaviour of std::cout.
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.
The StandardOutputCheck class asserts whether the standard output written in the enclosing code block...
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.
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).
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.
StandardOutputCheck(const string &expectedOutput)
Redirects standard output to an internal buffer.
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.