Improve ArgumentParser::readSpecifiedArgs()

- Fix minor issues
- Add further test cases
This commit is contained in:
Martchus 2016-10-29 23:54:30 +02:00
parent 97925f1252
commit 5119bb5c6a
4 changed files with 127 additions and 65 deletions

View File

@ -112,7 +112,7 @@ set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities") set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities")
set(META_VERSION_MAJOR 4) set(META_VERSION_MAJOR 4)
set(META_VERSION_MINOR 2) set(META_VERSION_MINOR 2)
set(META_VERSION_PATCH 0) set(META_VERSION_PATCH 1)
# find required 3rd party libraries # find required 3rd party libraries
include(3rdParty) include(3rdParty)

View File

@ -415,7 +415,8 @@ void ArgumentParser::readArgs(int argc, const char * const *argv)
// read specified arguments // read specified arguments
try { try {
const char *const *argv2 = argv; const char *const *argv2 = argv;
readSpecifiedArgs(m_mainArgs, index, argv2, argv + (completionMode ? min(static_cast<unsigned int>(argc), currentWordIndex + 1) : static_cast<unsigned int>(argc)), lastDetectedArgument, completionMode); const char *argDenotation = nullptr;
readSpecifiedArgs(m_mainArgs, index, argv2, argv + (completionMode ? min(static_cast<unsigned int>(argc), currentWordIndex + 1) : static_cast<unsigned int>(argc)), lastDetectedArgument, argDenotation, completionMode);
} catch(const Failure &) { } catch(const Failure &) {
if(!completionMode) { if(!completionMode) {
throw; throw;
@ -479,39 +480,60 @@ void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args
/*! /*!
* \brief Reads the specified commands line arguments. * \brief Reads the specified commands line arguments.
* \remarks Results are stored in Argument instances added as main arguments and sub arguments. * \param args Specifies the Argument instances to store the results. Sub arguments of \a args are considered as well.
* \param index Specifies and index which is incremented when an argument is encountered (the current index is stored in the occurrence) or a value is encountered.
* \param argv Points to the first argument denotation and will be incremented when a denotation has been processed.
* \param end Points to the end of the \a argv array.
* \param lastArg Specifies the last Argument instance which could be detected. Set to nullptr in the initial call. Used for Bash completion.
* \param argDenotation Specifies the currently processed abbreviation denotation (should be substring of \a argv). Set to nullptr for processing \a argv from the beginning (default).
* \param completionMode Specifies whether completion mode is enabled. In this case reading args will be continued even if an denotation is unknown (regardless of unknownArgumentBehavior()).
* \remarks Results are stored in specified \a args and assigned sub arguments.
*/ */
void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, Argument *&lastArg, bool completionMode) void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, Argument *&lastArg, const char *&argDenotation, bool completionMode)
{ {
// method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
Argument *const parentArg = lastArg; Argument *const parentArg = lastArg;
// determine the current path
const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>(); const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>();
Argument *lastArgInLevel = nullptr; Argument *lastArgInLevel = nullptr;
vector<const char *> *values = nullptr; vector<const char *> *values = nullptr;
// iterate through all argument denotations; loop might exit earlier when an denotation is unknown
while(argv != end) { while(argv != end) {
if(values && lastArgInLevel->requiredValueCount() != static_cast<size_t>(-1) && values->size() < lastArgInLevel->requiredValueCount()) { if(values && lastArgInLevel->requiredValueCount() != static_cast<size_t>(-1) && values->size() < lastArgInLevel->requiredValueCount()) {
// there are still values to read // there are still values to read
values->emplace_back(*argv); values->emplace_back(argDenotation ? argDenotation : *argv);
++index, ++argv; ++index, ++argv, argDenotation = nullptr;
} else { } else {
// determine denotation type // determine how denotation must be processed
const char *argDenotation = *argv;
if(!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
// skip empty arguments
++index, ++argv;
continue;
}
bool abbreviationFound = false; bool abbreviationFound = false;
unsigned char argDenotationType = Value; unsigned char argDenotationType;
*argDenotation == '-' && (++argDenotation, ++argDenotationType) if(argDenotation) {
&& *argDenotation == '-' && (++argDenotation, ++argDenotationType); // continue reading childs for abbreviation denotation already detected
abbreviationFound = false;
argDenotationType = Abbreviation;
} else {
// determine denotation type
argDenotation = *argv;
if(!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
// skip empty arguments
++index, ++argv, argDenotation = nullptr;
continue;
}
abbreviationFound = false;
argDenotationType = Value;
*argDenotation == '-' && (++argDenotation, ++argDenotationType)
&& *argDenotation == '-' && (++argDenotation, ++argDenotationType);
}
// try to find matching Argument instance // try to find matching Argument instance
Argument *matchingArg = nullptr; Argument *matchingArg = nullptr;
size_t argDenLen; size_t argDenotationLength;
if(argDenotationType != Value) { if(argDenotationType != Value) {
const char *const equationPos = strchr(argDenotation, '='); const char *const equationPos = strchr(argDenotation, '=');
for(argDenLen = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation); argDenLen; matchingArg = nullptr) { for(argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation); argDenotationLength; matchingArg = nullptr) {
// search for arguments by abbreviation or name depending on the denotation type // search for arguments by abbreviation or name depending on the previously determined denotation type
if(argDenotationType == Abbreviation) { if(argDenotationType == Abbreviation) {
for(Argument *arg : args) { for(Argument *arg : args) {
if(arg->abbreviation() && arg->abbreviation() == *argDenotation) { if(arg->abbreviation() && arg->abbreviation() == *argDenotation) {
@ -522,7 +544,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
} }
} else { } else {
for(Argument *arg : args) { for(Argument *arg : args) {
if(arg->name() && !strncmp(arg->name(), argDenotation, argDenLen) && *(arg->name() + argDenLen) == '\0') { if(arg->name() && !strncmp(arg->name(), argDenotation, argDenotationLength) && *(arg->name() + argDenotationLength) == '\0') {
matchingArg = arg; matchingArg = arg;
break; break;
} }
@ -530,7 +552,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
} }
if(matchingArg) { if(matchingArg) {
// an argument matched the specified denotation // an argument matched the specified denotation so add an occurrence
matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg); matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
// prepare reading parameter values // prepare reading parameter values
@ -539,12 +561,18 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
values->push_back(equationPos + 1); values->push_back(equationPos + 1);
} }
// read sub arguments if no abbreviated argument follows // read sub arguments
++index, ++m_actualArgc, lastArg = lastArgInLevel = matchingArg; ++index, ++m_actualArgc, lastArg = lastArgInLevel = matchingArg;
if(argDenotationType != Abbreviation || (!*++argDenotation && argDenotation != equationPos)) { if(argDenotationType != Abbreviation || (++argDenotation != equationPos)) {
readSpecifiedArgs(matchingArg->m_subArgs, index, ++argv, end, lastArg, completionMode); if(argDenotationType != Abbreviation || !*argDenotation) {
// no further abbreviations follow -> read sub args for next argv
readSpecifiedArgs(lastArg->m_subArgs, index, ++argv, end, lastArg, argDenotation = nullptr, completionMode);
} else {
// further abbreviations follow -> don't increment argv, keep processing outstanding chars of argDenotation
readSpecifiedArgs(lastArg->m_subArgs, index, argv, end, lastArg, argDenotation, completionMode);
}
break; break;
} // else: another abbreviated argument follows } // else: another abbreviated argument follows (and it is not present in the sub args)
} else { } else {
break; break;
} }
@ -552,13 +580,13 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
} }
if(!matchingArg) { if(!matchingArg) {
// unknown argument might be a sibling of the parent element
if(argDenotationType != Value) { if(argDenotationType != Value) {
// unknown argument might be a sibling of the parent element
for(auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend(); ; ++parentArgument) { for(auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend(); ; ++parentArgument) {
for(Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : m_mainArgs)) { for(Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : m_mainArgs)) {
if(sibling->occurrences() < sibling->maxOccurrences()) { if(sibling->occurrences() < sibling->maxOccurrences()) {
if((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation)) if((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation))
|| (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenLen))) { || (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenotationLength))) {
return; return;
} }
} }
@ -569,10 +597,10 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
}; };
} }
// unknown argument might just be a parameter value of the last argument
if(lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) { if(lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
// unknown argument might just be a parameter of the last argument
values->emplace_back(abbreviationFound ? argDenotation : *argv); values->emplace_back(abbreviationFound ? argDenotation : *argv);
++index, ++argv; ++index, ++argv, argDenotation = nullptr;
continue; continue;
} }
@ -587,8 +615,8 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
} }
} }
// use the first default argument which is not already present if there is still no match
if(!matchingArg && (!completionMode || (argv + 1 != end))) { if(!matchingArg && (!completionMode || (argv + 1 != end))) {
// use the first default argument which is not already present
for(Argument *arg : args) { for(Argument *arg : args) {
if(arg->isImplicit() && !arg->isPresent()) { if(arg->isImplicit() && !arg->isPresent()) {
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg); (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
@ -600,7 +628,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
if(matchingArg) { if(matchingArg) {
// an argument matched the specified denotation // an argument matched the specified denotation
if(lastArgInLevel == matchingArg) { if(lastArgInLevel == matchingArg) {
break; // TODO: why? break; // break required? -> TODO: add test for this condition
} }
// prepare reading parameter values // prepare reading parameter values
@ -608,31 +636,34 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
// read sub arguments // read sub arguments
++m_actualArgc, lastArg = lastArgInLevel = matchingArg; ++m_actualArgc, lastArg = lastArgInLevel = matchingArg;
readSpecifiedArgs(matchingArg->m_subArgs, index, argv, end, lastArg, completionMode); readSpecifiedArgs(lastArg->m_subArgs, index, argv, end, lastArg, argDenotation = nullptr, completionMode);
continue; continue;
} }
if(!parentArg) { // argument denotation is unknown -> handle error
if(completionMode) { if(parentArg) {
++index, ++argv; // continue with parent level
} else { return;
switch(m_unknownArgBehavior) {
case UnknownArgumentBehavior::Warn:
cerr << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
FALLTHROUGH;
case UnknownArgumentBehavior::Ignore:
++index, ++argv;
break;
case UnknownArgumentBehavior::Fail:
throw Failure("The specified argument \"" + string(*argv) + "\" is unknown and will be ignored.");
}
}
} else {
return; // unknown argument name or abbreviation found -> continue with parent level
} }
} if(completionMode) {
} // ignore unknown denotation
} ++index, ++argv, argDenotation = nullptr;
} else {
switch(m_unknownArgBehavior) {
case UnknownArgumentBehavior::Warn:
cerr << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
FALLTHROUGH;
case UnknownArgumentBehavior::Ignore:
// ignore unknown denotation
++index, ++argv, argDenotation = nullptr;
break;
case UnknownArgumentBehavior::Fail:
throw Failure("The specified argument \"" + string(*argv) + "\" is unknown and will be ignored.");
}
}
} // if(!matchingArg)
} // no values to read
} // while(argv != end)
} }
/*! /*!
* \brief Returns whether \a arg1 should be listed before \a arg2 when * \brief Returns whether \a arg1 should be listed before \a arg2 when

View File

@ -223,7 +223,7 @@ public:
private: private:
IF_DEBUG_BUILD(void verifyArgs(const ArgumentVector &args);) IF_DEBUG_BUILD(void verifyArgs(const ArgumentVector &args);)
void readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, Argument *&lastArg, bool completionMode = false); void readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, Argument *&lastArg, const char *&argDenotation, bool completionMode = false);
void printBashCompletion(int argc, const char * const *argv, unsigned int cursorPos, const Argument *lastDetectedArg); void printBashCompletion(int argc, const char * const *argv, unsigned int cursorPos, const Argument *lastDetectedArg);
void checkConstraints(const ArgumentVector &args); void checkConstraints(const ArgumentVector &args);
void invokeCallbacks(const ArgumentVector &args); void invokeCallbacks(const ArgumentVector &args);

View File

@ -241,15 +241,27 @@ void ArgumentParserTests::testParsing()
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time.")); CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time."));
} }
// test abbreviation combination with nesting "-pf"
const char *argv10[] = {"tageditor", "-pf", "test"};
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset(), verboseArg.setRequired(false);
parser.parseArgs(3, argv10);
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
CPPUNIT_ASSERT(!fileArg.isPresent());
CPPUNIT_ASSERT(filesArg.isPresent());
CPPUNIT_ASSERT_EQUAL(filesArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
CPPUNIT_ASSERT(!strcmp(filesArg.values(0).front(), "test"));
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
// it should not complain if -i is not present // it should not complain if -i is not present
const char *argv6[] = {"tageditor", "-g"}; const char *argv6[] = {"tageditor", "-g"};
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset(); displayTagInfoArg.reset(), fileArg.reset(), verboseArg.reset(), filesArg.reset();
parser.parseArgs(2, argv6); parser.parseArgs(2, argv6);
CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent()); CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
// it should not be possible to specify -f without -i or -p // it should not be possible to specify -f without -i or -p
const char *argv7[] = {"tageditor", "-f", "test"}; const char *argv7[] = {"tageditor", "-f", "test"};
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset(), qtConfigArgs.qtWidgetsGuiArg().reset(); displayFileInfoArg.reset(), fileArg.reset(), filesArg.reset(), verboseArg.reset(), qtConfigArgs.qtWidgetsGuiArg().reset();
try { try {
parser.parseArgs(3, argv7); parser.parseArgs(3, argv7);
CPPUNIT_FAIL("Exception expected."); CPPUNIT_FAIL("Exception expected.");
@ -258,6 +270,24 @@ void ArgumentParserTests::testParsing()
CPPUNIT_ASSERT(!strcmp(e.what(), "The specified argument \"-f\" is unknown and will be ignored.")); CPPUNIT_ASSERT(!strcmp(e.what(), "The specified argument \"-f\" is unknown and will be ignored."));
} }
// test equation sign syntax
const char *argv11[] = {"tageditor", "-if=test"};
fileArg.reset();
parser.parseArgs(2, argv11);
CPPUNIT_ASSERT(!filesArg.isPresent());
CPPUNIT_ASSERT(fileArg.isPresent());
CPPUNIT_ASSERT_EQUAL(fileArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
CPPUNIT_ASSERT(!strcmp(fileArg.values(0).front(), "test"));
// test specifying value directly after abbreviation
const char *argv12[] = {"tageditor", "-iftest"};
displayFileInfoArg.reset(), fileArg.reset();
parser.parseArgs(2, argv12);
CPPUNIT_ASSERT(!filesArg.isPresent());
CPPUNIT_ASSERT(fileArg.isPresent());
CPPUNIT_ASSERT_EQUAL(fileArg.values(0).size(), static_cast<vector<const char *>::size_type>(1));
CPPUNIT_ASSERT(!strcmp(fileArg.values(0).front(), "test"));
// test default argument // test default argument
const char *argv8[] = {"tageditor"}; const char *argv8[] = {"tageditor"};
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset(); displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
@ -377,6 +407,7 @@ void ArgumentParserTests::testBashCompletion()
size_t index = 0; size_t index = 0;
Argument *lastDetectedArg = nullptr; Argument *lastDetectedArg = nullptr;
const char *argDenotation = nullptr;
// redirect cout to custom buffer // redirect cout to custom buffer
stringstream buffer; stringstream buffer;
@ -386,7 +417,7 @@ void ArgumentParserTests::testBashCompletion()
// should fail because operation flags are not set // should fail because operation flags are not set
const char *const argv1[] = {"se"}; const char *const argv1[] = {"se"};
const char *const *argv = argv1; const char *const *argv = argv1;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv1 + 1, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv1 + 1, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(1, argv1, 0, lastDetectedArg); parser.printBashCompletion(1, argv1, 0, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=()\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=()\n"), buffer.str());
@ -396,7 +427,7 @@ void ArgumentParserTests::testBashCompletion()
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
getArg.setDenotesOperation(true), setArg.setDenotesOperation(true); getArg.setDenotesOperation(true), setArg.setDenotesOperation(true);
argv = argv1; argv = argv1;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv1 + 1, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv1 + 1, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(1, argv1, 0, lastDetectedArg); parser.printBashCompletion(1, argv1, 0, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('set' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('set' )\n"), buffer.str());
@ -406,7 +437,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()); index = 0, lastDetectedArg = nullptr, buffer.str(string());
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv2; argv = argv2;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv2 + 1, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv2 + 1, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(1, argv2, 0, lastDetectedArg); parser.printBashCompletion(1, argv2, 0, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('set' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('set' )\n"), buffer.str());
@ -415,7 +446,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv2; argv = argv2;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv2 + 1, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv2 + 1, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(1, argv2, 1, lastDetectedArg); parser.printBashCompletion(1, argv2, 1, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--values' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--values' )\n"), buffer.str());
@ -424,7 +455,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = nullptr; argv = nullptr;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, nullptr, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, nullptr, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(0, nullptr, 0, lastDetectedArg); parser.printBashCompletion(0, nullptr, 0, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('display-file-info' 'get' 'set' '--help' )\n"), buffer.str());
@ -434,7 +465,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv3; argv = argv3;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv3 + 2, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv3 + 2, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(2, argv3, 2, lastDetectedArg); parser.printBashCompletion(2, argv3, 2, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' )\n"), buffer.str());
@ -444,7 +475,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv4; argv = argv4;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv4 + 3, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv4 + 3, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(3, argv4, 2, lastDetectedArg); parser.printBashCompletion(3, argv4, 2, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n"), buffer.str());
@ -459,7 +490,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), getArg.reset(), setArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv5; argv = argv5;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv5 + 3, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv5 + 3, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(3, argv5, 2, lastDetectedArg); parser.printBashCompletion(3, argv5, 2, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
// order for file names is not specified // order for file names is not specified
@ -475,7 +506,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset(), valuesArg.reset(), filesArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset(), valuesArg.reset(), filesArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv6; argv = argv6;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv6 + 2, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv6 + 2, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(2, argv6, 1, lastDetectedArg); parser.printBashCompletion(2, argv6, 1, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--values' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--values' )\n"), buffer.str());
@ -485,7 +516,7 @@ void ArgumentParserTests::testBashCompletion()
index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset(), valuesArg.reset(), filesArg.reset(); index = 0, lastDetectedArg = nullptr, buffer.str(string()), setArg.reset(), valuesArg.reset(), filesArg.reset();
cout.rdbuf(buffer.rdbuf()); cout.rdbuf(buffer.rdbuf());
argv = argv7; argv = argv7;
parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv7 + 3, lastDetectedArg, true); parser.readSpecifiedArgs(parser.m_mainArgs, index, argv, argv7 + 3, lastDetectedArg, argDenotation = nullptr, true);
parser.printBashCompletion(3, argv7, 2, lastDetectedArg); parser.printBashCompletion(3, argv7, 2, lastDetectedArg);
cout.rdbuf(regularCoutBuffer); cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--nested-sub' '--verbose' )\n"), buffer.str()); CPPUNIT_ASSERT_EQUAL(string("COMPREPLY=('--files' '--nested-sub' '--verbose' )\n"), buffer.str());