Improve argument parser
- Fix some implementation details - Extend tests
This commit is contained in:
parent
526cbc5282
commit
79ce6e9aa6
|
@ -61,7 +61,7 @@ Argument::Argument(const char *name, char abbreviation, const char *description,
|
|||
m_combinable(false),
|
||||
m_denotesOperation(false),
|
||||
m_requiredValueCount(0),
|
||||
m_default(false),
|
||||
m_implicit(false),
|
||||
m_isMainArg(false)
|
||||
{}
|
||||
|
||||
|
@ -248,24 +248,44 @@ void Argument::reset()
|
|||
*/
|
||||
ArgumentParser::ArgumentParser() :
|
||||
m_actualArgc(0),
|
||||
m_currentDirectory(nullptr),
|
||||
m_ignoreUnknownArgs(false)
|
||||
m_executable(nullptr),
|
||||
m_ignoreUnknownArgs(false),
|
||||
m_defaultArg(nullptr)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Sets the main arguments for the parser. The parser will use these argument definitions
|
||||
* to when parsing the command line arguments and when printing help information.
|
||||
*
|
||||
* \remarks
|
||||
* The parser does not take ownership. Do not destroy the arguments as long as they are used as
|
||||
* main arguments.
|
||||
* - The parser does not take ownership. Do not destroy the arguments as long as they are used as
|
||||
* main arguments.
|
||||
* - Sets the first specified argument as default argument if none is assigned yet and the
|
||||
* first argument has no mandatory sub arguments.
|
||||
*/
|
||||
void ArgumentParser::setMainArguments(const ArgumentInitializerList &mainArguments)
|
||||
{
|
||||
for(Argument *arg : mainArguments) {
|
||||
arg->m_isMainArg = true;
|
||||
if(mainArguments.size()) {
|
||||
for(Argument *arg : mainArguments) {
|
||||
arg->m_isMainArg = true;
|
||||
}
|
||||
m_mainArgs.assign(mainArguments);
|
||||
if(!m_defaultArg) {
|
||||
if(!(*mainArguments.begin())->requiredValueCount()) {
|
||||
bool subArgsRequired = false;
|
||||
for(Argument *subArg : (*mainArguments.begin())->subArguments()) {
|
||||
if(subArg->isRequired()) {
|
||||
subArgsRequired = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!subArgsRequired) {
|
||||
m_defaultArg = *mainArguments.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_mainArgs.clear();
|
||||
}
|
||||
m_mainArgs.assign(mainArguments);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -347,15 +367,23 @@ void ArgumentParser::parseArgs(int argc, const char *argv[])
|
|||
{
|
||||
IF_DEBUG_BUILD(verifyArgs(m_mainArgs);)
|
||||
m_actualArgc = 0;
|
||||
if(argc > 0) {
|
||||
m_currentDirectory = *argv;
|
||||
size_t index = 0;
|
||||
++argv;
|
||||
readSpecifiedArgs(m_mainArgs, index, argv, argv + argc - 1);
|
||||
if(argc) {
|
||||
m_executable = *argv;
|
||||
if(--argc) {
|
||||
size_t index = 0;
|
||||
++argv;
|
||||
readSpecifiedArgs(m_mainArgs, index, argv, argv + argc);
|
||||
} else {
|
||||
// no arguments specified -> set default argument as present
|
||||
if(m_defaultArg) {
|
||||
m_defaultArg->m_indices.push_back(0);
|
||||
m_defaultArg->m_values.emplace_back();
|
||||
}
|
||||
}
|
||||
checkConstraints(m_mainArgs);
|
||||
invokeCallbacks(m_mainArgs);
|
||||
} else {
|
||||
m_currentDirectory = nullptr;
|
||||
m_executable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,13 +410,13 @@ void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args
|
|||
abbreviations.reserve(args.size());
|
||||
vector<string> names;
|
||||
names.reserve(args.size());
|
||||
bool hasDefault = false;
|
||||
bool hasImplicit = false;
|
||||
for(const Argument *arg : args) {
|
||||
assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
|
||||
verifiedArgs.push_back(arg);
|
||||
assert(arg->isMainArgument() || !arg->denotesOperation());
|
||||
assert(!arg->isDefault() || !hasDefault);
|
||||
hasDefault |= arg->isDefault();
|
||||
assert(!arg->isImplicit() || !hasImplicit);
|
||||
hasImplicit |= arg->isImplicit();
|
||||
assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
|
||||
abbreviations.push_back(arg->abbreviation());
|
||||
assert(!arg->name() || find(names.cbegin(), names.cend(), arg->name()) == names.cend());
|
||||
|
@ -403,7 +431,7 @@ void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args
|
|||
* \brief Reads the specified commands line arguments.
|
||||
* \remarks Results are stored in Argument instances added as main arguments and sub arguments.
|
||||
*/
|
||||
void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char **&argv, const char **end)
|
||||
void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char **&argv, const char **end, unsigned int level)
|
||||
{
|
||||
enum ArgumentDenotationType : unsigned char {
|
||||
Value = 0, // parameter value
|
||||
|
@ -411,7 +439,6 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
|
|||
FullName = 2 // full argument name
|
||||
};
|
||||
|
||||
bool isTopLevel = index == 0;
|
||||
Argument *lastArg = nullptr;
|
||||
vector<const char *> *values = nullptr;
|
||||
while(argv != end) {
|
||||
|
@ -464,7 +491,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
|
|||
// read sub arguments if no abbreviated argument follows
|
||||
++index, ++m_actualArgc, lastArg = matchingArg;
|
||||
if(argDenotationType != Abbreviation || (!*++argDenotation && argDenotation != equationPos)) {
|
||||
readSpecifiedArgs(matchingArg->m_subArgs, index, ++argv, end);
|
||||
readSpecifiedArgs(matchingArg->m_subArgs, index, ++argv, end, level + 1);
|
||||
break;
|
||||
} // else: another abbreviated argument follows
|
||||
} else {
|
||||
|
@ -481,7 +508,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
|
|||
continue;
|
||||
} else {
|
||||
// first value might denote "operation"
|
||||
if(isTopLevel) {
|
||||
if(!level) {
|
||||
for(Argument *arg : args) {
|
||||
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
|
||||
(matchingArg = arg)->m_indices.push_back(index);
|
||||
|
@ -494,7 +521,7 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
|
|||
if(!matchingArg) {
|
||||
// use the first default argument
|
||||
for(Argument *arg : args) {
|
||||
if(arg->isDefault()) {
|
||||
if(arg->isImplicit()) {
|
||||
(matchingArg = arg)->m_indices.push_back(index);
|
||||
break;
|
||||
}
|
||||
|
@ -512,11 +539,11 @@ void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index,
|
|||
|
||||
// read sub arguments if no abbreviated argument follows
|
||||
++m_actualArgc, lastArg = matchingArg;
|
||||
readSpecifiedArgs(matchingArg->m_subArgs, index, argv, end);
|
||||
readSpecifiedArgs(matchingArg->m_subArgs, index, argv, end, level + 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isTopLevel) {
|
||||
if(!level) {
|
||||
if(m_ignoreUnknownArgs) {
|
||||
cerr << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
|
||||
++index, ++argv;
|
||||
|
@ -557,7 +584,7 @@ void ArgumentParser::checkConstraints(const ArgumentVector &args)
|
|||
throw Failure("The argument \"" + string(conflictingArgument->name()) + "\" can not be combined with \"" + arg->name() + "\".");
|
||||
}
|
||||
for(size_t i = 0; i != occurrences; ++i) {
|
||||
if(!arg->allRequiredValuesPresent(occurrences)) {
|
||||
if(!arg->allRequiredValuesPresent(i)) {
|
||||
stringstream ss(stringstream::in | stringstream::out);
|
||||
ss << "Not all parameter for argument \"" << arg->name() << "\" ";
|
||||
if(i) {
|
||||
|
@ -566,8 +593,7 @@ void ArgumentParser::checkConstraints(const ArgumentVector &args)
|
|||
ss << "provided. You have to provide the following parameter:";
|
||||
size_t valueNamesPrint = 0;
|
||||
for(const auto &name : arg->m_valueNames) {
|
||||
ss << "\n" << name;
|
||||
++valueNamesPrint;
|
||||
ss << ' ' << name, ++valueNamesPrint;
|
||||
}
|
||||
if(arg->m_requiredValueCount != static_cast<size_t>(-1)) {
|
||||
while(valueNamesPrint < arg->m_requiredValueCount) {
|
||||
|
@ -596,11 +622,7 @@ void ArgumentParser::invokeCallbacks(const ArgumentVector &args)
|
|||
// invoke the callback for each occurance of the argument
|
||||
if(arg->m_callbackFunction) {
|
||||
for(const auto &valuesOfOccurance : arg->m_values) {
|
||||
if(arg->isDefault() && valuesOfOccurance.empty()) {
|
||||
arg->m_callbackFunction(arg->defaultValues());
|
||||
} else {
|
||||
arg->m_callbackFunction(valuesOfOccurance);
|
||||
}
|
||||
arg->m_callbackFunction(valuesOfOccurance);
|
||||
}
|
||||
}
|
||||
// invoke the callbacks for sub arguments recursively
|
||||
|
|
|
@ -57,10 +57,6 @@ public:
|
|||
void setValueNames(std::initializer_list<const char *> valueNames);
|
||||
void appendValueName(const char *valueName);
|
||||
bool allRequiredValuesPresent(std::size_t occurrance = 0) const;
|
||||
bool isDefault() const;
|
||||
void setDefault(bool isDefault);
|
||||
const std::vector<const char *> &defaultValues() const;
|
||||
void setDefaultValues(const std::initializer_list<const char *> &defaultValues);
|
||||
bool isPresent() const;
|
||||
std::size_t occurrences() const;
|
||||
const std::vector<std::size_t> &indices() const;
|
||||
|
@ -98,8 +94,7 @@ private:
|
|||
bool m_denotesOperation;
|
||||
std::size_t m_requiredValueCount;
|
||||
std::vector<const char *> m_valueNames;
|
||||
bool m_default;
|
||||
std::vector<const char *> m_defaultValues;
|
||||
bool m_implicit;
|
||||
std::vector<std::size_t> m_indices;
|
||||
std::vector<std::vector<const char *> > m_values;
|
||||
ArgumentVector m_subArgs;
|
||||
|
@ -297,62 +292,25 @@ inline void Argument::appendValueName(const char *valueName)
|
|||
inline bool Argument::allRequiredValuesPresent(std::size_t occurrance) const
|
||||
{
|
||||
return m_requiredValueCount == static_cast<std::size_t>(-1)
|
||||
|| (m_values[occurrance].size() >= static_cast<std::size_t>(m_requiredValueCount))
|
||||
|| (m_default && m_defaultValues.size() >= static_cast<std::size_t>(m_requiredValueCount));
|
||||
|| (m_values[occurrance].size() >= static_cast<std::size_t>(m_requiredValueCount));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an indication whether the argument is a default argument.
|
||||
*
|
||||
* A default argument will be flagged as present when parsing arguments even
|
||||
* if it is not actually present and there is no uncombinable argument present
|
||||
* and the it is a main argument or the parent is present.
|
||||
*
|
||||
* The callback function will be invoked in this case as the argument where
|
||||
* actually present.
|
||||
*
|
||||
* The default value (for this property) is false.
|
||||
*
|
||||
* \sa setDefault()
|
||||
* \brief Returns an indication whether the argument is an implicit argument.
|
||||
* \sa setImplicit()
|
||||
*/
|
||||
inline bool Argument::isDefault() const
|
||||
inline bool Argument::isImplicit() const
|
||||
{
|
||||
return m_default;
|
||||
return m_implicit;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether the argument is a default argument.
|
||||
* \sa isDefault()
|
||||
* \brief Sets whether the argument is an implicit argument.
|
||||
* \sa isImplicit()
|
||||
*/
|
||||
inline void Argument::setDefault(bool isDefault)
|
||||
inline void Argument::setImplicit(bool implicit)
|
||||
{
|
||||
m_default = isDefault;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the default values.
|
||||
* \sa isDefault()
|
||||
* \sa setDefault()
|
||||
* \sa setDefaultValues()
|
||||
*/
|
||||
inline const std::vector<const char *> &Argument::defaultValues() const
|
||||
{
|
||||
return m_defaultValues;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the default values.
|
||||
*
|
||||
* There must be as many default values as required values
|
||||
* if the argument is a default argument.
|
||||
*
|
||||
* \sa isDefault()
|
||||
* \sa setDefault()
|
||||
* \sa defaultValues()
|
||||
*/
|
||||
inline void Argument::setDefaultValues(const std::initializer_list<const char *> &defaultValues)
|
||||
{
|
||||
m_defaultValues = defaultValues;
|
||||
m_implicit = implicit;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -564,20 +522,23 @@ public:
|
|||
void parseArgs(int argc, char *argv[]);
|
||||
void parseArgs(int argc, const char *argv[]);
|
||||
unsigned int actualArgumentCount() const;
|
||||
const char *currentDirectory() const;
|
||||
const char *executable() const;
|
||||
bool areUnknownArgumentsIgnored() const;
|
||||
void setIgnoreUnknownArguments(bool ignore);
|
||||
Argument *defaultArgument() const;
|
||||
void setDefaultArgument(Argument *argument);
|
||||
|
||||
private:
|
||||
IF_DEBUG_BUILD(void verifyArgs(const ArgumentVector &args);)
|
||||
void readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char **&argv, const char **end);
|
||||
void readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char **&argv, const char **end, unsigned int level = 0);
|
||||
void checkConstraints(const ArgumentVector &args);
|
||||
void invokeCallbacks(const ArgumentVector &args);
|
||||
|
||||
ArgumentVector m_mainArgs;
|
||||
unsigned int m_actualArgc;
|
||||
const char *m_currentDirectory;
|
||||
const char *m_executable;
|
||||
bool m_ignoreUnknownArgs;
|
||||
Argument *m_defaultArg;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -606,11 +567,11 @@ inline unsigned int ArgumentParser::actualArgumentCount() const
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the current directory.
|
||||
* \brief Returns the name of the current executable.
|
||||
*/
|
||||
inline const char *ArgumentParser::currentDirectory() const
|
||||
inline const char *ArgumentParser::executable() const
|
||||
{
|
||||
return m_currentDirectory;
|
||||
return m_executable;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -645,6 +606,24 @@ inline void ArgumentParser::setIgnoreUnknownArguments(bool ignore)
|
|||
m_ignoreUnknownArgs = ignore;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the default argument.
|
||||
* \remarks The default argument is assumed to be present if no other arguments have been specified.
|
||||
*/
|
||||
inline Argument *ArgumentParser::defaultArgument() const
|
||||
{
|
||||
return m_defaultArg;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the default argument.
|
||||
* \remarks The default argument is assumed to be present if no other arguments have been specified.
|
||||
*/
|
||||
inline void ArgumentParser::setDefaultArgument(Argument *argument)
|
||||
{
|
||||
m_defaultArg = argument;
|
||||
}
|
||||
|
||||
class LIB_EXPORT HelpArgument : public Argument
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -68,9 +68,8 @@ void ArgumentParserTests::testArgument()
|
|||
*/
|
||||
void ArgumentParserTests::testParsing()
|
||||
{
|
||||
// setup parser with some test argument definitions
|
||||
ArgumentParser parser;
|
||||
|
||||
// add some test argument definitions
|
||||
SET_APPLICATION_INFO;
|
||||
QT_CONFIG_ARGUMENTS qtConfigArgs;
|
||||
HelpArgument helpArg(parser);
|
||||
|
@ -94,7 +93,7 @@ void ArgumentParserTests::testParsing()
|
|||
Argument fieldsArg("fields", '\0', "specifies the fields");
|
||||
fieldsArg.setRequiredValueCount(-1);
|
||||
fieldsArg.setValueNames({"title", "album", "artist", "trackpos"});
|
||||
fieldsArg.setDefault(true);
|
||||
fieldsArg.setImplicit(true);
|
||||
Argument displayTagInfoArg("get", 'p', "displays the values of all specified tag fields (displays all fields if none specified)");
|
||||
displayTagInfoArg.setDenotesOperation(true);
|
||||
displayTagInfoArg.setSubArguments({&fieldsArg, &filesArg, &verboseArg});
|
||||
|
@ -115,8 +114,9 @@ void ArgumentParserTests::testParsing()
|
|||
filesArg.setCombinable(true);
|
||||
parser.parseArgs(7, argv);
|
||||
// check results
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(parser.currentDirectory(), "tageditor"));
|
||||
CPPUNIT_ASSERT(!strcmp(parser.executable(), "tageditor"));
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
|
@ -131,6 +131,7 @@ void ArgumentParserTests::testParsing()
|
|||
displayTagInfoArg.reset(), fieldsArg.reset(), filesArg.reset();
|
||||
parser.parseArgs(7, argv2);
|
||||
// check results again
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
|
@ -157,8 +158,23 @@ void ArgumentParserTests::testParsing()
|
|||
// repeat the test, but this time just ignore the undefined argument
|
||||
displayTagInfoArg.reset(), fieldsArg.reset(), filesArg.reset();
|
||||
parser.setIgnoreUnknownArguments(true);
|
||||
parser.parseArgs(6, argv3);
|
||||
// redirect stderr to check whether warnings are printed correctly
|
||||
stringstream buffer;
|
||||
streambuf *regularCerrBuffer = cerr.rdbuf(buffer.rdbuf());
|
||||
try {
|
||||
parser.parseArgs(6, argv3);
|
||||
cerr.rdbuf(regularCerrBuffer);
|
||||
} catch(...) {
|
||||
cerr.rdbuf(regularCerrBuffer);
|
||||
throw;
|
||||
}
|
||||
CPPUNIT_ASSERT(!strcmp(buffer.str().data(), "The specified argument \"album\" is unknown and will be ignored.\n"
|
||||
"The specified argument \"title\" is unknown and will be ignored.\n"
|
||||
"The specified argument \"diskpos\" is unknown and will be ignored.\n"
|
||||
"The specified argument \"--file\" is unknown and will be ignored.\n"
|
||||
"The specified argument \"somefile\" is unknown and will be ignored.\n"));
|
||||
// none of the arguments should be present now
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!fieldsArg.isPresent());
|
||||
|
@ -169,6 +185,7 @@ void ArgumentParserTests::testParsing()
|
|||
displayTagInfoArg.reset(), fieldsArg.reset(), filesArg.reset();
|
||||
parser.setIgnoreUnknownArguments(false);
|
||||
parser.parseArgs(4, argv4);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(verboseArg.isPresent());
|
||||
CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
|
||||
|
@ -183,6 +200,7 @@ void ArgumentParserTests::testParsing()
|
|||
parser.parseArgs(4, argv4);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch(const Failure &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" mustn't be specified more than 1 time."));
|
||||
}
|
||||
|
||||
|
@ -190,11 +208,13 @@ void ArgumentParserTests::testParsing()
|
|||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
|
||||
verboseArg.setConstraints(0, -1);
|
||||
parser.parseArgs(4, argv4);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// make verbose mandatory
|
||||
verboseArg.setRequired(true);
|
||||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
|
||||
parser.parseArgs(4, argv4);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// make it complain about missing argument
|
||||
const char *argv5[] = {"tageditor", "-i", "-f", "test"};
|
||||
|
@ -203,6 +223,7 @@ void ArgumentParserTests::testParsing()
|
|||
parser.parseArgs(4, argv5);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch(const Failure &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time."));
|
||||
}
|
||||
|
||||
|
@ -210,16 +231,59 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv6[] = {"tageditor", "-g"};
|
||||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
|
||||
parser.parseArgs(2, argv6);
|
||||
CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// it should not be possible to specify -f without -i or -p
|
||||
const char *argv7[] = {"tageditor", "-f", "test"};
|
||||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
|
||||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset(), qtConfigArgs.qtWidgetsGuiArg().reset();
|
||||
try {
|
||||
parser.parseArgs(3, argv7);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch(const Failure &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The specified argument \"-f\" is unknown and will be ignored."));
|
||||
}
|
||||
|
||||
// test default argument
|
||||
const char *argv8[] = {"tageditor"};
|
||||
displayFileInfoArg.reset(), fileArg.reset(), verboseArg.reset();
|
||||
parser.parseArgs(1, argv8);
|
||||
CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
CPPUNIT_ASSERT(!displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(!fileArg.isPresent());
|
||||
|
||||
// test required value count constraint with sufficient number of provided parameters
|
||||
qtConfigArgs.qtWidgetsGuiArg().reset();
|
||||
fieldsArg.setRequiredValueCount(3);
|
||||
verboseArg.setRequired(false);
|
||||
parser.parseArgs(7, argv2);
|
||||
// this should still work without complaints
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album"));
|
||||
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(filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(filesArg.values().at(0), "somefile"));
|
||||
|
||||
// test required value count constraint with insufficient number of provided parameters
|
||||
displayTagInfoArg.reset(), fieldsArg.reset(), filesArg.reset();
|
||||
fieldsArg.setRequiredValueCount(4);
|
||||
const char *argv9[] = {"tageditor", "-p", "album", "title", "diskpos"};
|
||||
try {
|
||||
parser.parseArgs(5, argv9);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch(const Failure &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
|
||||
}
|
||||
}
|
||||
|
||||
void ArgumentParserTests::testCallbacks()
|
||||
|
@ -249,5 +313,4 @@ void ArgumentParserTests::testCallbacks()
|
|||
callbackArg.reset();
|
||||
const char *argv2[] = {"test", "-l", "val1", "val2"};
|
||||
parser.parseArgs(4, argv2);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue