Improve tests of argument parser
This commit is contained in:
parent
2a797e436d
commit
368fc46e47
|
@ -153,6 +153,7 @@ if(ENABLE_ESCAPE_CODES_BY_DEAULT)
|
|||
set_source_files_properties(
|
||||
application/argumentparser.cpp
|
||||
io/ansiescapecodes.cpp
|
||||
tests/argumentparsertests.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_ESCAPE_CODES_ENABLED_BY_DEFAULT
|
||||
)
|
||||
else()
|
||||
|
|
|
@ -1267,6 +1267,7 @@ void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsi
|
|||
}
|
||||
|
||||
if (opening && openingDenotationType == Abbreviation && !nextArgumentOrValue) {
|
||||
// TODO: add test for this case
|
||||
cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
|
||||
} else if (lastDetectedArg && reader.argDenotationType == Abbreviation && !nextArgumentOrValue) {
|
||||
if (reader.argv == reader.end) {
|
||||
|
|
|
@ -934,6 +934,8 @@ inline ConfigValueArgument::ConfigValueArgument(
|
|||
}
|
||||
|
||||
class CPP_UTILITIES_EXPORT NoColorArgument : public Argument {
|
||||
friend ArgumentParserTests;
|
||||
|
||||
public:
|
||||
NoColorArgument();
|
||||
~NoColorArgument();
|
||||
|
|
|
@ -39,6 +39,7 @@ class ArgumentParserTests : public TestFixture {
|
|||
CPPUNIT_TEST(testBashCompletion);
|
||||
CPPUNIT_TEST(testHelp);
|
||||
CPPUNIT_TEST(testSetMainArguments);
|
||||
CPPUNIT_TEST(testNoColorArgument);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -51,6 +52,7 @@ public:
|
|||
void testBashCompletion();
|
||||
void testHelp();
|
||||
void testSetMainArguments();
|
||||
void testNoColorArgument();
|
||||
|
||||
private:
|
||||
void callback();
|
||||
|
@ -128,6 +130,12 @@ void ArgumentParserTests::testParsing()
|
|||
NoColorArgument noColorArg;
|
||||
parser.setMainArguments({ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &helpArg, &noColorArg });
|
||||
|
||||
// no args present
|
||||
parser.parseArgs(0, nullptr);
|
||||
CPPUNIT_ASSERT(!parser.executable());
|
||||
CPPUNIT_ASSERT(!parser.specifiedOperation());
|
||||
CPPUNIT_ASSERT_EQUAL(0u, parser.actualArgumentCount());
|
||||
|
||||
// error about uncombinable arguments
|
||||
const char *argv[] = { "tageditor", "get", "album", "title", "diskpos", "-f", "somefile" };
|
||||
// try to parse, this should fail
|
||||
|
@ -135,8 +143,15 @@ void ArgumentParserTests::testParsing()
|
|||
parser.parseArgs(7, argv);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"files\" can not be combined with \"fields\"."));
|
||||
CPPUNIT_ASSERT_EQUAL("The argument \"files\" can not be combined with \"fields\"."s, string(e.what()));
|
||||
// test printing btw
|
||||
stringstream ss;
|
||||
ss << e;
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"Error: Unable to parse arguments: The argument \"files\" can not be combined with \"fields\".\nSee --help for available commands.\n"s,
|
||||
ss.str());
|
||||
}
|
||||
CPPUNIT_ASSERT(parser.isUncombinableMainArgPresent());
|
||||
|
||||
// arguments read correctly after successful parse
|
||||
filesArg.setCombinable(true);
|
||||
|
@ -153,6 +168,7 @@ void ArgumentParserTests::testParsing()
|
|||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "title"));
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(2), "diskpos"));
|
||||
CPPUNIT_ASSERT_THROW(displayTagInfoArg.values().at(3), out_of_range);
|
||||
CPPUNIT_ASSERT_EQUAL(&displayTagInfoArg, parser.specifiedOperation());
|
||||
|
||||
// skip empty args
|
||||
const char *argv2[] = { "tageditor", "", "-p", "album", "title", "diskpos", "", "--files", "somefile" };
|
||||
|
@ -473,10 +489,17 @@ void ArgumentParserTests::testBashCompletion()
|
|||
valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
|
||||
valuesArg.setImplicit(false);
|
||||
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
|
||||
Argument selectorsArg("selectors", '\0', "has some more pre-defined values");
|
||||
selectorsArg.setRequiredValueCount(Argument::varValueCount);
|
||||
selectorsArg.setPreDefinedCompletionValues("tag=id3v1 tag=id3v2 tag=matroska target=file target=track");
|
||||
selectorsArg.setImplicit(false);
|
||||
selectorsArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues);
|
||||
selectorsArg.setCallback(
|
||||
[&selectorsArg](const ArgumentOccurrence &) { selectorsArg.setPreDefinedCompletionValues("tag=matroska tag=mp4 tag=vorbis"); });
|
||||
Argument getArg("get", 'g', "gets tag values");
|
||||
getArg.setSubArguments({ &fieldsArg, &filesArg });
|
||||
Argument setArg("set", 's', "sets tag values");
|
||||
setArg.setSubArguments({ &valuesArg, &filesArg });
|
||||
setArg.setSubArguments({ &valuesArg, &filesArg, &selectorsArg });
|
||||
|
||||
parser.setMainArguments({ &helpArg, &displayFileInfoArg, &getArg, &setArg });
|
||||
|
||||
|
@ -510,7 +533,7 @@ void ArgumentParserTests::testBashCompletion()
|
|||
// advance the cursor position -> the completion should propose the next argument
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('--files' '--values' )\n");
|
||||
const OutputCheck c("COMPREPLY=('--files' '--selectors' '--values' )\n");
|
||||
reader.reset(argv2, argv2 + 1).read();
|
||||
parser.printBashCompletion(1, argv2, 1, reader);
|
||||
}
|
||||
|
@ -519,7 +542,7 @@ void ArgumentParserTests::testBashCompletion()
|
|||
parser.resetArgs();
|
||||
filesArg.setDenotesOperation(true);
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('files' '--values' )\n");
|
||||
const OutputCheck c("COMPREPLY=('files' '--selectors' '--values' )\n");
|
||||
reader.reset(argv2, argv2 + 1).read();
|
||||
parser.printBashCompletion(1, argv2, 1, reader);
|
||||
}
|
||||
|
@ -551,6 +574,39 @@ void ArgumentParserTests::testBashCompletion()
|
|||
parser.printBashCompletion(3, argv4, 2, reader);
|
||||
}
|
||||
|
||||
// pre-defined values containing equation sign, equation sign already present
|
||||
const char *const argv12[] = { "set", "--selectors", "tag=id3" };
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('tag=id3v1' 'tag=id3v2' )\n");
|
||||
reader.reset(argv12, argv12 + 3).read();
|
||||
parser.printBashCompletion(3, argv12, 2, reader);
|
||||
}
|
||||
|
||||
// recombining pre-defined values containing equation sign, equation sign already present
|
||||
const char *const argv13[] = { "set", "--selectors", "tag", "=", "id3" };
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('id3v1' 'id3v2' )\n");
|
||||
reader.reset(argv13, argv13 + 5).read();
|
||||
parser.printBashCompletion(5, argv13, 4, reader);
|
||||
}
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('id3v1' 'id3v2' 'matroska' )\n");
|
||||
reader.reset(argv13, argv13 + 5).read();
|
||||
parser.printBashCompletion(5, argv13, 3, reader);
|
||||
}
|
||||
|
||||
// computing pre-defined values just in time using callback
|
||||
selectorsArg.setValueCompletionBehavior(selectorsArg.valueCompletionBehaviour() | ValueCompletionBehavior::InvokeCallback);
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('matroska' 'mp4' 'vorbis' )\n");
|
||||
reader.reset(argv13, argv13 + 5).read();
|
||||
parser.printBashCompletion(5, argv13, 3, reader);
|
||||
}
|
||||
|
||||
// pre-defined values for implicit argument
|
||||
parser.resetArgs();
|
||||
{
|
||||
|
@ -578,7 +634,7 @@ void ArgumentParserTests::testBashCompletion()
|
|||
const char *const argv6[] = { "set", "--" };
|
||||
parser.resetArgs();
|
||||
{
|
||||
const OutputCheck c("COMPREPLY=('--files' '--values' )\n");
|
||||
const OutputCheck c("COMPREPLY=('--files' '--selectors' '--values' )\n");
|
||||
reader.reset(argv6, argv6 + 2).read();
|
||||
parser.printBashCompletion(2, argv6, 1, reader);
|
||||
}
|
||||
|
@ -656,7 +712,11 @@ void ArgumentParserTests::testHelp()
|
|||
verboseArg.setCombinable(true);
|
||||
ConfigValueArgument nestedSubArg("nested-sub", '\0', "nested sub arg", { "value1", "value2" });
|
||||
nestedSubArg.setRequiredValueCount(Argument::varValueCount);
|
||||
Argument subArg("sub", '\0', "sub arg");
|
||||
Argument subArg("foo", 'f', "dummy");
|
||||
subArg.setName("sub");
|
||||
subArg.setAbbreviation('\0');
|
||||
subArg.setDescription("sub arg");
|
||||
subArg.setExample("sub arg example");
|
||||
subArg.setRequired(true);
|
||||
subArg.addSubArgument(&nestedSubArg);
|
||||
Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened");
|
||||
|
@ -692,6 +752,7 @@ void ArgumentParserTests::testHelp()
|
|||
" particularities: mandatory if parent argument is present\n"
|
||||
" \e[1m--nested-sub\e[0m [value1] [value2] ...\n"
|
||||
" nested sub arg\n"
|
||||
" example: sub arg example\n"
|
||||
"\n"
|
||||
"\e[1m--env\e[0m [file] [value 2]\n"
|
||||
" env\n"
|
||||
|
@ -701,6 +762,35 @@ void ArgumentParserTests::testHelp()
|
|||
EscapeCodes::enabled = true;
|
||||
parser.parseArgs(2, argv);
|
||||
}
|
||||
|
||||
verboseArg.setDenotesOperation(false);
|
||||
{
|
||||
const OutputCheck c(APP_NAME ", version " APP_VERSION "\n"
|
||||
"Linked against: somelib, some other lib\n"
|
||||
"\n"
|
||||
"Available arguments:\n"
|
||||
"--verbose, -v\n"
|
||||
" be verbose\n"
|
||||
" example: actually not an operation\n"
|
||||
"\n"
|
||||
"--files, -f\n"
|
||||
" specifies the path of the file(s) to be opened\n"
|
||||
" --sub\n"
|
||||
" sub arg\n"
|
||||
" particularities: mandatory if parent argument is present\n"
|
||||
" --nested-sub [value1] [value2] ...\n"
|
||||
" nested sub arg\n"
|
||||
" example: sub arg example\n"
|
||||
"\n"
|
||||
"--env [file] [value 2]\n"
|
||||
" env\n"
|
||||
" default environment variable: FILES\n"
|
||||
"\n"
|
||||
"Project website: " APP_URL "\n");
|
||||
EscapeCodes::enabled = false;
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(2, argv);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -722,3 +812,37 @@ void ArgumentParserTests::testSetMainArguments()
|
|||
parser.setMainArguments({ &helpArg });
|
||||
CPPUNIT_ASSERT_MESSAGE("default if no required sub arg", &helpArg == parser.defaultArgument());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Tests whether NocolorArgument toggles escape codes correctly.
|
||||
*/
|
||||
void ArgumentParserTests::testNoColorArgument()
|
||||
{
|
||||
// assume escape codes are enabled by default
|
||||
EscapeCodes::enabled = true;
|
||||
|
||||
// ensure the initialization is not skipped
|
||||
NoColorArgument::s_instance = nullptr;
|
||||
|
||||
{
|
||||
unsetenv("ENABLE_ESCAPE_CODES");
|
||||
NoColorArgument noColorArg;
|
||||
noColorArg.apply();
|
||||
CPPUNIT_ASSERT_MESSAGE("default used if not present", EscapeCodes::enabled);
|
||||
noColorArg.m_occurrences.emplace_back(0);
|
||||
noColorArg.apply();
|
||||
CPPUNIT_ASSERT_MESSAGE("default negated if present", !EscapeCodes::enabled);
|
||||
const NoColorArgument secondInstance;
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("s_instance not altered by 2nd instance", &noColorArg, NoColorArgument::s_instance);
|
||||
}
|
||||
{
|
||||
setenv("ENABLE_ESCAPE_CODES", " 0 ", 1);
|
||||
const NoColorArgument noColorArg;
|
||||
CPPUNIT_ASSERT(!EscapeCodes::enabled);
|
||||
}
|
||||
{
|
||||
setenv("ENABLE_ESCAPE_CODES", " 1 ", 1);
|
||||
const NoColorArgument noColorArg;
|
||||
CPPUNIT_ASSERT(EscapeCodes::enabled);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue