4 #include "../conversion/stringbuilder.h"
5 #include "../conversion/stringconversion.h"
7 #include "../application/argumentparser.h"
8 #include "../application/argumentparserprivate.h"
9 #include "../application/commandlineutils.h"
10 #include "../application/fakeqtconfigarguments.h"
12 #include "../io/ansiescapecodes.h"
13 #include "../io/path.h"
15 #include "../misc/parseerror.h"
17 #include "resources/config.h"
19 #include <cppunit/TestFixture.h>
20 #include <cppunit/extensions/HelperMacros.h>
25 #ifdef PLATFORM_WINDOWS
33 using namespace CPPUNIT_NS;
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);
50 CPPUNIT_TEST_SUITE_END();
53 void setUp()
override;
54 void tearDown()
override;
59 void testSetMainArguments();
60 void testValueConversion();
61 #ifndef PLATFORM_WINDOWS
62 void testBashCompletion();
64 void testNoColorArgument();
69 [[noreturn]]
void failOnExit(
int code);
76 #ifndef PLATFORM_WINDOWS
77 setenv(
"ENABLE_ESCAPE_CODES",
"0", 1);
86 [[noreturn]]
void ArgumentParserTests::failOnExit(
int code)
88 CPPUNIT_FAIL(
argsToString(
"Exited unexpectedly with code ", code));
96 Argument argument(
"test",
't',
"some description");
97 CPPUNIT_ASSERT_EQUAL(
false, argument.
isRequired());
99 CPPUNIT_ASSERT_EQUAL(
true, argument.
isRequired());
100 Argument subArg(
"sub",
's',
"sub arg");
102 CPPUNIT_ASSERT_EQUAL(&argument, subArg.
parents().at(0));
106 #ifndef PLATFORM_WINDOWS
107 setenv(
"FOO_ENV_VAR",
"foo", 1);
108 CPPUNIT_ASSERT_EQUAL(
"foo"s,
string(argument.
firstValue()));
111 occurrence.
values.emplace_back(
"bar");
112 argument.m_occurrences.emplace_back(move(occurrence));
113 CPPUNIT_ASSERT_EQUAL(
"bar"s,
string(argument.
firstValue()));
123 parser.
setExitFunction(std::bind(&ArgumentParserTests::failOnExit,
this, std::placeholders::_1));
126 Argument verboseArg(
"verbose",
'v',
"be verbose");
128 Argument fileArg(
"file",
'f',
"specifies the path of the file to be opened");
132 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
135 Argument outputFileArg(
"output-file",
'o',
"specifies the path of the output file");
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");
144 displayFileInfoArg.
setSubArguments({ &fileArg, &verboseArg, ¬AlbumArg });
145 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
147 fieldsArg.
setValueNames({
"title",
"album",
"artist",
"trackpos" });
149 Argument displayTagInfoArg(
"get",
'p',
"displays the values of all specified tag fields (displays all fields if none specified)");
151 displayTagInfoArg.
setSubArguments({ &fieldsArg, &filesArg, &verboseArg, ¬AlbumArg });
153 { &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &parser.
noColorArg(), &parser.
helpArg() });
156 parser.
parseArgs(0,
nullptr, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
162 const char *argv[] = {
"tageditor",
"get",
"album",
"title",
"diskpos",
"-f",
"somefile" };
165 parser.
parseArgs(7, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
166 CPPUNIT_FAIL(
"Exception expected.");
168 CPPUNIT_ASSERT_EQUAL(
"The argument \"files\" can not be combined with \"fields\"."s,
string(e.what()));
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,
181 parser.
parseArgs(7, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
183 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
184 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
185 CPPUNIT_ASSERT(!strcmp(parser.
executable(),
"tageditor"));
187 CPPUNIT_ASSERT(displayTagInfoArg.
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);
196 const char *argv2[] = {
"tageditor",
"",
"-p",
"album",
"title",
"diskpos",
"",
"--files",
"somefile" };
199 parser.
parseArgs(9, argv2, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
201 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
202 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
204 CPPUNIT_ASSERT(displayTagInfoArg.
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);
212 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
215 const char *argv3[] = {
"tageditor",
"album",
"title",
"diskpos",
"--files",
"somefile" };
218 parser.
parseArgs(6, argv3, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
219 CPPUNIT_FAIL(
"Exception expected.");
221 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"album\" is unknown.\nDid you mean get or --help?"s,
string(e.what()));
225 const char *argv18[] = {
"tageditor",
"get",
"album",
"title",
"diskpos",
"--verbose",
"--fi" };
228 parser.
parseArgs(7, argv18, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
229 CPPUNIT_FAIL(
"Exception expected.");
231 CPPUNIT_ASSERT_EQUAL(
"The specified argument \"--fi\" is unknown.\nDid you mean --files or --no-color?"s,
string(e.what()));
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,
247 parser.
parseArgs(6, argv3, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
250 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
251 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
252 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
258 const char *argv4[] = {
"tageditor",
"-i",
"-vf",
"test" };
261 parser.
parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
262 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
263 CPPUNIT_ASSERT(displayFileInfoArg.
isPresent());
265 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
268 CPPUNIT_ASSERT(!strcmp(fileArg.
values().at(0),
"test"));
269 CPPUNIT_ASSERT_THROW(fileArg.
values().at(1), out_of_range);
272 displayFileInfoArg.
reset();
275 parser.
parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
276 CPPUNIT_FAIL(
"Exception expected.");
278 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
279 CPPUNIT_ASSERT(!strcmp(e.what(),
"The argument \"verbose\" mustn't be specified more than 1 time."));
283 displayFileInfoArg.
reset();
286 parser.
parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
287 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
292 parser.
parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
293 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
296 const char *argv5[] = {
"tageditor",
"-i",
"-f",
"test" };
297 displayFileInfoArg.
reset();
301 parser.
parseArgs(4, argv5, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
302 CPPUNIT_FAIL(
"Exception expected.");
304 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
305 CPPUNIT_ASSERT(!strcmp(e.what(),
"The argument \"verbose\" must be specified at least 1 time."));
310 const char *argv10[] = {
"tageditor",
"-pf",
"test" };
312 parser.
parseArgs(3, argv10, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
313 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
314 CPPUNIT_ASSERT(!displayFileInfoArg.
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());
322 const char *argv6[] = {
"tageditor",
"-g" };
324 parser.
parseArgs(2, argv6, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
325 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
328 const char *argv7[] = {
"tageditor",
"-f",
"test" };
331 parser.
parseArgs(3, argv7, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
332 CPPUNIT_FAIL(
"Exception expected.");
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()));
339 const char *argv11[] = {
"tageditor",
"-if=test-v" };
341 parser.
parseArgs(2, argv11, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
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" };
349 parser.
parseArgs(4, argv15, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
353 CPPUNIT_ASSERT_EQUAL(1_st, fileArg.
values(0).size());
354 CPPUNIT_ASSERT_EQUAL(
"test"s,
string(fileArg.
values(0).front()));
357 const char *argv12[] = {
"tageditor",
"-iftest" };
359 parser.
parseArgs(2, argv12, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
362 CPPUNIT_ASSERT_EQUAL(1_st, fileArg.
values(0).size());
363 CPPUNIT_ASSERT(!strcmp(fileArg.
values(0).front(),
"test"));
366 const char *argv17[] = {
"tageditor",
"-if=test-v",
"--no-color" };
368 parser.
parseArgs(3, argv17, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
373 CPPUNIT_ASSERT_EQUAL(1_st, fileArg.
values(0).size());
374 CPPUNIT_ASSERT_EQUAL(
"test-v"s,
string(fileArg.
values(0).front()));
377 const char *argv8[] = {
"tageditor" };
379 parser.
parseArgs(1, argv8, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
380 CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
381 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
383 CPPUNIT_ASSERT(!displayTagInfoArg.
isPresent());
386 if (getenv(
"PATH")) {
388 CPPUNIT_ASSERT(!strcmp(fileArg.
firstValue(), getenv(
"PATH")));
394 const char *argv13[] = {
"tageditor",
"get",
"--fields",
"album=test",
"title",
"diskpos",
"--files",
"somefile" };
397 parser.
parseArgs(8, argv13, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
399 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
400 CPPUNIT_ASSERT(!displayFileInfoArg.
isPresent());
402 CPPUNIT_ASSERT(displayTagInfoArg.
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);
409 CPPUNIT_ASSERT(!strcmp(filesArg.
values().at(0),
"somefile"));
410 CPPUNIT_ASSERT(!notAlbumArg.
isPresent());
413 const char *argv9[] = {
"tageditor",
"-p",
"album",
"title",
"diskpos" };
417 parser.
parseArgs(5, argv9, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
418 CPPUNIT_FAIL(
"Exception expected.");
420 CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
421 CPPUNIT_ASSERT_EQUAL(
422 "Not all parameters for argument \"fields\" provided. You have to provide the following parameters: title album artist trackpos"s,
427 const char *argv16[] = {
"tageditor",
"--hel",
"-p",
"album",
"title",
"diskpos" };
431 parser.
parseArgs(6, argv16, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
432 CPPUNIT_FAIL(
"Exception expected.");
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()));
439 const char *argv14[] = {
"tageditor",
"get",
"fields",
"album=test",
"-f",
"somefile" };
442 parser.
parseArgs(6, argv14, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
443 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
445 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"album=test"));
450 parser.
parseArgs(6, argv14, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
451 CPPUNIT_ASSERT(displayTagInfoArg.
isPresent());
453 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(0),
"fields"));
454 CPPUNIT_ASSERT(!strcmp(fieldsArg.
values().at(1),
"album=test"));
463 parser.
setExitFunction(std::bind(&ArgumentParserTests::failOnExit,
this, std::placeholders::_1));
464 Argument callbackArg(
"with-callback",
't',
"callback test");
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"));
474 Argument noCallbackArg(
"no-callback",
'l',
"callback test");
479 const char *argv[] = {
"test",
"-t",
"val1",
"val2" };
481 parser.
parseArgs(4, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
483 CPPUNIT_ASSERT_EQUAL(
i, 42);
488 const char *argv2[] = {
"test",
"-l",
"val1",
"val2" };
489 parser.
parseArgs(4, argv2, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
492 #ifndef PLATFORM_WINDOWS
496 static bool exitCalled =
false;
507 parser.
setExitFunction(std::bind(&ArgumentParserTests::failOnExit,
this, std::placeholders::_1));
508 Argument verboseArg(
"verbose",
'v',
"be verbose");
510 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
513 Argument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg");
514 Argument subArg(
"sub",
'\0',
"sub arg");
516 Argument displayFileInfoArg(
"display-file-info",
'i',
"displays general file information");
518 displayFileInfoArg.
setSubArguments({ &filesArg, &verboseArg, &subArg });
519 Argument fieldsArg(
"fields",
'\0',
"specifies the fields");
523 Argument valuesArg(
"values",
'\0',
"specifies the fields");
527 valuesArg.
setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
528 Argument selectorsArg(
"selectors",
'\0',
"has some more pre-defined values");
535 Argument getArg(
"get",
'g',
"gets tag values");
537 Argument setArg(
"set",
's',
"sets tag values");
543 const char *
const argv1[] = {
"se" };
547 CPPUNIT_ASSERT(reader.
read());
548 parser.printBashCompletion(1, argv1, 0, reader);
557 parser.printBashCompletion(1, argv1, 0, reader);
561 const char *
const argv2[] = {
"set" };
566 parser.printBashCompletion(1, argv2, 0, reader);
572 const OutputCheck c(
"COMPREPLY=('--files' '--no-color' '--selectors' '--values' )\n");
574 parser.printBashCompletion(1, argv2, 1, reader);
581 const OutputCheck c(
"COMPREPLY=('files' '--no-color' '--selectors' '--values' )\n");
583 parser.printBashCompletion(1, argv2, 1, reader);
590 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' '--no-color' )\n");
592 parser.printBashCompletion(0,
nullptr, 0, reader);
596 const char *
const argv3[] = {
"get",
"--fields" };
599 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' '--no-color' )\n");
601 parser.printBashCompletion(2, argv3, 2, reader);
605 const char *
const argv4[] = {
"set",
"--values",
"a" };
608 const OutputCheck c(
"COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n");
610 parser.printBashCompletion(3, argv4, 2, reader);
614 const char *
const argv12[] = {
"set",
"--selectors",
"tag=id3" };
617 const OutputCheck c(
"COMPREPLY=('tag=id3v1' 'tag=id3v2' )\n");
619 parser.printBashCompletion(3, argv12, 2, reader);
623 const char *
const argv13[] = {
"set",
"--selectors",
"tag",
"=",
"id3" };
626 const OutputCheck c(
"COMPREPLY=('id3v1' 'id3v2' )\n");
628 parser.printBashCompletion(5, argv13, 4, reader);
632 const OutputCheck c(
"COMPREPLY=('id3v1' 'id3v2' 'matroska' )\n");
634 parser.printBashCompletion(5, argv13, 3, reader);
641 const OutputCheck c(
"COMPREPLY=('matroska' 'mp4' 'vorbis' )\n");
643 parser.printBashCompletion(5, argv13, 3, reader);
649 const OutputCheck c(
"COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' '--no-color' )\n");
651 parser.printBashCompletion(1, argv3, 2, reader);
656 iniFilePath.resize(iniFilePath.size() - 4);
658 mkvFilePath.resize(mkvFilePath.size() - 17);
660 const char *
const argv5[] = {
"get",
"--files", iniFilePath.c_str() };
662 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
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");
670 parser.printBashCompletion(3, argv5, 2, reader);
675 directoryPath.resize(directoryPath.size() - 4);
678 const char *
const argv14[] = {
"get",
"--files", directoryPath.c_str() };
680 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
681 const OutputCheck c(
"COMPREPLY=('" % directoryPath +
"' ); compopt -o filenames\n");
686 parser.printBashCompletion(3, argv14, 2, reader);
690 const char *
const argv6[] = {
"set",
"--" };
693 const OutputCheck c(
"COMPREPLY=('--files' '--no-color' '--selectors' '--values' )\n");
695 parser.printBashCompletion(2, argv6, 1, reader);
699 const char *
const argv7[] = {
"-i",
"--sub",
"--" };
702 const OutputCheck c(
"COMPREPLY=('--files' '--nested-sub' '--no-color' '--verbose' )\n");
704 parser.printBashCompletion(3, argv7, 2, reader);
708 const char *
const argv8[] = {
"set",
"--values",
"t" };
711 const OutputCheck c(
"COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace\n");
713 parser.printBashCompletion(3, argv8, 2, reader);
717 const char *
const argv9[] = {
"-gf" };
722 parser.printBashCompletion(1, argv9, 0, reader);
726 const OutputCheck c([](
const string &actualOutput) { CPPUNIT_ASSERT_EQUAL(0_st, actualOutput.find(
"COMPREPLY=('--fields' ")); });
728 parser.printBashCompletion(1, argv9, 1, reader);
735 const char *
const argv10[] = {
"/some/path/tageditor",
"--bash-completion-for",
"0" };
738 const OutputCheck c(
"COMPREPLY=('display-file-info' 'get' 'set' '--help' '--no-color' )\n");
741 CPPUNIT_ASSERT(!strcmp(
"/some/path/tageditor", parser.
executable()));
742 CPPUNIT_ASSERT(exitCalled);
745 const char *
const argv11[] = {
"/some/path/tageditor",
"--bash-completion-for",
"ge" };
754 #ifndef PLATFORM_WINDOWS
764 CPPUNIT_ASSERT_EQUAL(
static_cast<unsigned char>(4 + 3), indent.
level);
768 parser.
setExitFunction(std::bind(&ArgumentParserTests::failOnExit,
this, std::placeholders::_1));
769 OperationArgument verboseArg(
"verbose",
'v',
"be verbose",
"actually not an operation");
771 ConfigValueArgument nestedSubArg(
"nested-sub",
'\0',
"nested sub arg", {
"value1",
"value2" });
773 Argument subArg(
"foo",
'f',
"dummy");
780 Argument filesArg(
"files",
'f',
"specifies the path of the file(s) to be opened");
784 Argument envArg(
"env",
'\0',
"env");
788 Argument deprecatedArg(
"deprecated");
795 const char *
const argv[] = {
"app",
"-h" };
797 const OutputCheck c(
"\e[1m" APP_NAME
", version " APP_VERSION
"\n"
799 "\e[0m" APP_DESCRIPTION
"\n"
801 "Available operations:\n"
802 "\e[1mverbose, -v\e[0m\n"
804 " example: actually not an operation\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"
811 " particularities: mandatory if parent argument is present\n"
812 " \e[1m--nested-sub\e[0m [value1] [value2] ...\n"
814 " example: sub arg example\n"
816 "\e[1m--env\e[0m [file] [value 2]\n"
818 " default environment variable: FILES\n"
820 "\e[1m--no-color\e[0m\n"
821 " disables formatted/colorized output\n"
822 " default environment variable: ENABLE_ESCAPE_CODES\n"
824 "Linked against: somelib, some other lib\n"
826 "Project website: " APP_URL
"\n");
828 parser.
parseArgs(2, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
834 const OutputCheck c(APP_NAME
", version " APP_VERSION
"\n"
835 "\n" APP_DESCRIPTION
"\n"
837 "Available arguments:\n"
840 " example: actually not an operation\n"
843 " specifies the path of the file(s) to be opened\n"
846 " particularities: mandatory if parent argument is present\n"
847 " --nested-sub [value1] [value2] ...\n"
849 " example: sub arg example\n"
851 "--env [file] [value 2]\n"
853 " default environment variable: FILES\n"
855 "Linked against: somelib, some other lib\n"
857 "Project website: " APP_URL
"\n");
860 parser.
parseArgs(2, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
871 parser.
setExitFunction(std::bind(&ArgumentParserTests::failOnExit,
this, std::placeholders::_1));
873 Argument subArg(
"sub-arg",
's',
"mandatory sub arg");
878 CPPUNIT_ASSERT_MESSAGE(
"clear main args", parser.
mainArguments().empty());
880 CPPUNIT_ASSERT_MESSAGE(
"no default due to required sub arg", !parser.
defaultArgument());
883 CPPUNIT_ASSERT_MESSAGE(
"default if no required sub arg", &helpArg == parser.
defaultArgument());
886 #ifndef PLATFORM_WINDOWS
900 unsetenv(
"ENABLE_ESCAPE_CODES");
904 noColorArg.m_occurrences.emplace_back(0);
909 setenv(
"ENABLE_ESCAPE_CODES",
" 0 ", 1);
914 setenv(
"ENABLE_ESCAPE_CODES",
" 1 ", 1);
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));
936 occurrence.
values = {
"foo",
"42",
"7.5",
"-42" };
938 static_assert(std::is_same<std::tuple<>, decltype(occurrence.
convertValues<>())>::value,
"specifying no types yields empty tuple");
942 arg.m_occurrences = { occurrence, occurrence };
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) {
950 static_assert(std::is_same<std::tuple<>, decltype(arg.
valuesAs<>())>::value,
"specifying no types yields empty tuple");
954 occurrence.
convertValues<string,
unsigned int, double, int,
int>();
955 CPPUNIT_FAIL(
"Expected exception");
957 CPPUNIT_ASSERT_EQUAL(
"Expected 5 top-level values to be present but only 4 have been specified."s,
string(failure.what()));
961 CPPUNIT_FAIL(
"Expected exception");
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()));
966 occurrence.
path = { &arg };
968 occurrence.
convertValues<string,
unsigned int, double, int,
int>();
969 CPPUNIT_FAIL(
"Expected exception");
971 CPPUNIT_ASSERT_EQUAL(
"Expected 5 values for argument --test to be present but only 4 have been specified."s,
string(failure.what()));
975 CPPUNIT_FAIL(
"Expected exception");
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()));
#define SET_APPLICATION_INFO
Sets application meta data (including SET_DEPENDENCY_INFO) used by ArgumentParser::printHelp().
CPPUNIT_TEST_SUITE_REGISTRATION(ArgumentParserTests)
void checkConvertedValues(const std::string &message, const ValueTuple &values)
The ArgumentParserTests class tests the ArgumentParser and Argument classes.
void testNoColorArgument()
Tests whether NocolorArgument toggles escape codes correctly.
void testCallbacks()
Tests whether callbacks are called correctly.
void testValueConversion()
Tests value conversion provided by Argument and ArgumentOccurrence.
void testArgument()
Tests the behaviour of the argument class.
void testBashCompletion()
Tests bash completion.
void testHelp()
Tests –help output.
void testSetMainArguments()
Tests some corner cases in setMainArguments() which are not already checked in the other tests.
void testParsing()
Tests parsing command line arguments.
The ArgumentParser class provides a means for handling command line arguments.
const HelpArgument & helpArg() const
Returns the --help argument.
const char * executable() const
Returns the name of the current executable.
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const ArgumentVector & mainArguments() const
Returns the main arguments.
unsigned int actualArgumentCount() const
Returns the actual number of arguments that could be found when parsing.
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
void setUnknownArgumentBehavior(UnknownArgumentBehavior behavior)
Sets how unknown arguments are treated.
Argument * defaultArgument() const
Returns the default argument.
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
void setExitFunction(std::function< void(int)> exitFunction)
Specifies a function quit the application.
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
void parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior=ParseArgumentBehavior::CheckConstraints|ParseArgumentBehavior::InvokeCallbacks|ParseArgumentBehavior::ExitOnFailure)
Parses the specified command line arguments.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
const NoColorArgument & noColorArg() const
Returns the --no-color argument.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
The ArgumentReader class internally encapsulates the process of reading command line arguments.
ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
bool read()
Reads the commands line arguments specified when constructing the object.
The Argument class is a wrapper for command line argument information.
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
void setValueCompletionBehavior(ValueCompletionBehavior valueCompletionBehaviour)
Sets the items to be considered when generating completion for the values.
ValueCompletionBehavior valueCompletionBehaviour() const
Returns the items to be considered when generating completion for the values.
void setConstraints(std::size_t minOccurrences, std::size_t maxOccurrences)
Sets the allowed number of occurrences.
void setImplicit(bool implicit)
Sets whether the argument is an implicit argument.
void setValueNames(std::initializer_list< const char * > valueNames)
Sets the names of the required values.
void setDenotesOperation(bool denotesOperation)
Sets whether the argument denotes the operation.
void markAsDeprecated(const Argument *deprecatedBy=nullptr)
Marks the argument as deprecated.
void setAbbreviation(char abbreviation)
Sets the abbreviation of the argument.
void setDescription(const char *description)
Sets the description of the argument.
void appendValueName(const char *valueName)
Appends a value name.
const std::vector< const char * > & values(std::size_t occurrence=0) const
Returns the parameter values for the specified occurrence of argument.
std::vector< std::tuple< TargetType... > > allValuesAs() const
Converts the present values for all occurrence to the specified target types.
void reset()
Resets occurrences (indices, values and paths).
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
void setExample(const char *example)
Sets the a usage example for the argument.
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
const ArgumentVector & parents() const
Returns the parents of this argument.
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
void setCombinable(bool combinable)
Sets whether this argument can be combined.
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
bool isRequired() const
Returns an indication whether the argument is mandatory.
void setRequired(bool required)
Sets whether this argument is mandatory or not.
void setPreDefinedCompletionValues(const char *preDefinedCompletionValues)
Assigns the values to be used when generating completion for the values.
void setEnvironmentVariable(const char *environmentVariable)
Sets the environment variable queried when firstValue() is called.
std::tuple< TargetType... > valuesAs(std::size_t occurrence=0) const
Converts the present values for the specified occurrence to the specified target types.
void setRequiredValueCount(std::size_t requiredValueCount)
Sets the number of values which are required to be given for this argument.
void setName(const char *name)
Sets the name of the argument.
The ConfigValueArgument class is an Argument where setCombinable() is true by default.
The HelpArgument class prints help information for an argument parser when present (–help,...
The Indentation class allows printing indentation conveniently, eg.
The NoColorArgument class allows to specify whether use of escape codes or similar technique to provi...
void apply() const
Sets EscapeCodes::enabled according to the presence of the first instantiation of NoColorArgument.
The OperationArgument class is an Argument where denotesOperation() is true by default.
The StandardOutputCheck class asserts whether the (standard) output written in the enclosing code blo...
The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
#define QT_CONFIG_ARGUMENTS
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
Contains literals to ease asserting with CPPUNIT_ASSERT_EQUAL.
Contains all utilities provides by the c++utilities library.
CPP_UTILITIES_EXPORT std::string testFilePath(const std::string &relativeTestFilePath)
Convenience function to invoke TestApplication::testFilePath().
StringType argsToString(Args &&...args)
CPP_UTILITIES_EXPORT ApplicationInfo applicationInfo
Stores global application info used by ArgumentParser::printHelp() and AboutDialog.
std::vector< const char * > dependencyVersions
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
std::size_t index
The index of the occurrence.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.
std::tuple< RemainingTargetTypes... > convertValues() const
Converts the present values to the specified target types.
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).