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();
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));
103 CPPUNIT_ASSERT(!subArg.conflictsWithArgument());
106 #ifndef PLATFORM_WINDOWS // disabled under Windows for same reason as testNoColorArgument()
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 parameter for argument \"fields\" provided. You have to provide the following parameter: 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);
674 string directoryPath = iniFilePath.substr(0, iniFilePath.size() - 4);
677 const char *
const argv14[] = {
"get",
"--files", directoryPath.c_str() };
679 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
681 const OutputCheck c(
"COMPREPLY=('" % directoryPath +
"subdir' ); 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");
874 subArg.setRequired(
true);
875 helpArg.addSubArgument(&subArg);
878 CPPUNIT_ASSERT_MESSAGE(
"clear main args", parser.
mainArguments().empty());
880 CPPUNIT_ASSERT_MESSAGE(
"no default due to required sub arg", !parser.
defaultArgument());
881 subArg.setConstraints(0, 20);
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()));