Rework ArgumentParser::parseArgs()
* Remove "ext()" and "orExit()" versions * Exit by default (might be intrusive but it is the most common use) * Rename Failure to ParseError
This commit is contained in:
parent
93bdf5b4f1
commit
4c1b733290
|
@ -4,7 +4,6 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
|||
set(HEADER_FILES
|
||||
application/argumentparser.h
|
||||
application/commandlineutils.h
|
||||
application/failure.h
|
||||
application/fakeqtconfigarguments.h
|
||||
application/global.h
|
||||
chrono/datetime.h
|
||||
|
@ -28,6 +27,7 @@ set(HEADER_FILES
|
|||
misc/math.h
|
||||
misc/memory.h
|
||||
misc/multiarray.h
|
||||
misc/parseerror.h
|
||||
misc/traits.h
|
||||
misc/levenshtein.h
|
||||
tests/testutils.h
|
||||
|
@ -37,7 +37,6 @@ set(SRC_FILES
|
|||
application/argumentparserprivate.h
|
||||
application/argumentparser.cpp
|
||||
application/commandlineutils.cpp
|
||||
application/failure.cpp
|
||||
application/fakeqtconfigarguments.cpp
|
||||
chrono/datetime.cpp
|
||||
chrono/period.cpp
|
||||
|
@ -53,6 +52,7 @@ set(SRC_FILES
|
|||
io/nativefilestream.cpp
|
||||
io/misc.cpp
|
||||
misc/math.cpp
|
||||
misc/parseerror.cpp
|
||||
misc/levenshtein.cpp
|
||||
tests/testutils.cpp)
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#include "./argumentparser.h"
|
||||
#include "./argumentparserprivate.h"
|
||||
#include "./commandlineutils.h"
|
||||
#include "./failure.h"
|
||||
|
||||
#include "../conversion/stringbuilder.h"
|
||||
#include "../conversion/stringconversion.h"
|
||||
#include "../io/ansiescapecodes.h"
|
||||
#include "../io/path.h"
|
||||
#include "../misc/levenshtein.h"
|
||||
#include "../misc/parseerror.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
@ -855,36 +855,6 @@ void ArgumentParser::printHelp(ostream &os) const
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Parses the specified command line arguments.
|
||||
* \remarks
|
||||
* - The results are stored in the Argument instances assigned as main arguments and sub arguments.
|
||||
* - Calls the assigned callbacks if no constraints are violated.
|
||||
* - This method will not return in case shell completion is requested. This behavior can be altered
|
||||
* by overriding ApplicationUtilities::exitFunction which defaults to &std::exit.
|
||||
* \throws Throws Failure if the specified arguments are invalid or violate the constraints defined
|
||||
* by the Argument instances.
|
||||
* \sa readArgs(), parseArgsOrExit()
|
||||
*/
|
||||
void ArgumentParser::parseArgs(int argc, const char *const *argv)
|
||||
{
|
||||
parseArgsExt(argc, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Parses the specified command line arguments.
|
||||
* \remarks The same as parseArgs(), except that this method will not throw an exception in the error
|
||||
* case. Instead, it will print an error message and terminate the application with exit
|
||||
* code 1.
|
||||
* \sa parseArgs(), readArgs()
|
||||
* \deprecated In next major release, this method will be removed. parseArgs() can serve the same
|
||||
* purpose then.
|
||||
*/
|
||||
void ArgumentParser::parseArgsOrExit(int argc, const char *const *argv)
|
||||
{
|
||||
parseArgsExt(argc, argv);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Parses the specified command line arguments.
|
||||
*
|
||||
|
@ -898,11 +868,10 @@ void ArgumentParser::parseArgsOrExit(int argc, const char *const *argv)
|
|||
* - This method will not return in case shell completion is requested. This behavior can be altered
|
||||
* by overriding ApplicationUtilities::exitFunction which defaults to &std::exit.
|
||||
* \throws Throws Failure if the specified arguments are invalid and the ParseArgumentBehavior::ExitOnFailure
|
||||
* flag is not present.
|
||||
* flag is *not* present.
|
||||
* \sa parseArgs(), readArgs(), parseArgsOrExit()
|
||||
* \deprecated In next major release, this method will be available as parseArgs().
|
||||
*/
|
||||
void ArgumentParser::parseArgsExt(int argc, const char *const *argv, ParseArgumentBehavior behavior)
|
||||
void ArgumentParser::parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior)
|
||||
{
|
||||
try {
|
||||
readArgs(argc, argv);
|
||||
|
@ -915,7 +884,7 @@ void ArgumentParser::parseArgsExt(int argc, const char *const *argv, ParseArgume
|
|||
if (behavior & ParseArgumentBehavior::InvokeCallbacks) {
|
||||
invokeCallbacks(m_mainArgs);
|
||||
}
|
||||
} catch (const Failure &failure) {
|
||||
} catch (const ParseError &failure) {
|
||||
if (behavior & ParseArgumentBehavior::ExitOnFailure) {
|
||||
CMD_UTILS_START_CONSOLE;
|
||||
cerr << failure;
|
||||
|
@ -988,7 +957,7 @@ void ArgumentParser::readArgs(int argc, const char *const *argv)
|
|||
// fail when not all arguments could be processed, except when in completion mode
|
||||
if (!completionMode && !allArgsProcessed) {
|
||||
const auto suggestions(findSuggestions(argc, argv, static_cast<unsigned int>(argc - 1), reader));
|
||||
throw Failure(argsToString("The specified argument \"", *reader.argv, "\" is unknown.", suggestions));
|
||||
throw ParseError(argsToString("The specified argument \"", *reader.argv, "\" is unknown.", suggestions));
|
||||
}
|
||||
|
||||
// print Bash completion and prevent the applicaton to continue with the regular execution
|
||||
|
@ -1577,11 +1546,11 @@ void ArgumentParser::checkConstraints(const ArgumentVector &args)
|
|||
for (const Argument *arg : args) {
|
||||
const auto occurrences = arg->occurrences();
|
||||
if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
|
||||
throw Failure(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
|
||||
throw ParseError(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
|
||||
(arg->maxOccurrences() == 1 ? " time." : " times.")));
|
||||
}
|
||||
if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
|
||||
throw Failure(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
|
||||
throw ParseError(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
|
||||
(arg->minOccurrences() == 1 ? " time." : " times.")));
|
||||
}
|
||||
Argument *conflictingArgument = nullptr;
|
||||
|
@ -1593,7 +1562,7 @@ void ArgumentParser::checkConstraints(const ArgumentVector &args)
|
|||
conflictingArgument = arg->conflictsWithArgument();
|
||||
}
|
||||
if (conflictingArgument) {
|
||||
throw Failure(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
|
||||
throw ParseError(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
|
||||
}
|
||||
for (size_t i = 0; i != occurrences; ++i) {
|
||||
if (arg->allRequiredValuesPresent(i)) {
|
||||
|
@ -1615,7 +1584,7 @@ void ArgumentParser::checkConstraints(const ArgumentVector &args)
|
|||
ss << "\nvalue " << (++valueNamesPrint);
|
||||
}
|
||||
}
|
||||
throw Failure(ss.str());
|
||||
throw ParseError(ss.str());
|
||||
}
|
||||
|
||||
// check contraints of sub arguments recursively
|
||||
|
@ -1759,7 +1728,7 @@ void NoColorArgument::apply() const
|
|||
*/
|
||||
void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(const std::vector<Argument *> &argumentPath) const
|
||||
{
|
||||
throw Failure(argumentPath.empty()
|
||||
throw ParseError(argumentPath.empty()
|
||||
? argsToString("Conversion of top-level value \"", valueToConvert, "\" to type \"", targetTypeName, "\" failed: ", errorMessage)
|
||||
: argsToString("Conversion of value \"", valueToConvert, "\" (for argument --", argumentPath.back()->name(), ") to type \"",
|
||||
targetTypeName, "\" failed: ", errorMessage));
|
||||
|
@ -1770,7 +1739,7 @@ void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(const s
|
|||
*/
|
||||
void ArgumentOccurrence::throwNumberOfValuesNotSufficient(unsigned long valuesToConvert) const
|
||||
{
|
||||
throw Failure(path.empty()
|
||||
throw ParseError(path.empty()
|
||||
? argsToString("Expected ", valuesToConvert, " top-level values to be present but only ", values.size(), " have been specified.")
|
||||
: argsToString("Expected ", valuesToConvert, " values for argument --", path.back()->name(), " to be present but only ", values.size(),
|
||||
" have been specified."));
|
||||
|
|
|
@ -460,9 +460,7 @@ public:
|
|||
|
||||
// declare operations which will consider previously assigned argument definitions and maybe modify parsing results
|
||||
void printHelp(std::ostream &os) const;
|
||||
void parseArgs(int argc, const char *const *argv);
|
||||
void parseArgsOrExit(int argc, const char *const *argv);
|
||||
void parseArgsExt(int argc, const char *const *argv,
|
||||
void parseArgs(int argc, const char *const *argv,
|
||||
ParseArgumentBehavior behavior
|
||||
= ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks | ParseArgumentBehavior::ExitOnFailure);
|
||||
void readArgs(int argc, const char *const *argv);
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#include "./failure.h"
|
||||
|
||||
#include "../io/ansiescapecodes.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
|
||||
/*!
|
||||
* \class ApplicationUtilities::Failure
|
||||
* \brief The Failure class is thrown by an ArgumentParser when a parsing error occurs.
|
||||
*
|
||||
* \sa ApplicationUtilities::ArgumentParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Constructs a new Failure.
|
||||
*/
|
||||
Failure::Failure()
|
||||
: m_what("unspecified parsing exception")
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Constructs a new Failure. \a what is a std::string
|
||||
* describing the cause of the Failure.
|
||||
*/
|
||||
Failure::Failure(const std::string &what)
|
||||
: m_what(what)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Destroys the Failure.
|
||||
*/
|
||||
Failure::~Failure() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns a C-style character string describing the cause
|
||||
* of the Failure.
|
||||
*/
|
||||
const char *Failure::what() const noexcept
|
||||
{
|
||||
return m_what.c_str();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Prints an error message "Unable to parse arguments: ..." for the specified \a failure.
|
||||
*/
|
||||
std::ostream &operator<<(std::ostream &o, const Failure &failure)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace EscapeCodes;
|
||||
return o << Phrases::Error << "Unable to parse arguments: " << TextAttribute::Reset << failure.what() << "\nSee --help for available commands."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
} // namespace ApplicationUtilities
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef APPLICATION_UTILITIES_FAILURE_H
|
||||
#define APPLICATION_UTILITIES_FAILURE_H
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
#include <exception>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
|
||||
class CPP_UTILITIES_EXPORT Failure : public std::exception {
|
||||
public:
|
||||
Failure();
|
||||
Failure(const std::string &what);
|
||||
~Failure() noexcept override;
|
||||
|
||||
virtual const char *what() const noexcept override;
|
||||
|
||||
private:
|
||||
std::string m_what;
|
||||
};
|
||||
|
||||
CPP_UTILITIES_EXPORT std::ostream &operator<<(std::ostream &o, const Failure &failure);
|
||||
|
||||
} // namespace ApplicationUtilities
|
||||
|
||||
#endif // APPLICATION_UTILITIES_FAILURE_H
|
|
@ -0,0 +1,34 @@
|
|||
#include "./parseerror.h"
|
||||
|
||||
#include "../io/ansiescapecodes.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
|
||||
/*!
|
||||
* \class ApplicationUtilities::ParseError
|
||||
* \brief The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
|
||||
* \remarks The class might be used in other parsers, too.
|
||||
* \sa ApplicationUtilities::ArgumentParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Destroys the ParseError.
|
||||
*/
|
||||
ParseError::~ParseError() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Prints an error message "Unable to parse arguments: ..." for the specified \a failure.
|
||||
*/
|
||||
std::ostream &operator<<(std::ostream &o, const ParseError &failure)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace EscapeCodes;
|
||||
return o << Phrases::Error << "Unable to parse arguments: " << TextAttribute::Reset << failure.what() << "\nSee --help for available commands."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
} // namespace ApplicationUtilities
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef APPLICATION_UTILITIES_PARSE_ERROR_H
|
||||
#define APPLICATION_UTILITIES_PARSE_ERROR_H
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
|
||||
class CPP_UTILITIES_EXPORT ParseError : public std::runtime_error {
|
||||
public:
|
||||
ParseError();
|
||||
ParseError(const std::string &what);
|
||||
~ParseError() noexcept override;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new ParseError.
|
||||
*/
|
||||
inline ParseError::ParseError()
|
||||
: std::runtime_error("undetermined parsing")
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new ParseError. \a what is a std::string describing the cause of the ParseError.
|
||||
*/
|
||||
inline ParseError::ParseError(const std::string &what)
|
||||
: std::runtime_error(what)
|
||||
{
|
||||
}
|
||||
|
||||
CPP_UTILITIES_EXPORT std::ostream &operator<<(std::ostream &o, const ParseError &failure);
|
||||
|
||||
} // namespace ApplicationUtilities
|
||||
|
||||
#endif // APPLICATION_UTILITIES_PARSE_ERROR_H
|
|
@ -7,12 +7,13 @@
|
|||
#include "../application/argumentparser.h"
|
||||
#include "../application/argumentparserprivate.h"
|
||||
#include "../application/commandlineutils.h"
|
||||
#include "../application/failure.h"
|
||||
#include "../application/fakeqtconfigarguments.h"
|
||||
|
||||
#include "../io/ansiescapecodes.h"
|
||||
#include "../io/path.h"
|
||||
|
||||
#include "../misc/parseerror.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
@ -155,7 +156,7 @@ void ArgumentParserTests::testParsing()
|
|||
{ &qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayTagInfoArg, &displayFileInfoArg, &parser.noColorArg(), &parser.helpArg() });
|
||||
|
||||
// no args present
|
||||
parser.parseArgs(0, nullptr);
|
||||
parser.parseArgs(0, nullptr, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!parser.executable());
|
||||
CPPUNIT_ASSERT(!parser.specifiedOperation());
|
||||
CPPUNIT_ASSERT_EQUAL(0u, parser.actualArgumentCount());
|
||||
|
@ -164,9 +165,9 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv[] = { "tageditor", "get", "album", "title", "diskpos", "-f", "somefile" };
|
||||
// try to parse, this should fail
|
||||
try {
|
||||
parser.parseArgs(7, argv);
|
||||
parser.parseArgs(7, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT_EQUAL("The argument \"files\" can not be combined with \"fields\"."s, string(e.what()));
|
||||
// test printing btw
|
||||
stringstream ss;
|
||||
|
@ -180,7 +181,7 @@ void ArgumentParserTests::testParsing()
|
|||
// arguments read correctly after successful parse
|
||||
filesArg.setCombinable(true);
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(7, argv);
|
||||
parser.parseArgs(7, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
// check results
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
|
@ -198,7 +199,7 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv2[] = { "tageditor", "", "-p", "album", "title", "diskpos", "", "--files", "somefile" };
|
||||
// reparse the args
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(9, argv2);
|
||||
parser.parseArgs(9, argv2, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
// check results again
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
|
@ -217,9 +218,9 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv3[] = { "tageditor", "album", "title", "diskpos", "--files", "somefile" };
|
||||
try {
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(6, argv3);
|
||||
parser.parseArgs(6, argv3, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT_EQUAL("The specified argument \"album\" is unknown.\nDid you mean get or --help?"s, string(e.what()));
|
||||
}
|
||||
|
||||
|
@ -227,9 +228,9 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv18[] = { "tageditor", "get", "album", "title", "diskpos", "--verbose", "--fi" };
|
||||
try {
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(7, argv18);
|
||||
parser.parseArgs(7, argv18, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT_EQUAL("The specified argument \"--fi\" is unknown.\nDid you mean --files or --no-color?"s, string(e.what()));
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ void ArgumentParserTests::testParsing()
|
|||
#endif
|
||||
parser.resetArgs();
|
||||
EscapeCodes::enabled = false;
|
||||
parser.parseArgs(6, argv3);
|
||||
parser.parseArgs(6, argv3, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
|
||||
// none of the arguments should be present now
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
@ -260,7 +261,7 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv4[] = { "tageditor", "-i", "-vf", "test" };
|
||||
parser.setUnknownArgumentBehavior(UnknownArgumentBehavior::Fail);
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(4, argv4);
|
||||
parser.parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(verboseArg.isPresent());
|
||||
|
@ -274,9 +275,9 @@ void ArgumentParserTests::testParsing()
|
|||
displayFileInfoArg.reset();
|
||||
fileArg.reset();
|
||||
try {
|
||||
parser.parseArgs(4, argv4);
|
||||
parser.parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" mustn't be specified more than 1 time."));
|
||||
}
|
||||
|
@ -285,13 +286,13 @@ void ArgumentParserTests::testParsing()
|
|||
displayFileInfoArg.reset();
|
||||
fileArg.reset();
|
||||
verboseArg.setConstraints(0, Argument::varValueCount);
|
||||
parser.parseArgs(4, argv4);
|
||||
parser.parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// constraint checking: mandatory argument
|
||||
verboseArg.setRequired(true);
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(4, argv4);
|
||||
parser.parseArgs(4, argv4, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// contraint checking: error about missing mandatory argument
|
||||
|
@ -300,9 +301,9 @@ void ArgumentParserTests::testParsing()
|
|||
fileArg.reset();
|
||||
verboseArg.reset();
|
||||
try {
|
||||
parser.parseArgs(4, argv5);
|
||||
parser.parseArgs(4, argv5, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "The argument \"verbose\" must be specified at least 1 time."));
|
||||
}
|
||||
|
@ -311,7 +312,7 @@ void ArgumentParserTests::testParsing()
|
|||
// combined abbreviation with nesting "-pf"
|
||||
const char *argv10[] = { "tageditor", "-pf", "test" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(3, argv10);
|
||||
parser.parseArgs(3, argv10, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!fileArg.isPresent());
|
||||
|
@ -323,16 +324,16 @@ void ArgumentParserTests::testParsing()
|
|||
// constraint checking: no complains about missing -i
|
||||
const char *argv6[] = { "tageditor", "-g" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(2, argv6);
|
||||
parser.parseArgs(2, argv6, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
|
||||
// constraint checking: dependend arguments (-f requires -i or -p)
|
||||
const char *argv7[] = { "tageditor", "-f", "test" };
|
||||
parser.resetArgs();
|
||||
try {
|
||||
parser.parseArgs(3, argv7);
|
||||
parser.parseArgs(3, argv7, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT_EQUAL("The specified argument \"-f\" is unknown.\nDid you mean get or --help?"s, string(e.what()));
|
||||
}
|
||||
|
@ -340,7 +341,7 @@ void ArgumentParserTests::testParsing()
|
|||
// equation sign syntax
|
||||
const char *argv11[] = { "tageditor", "-if=test-v" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(2, argv11);
|
||||
parser.parseArgs(2, argv11, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(fileArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
|
@ -348,7 +349,7 @@ void ArgumentParserTests::testParsing()
|
|||
CPPUNIT_ASSERT_EQUAL("test-v"s, string(fileArg.values(0).front()));
|
||||
const char *argv15[] = { "tageditor", "-i", "--file=test", "-v" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(4, argv15);
|
||||
parser.parseArgs(4, argv15, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(fileArg.isPresent());
|
||||
CPPUNIT_ASSERT(verboseArg.isPresent());
|
||||
|
@ -358,7 +359,7 @@ void ArgumentParserTests::testParsing()
|
|||
// specifying value directly after abbreviation
|
||||
const char *argv12[] = { "tageditor", "-iftest" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(2, argv12);
|
||||
parser.parseArgs(2, argv12, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(fileArg.isPresent());
|
||||
CPPUNIT_ASSERT_EQUAL(1_st, fileArg.values(0).size());
|
||||
|
@ -367,7 +368,7 @@ void ArgumentParserTests::testParsing()
|
|||
// specifying top-level argument after abbreviation
|
||||
const char *argv17[] = { "tageditor", "-if=test-v", "--no-color" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(3, argv17);
|
||||
parser.parseArgs(3, argv17, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(!filesArg.isPresent());
|
||||
CPPUNIT_ASSERT(fileArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
|
@ -378,7 +379,7 @@ void ArgumentParserTests::testParsing()
|
|||
// default argument
|
||||
const char *argv8[] = { "tageditor" };
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(1, argv8);
|
||||
parser.parseArgs(1, argv8, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(!verboseArg.isPresent());
|
||||
|
@ -396,7 +397,7 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv13[] = { "tageditor", "get", "--fields", "album=test", "title", "diskpos", "--files", "somefile" };
|
||||
verboseArg.setRequired(false);
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(8, argv13);
|
||||
parser.parseArgs(8, argv13, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
// this should still work without complaints
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!displayFileInfoArg.isPresent());
|
||||
|
@ -416,9 +417,9 @@ void ArgumentParserTests::testParsing()
|
|||
fieldsArg.setRequiredValueCount(4);
|
||||
parser.resetArgs();
|
||||
try {
|
||||
parser.parseArgs(5, argv9);
|
||||
parser.parseArgs(5, argv9, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"s,
|
||||
|
@ -430,9 +431,9 @@ void ArgumentParserTests::testParsing()
|
|||
fieldsArg.setRequiredValueCount(Argument::varValueCount);
|
||||
parser.resetArgs();
|
||||
try {
|
||||
parser.parseArgs(6, argv16);
|
||||
parser.parseArgs(6, argv16, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_FAIL("Exception expected.");
|
||||
} catch (const Failure &e) {
|
||||
} catch (const ParseError &e) {
|
||||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT_EQUAL("The specified argument \"--hel\" is unknown.\nDid you mean --help or get?"s, string(e.what()));
|
||||
}
|
||||
|
@ -441,7 +442,7 @@ void ArgumentParserTests::testParsing()
|
|||
const char *argv14[] = { "tageditor", "get", "fields", "album=test", "-f", "somefile" };
|
||||
parser.resetArgs();
|
||||
fieldsArg.setDenotesOperation(true);
|
||||
parser.parseArgs(6, argv14);
|
||||
parser.parseArgs(6, argv14, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
|
||||
|
@ -449,7 +450,7 @@ void ArgumentParserTests::testParsing()
|
|||
// implicit flag still works when argument doesn't denote operation
|
||||
parser.resetArgs();
|
||||
fieldsArg.setDenotesOperation(false);
|
||||
parser.parseArgs(6, argv14);
|
||||
parser.parseArgs(6, argv14, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "fields"));
|
||||
|
@ -480,7 +481,7 @@ void ArgumentParserTests::testCallbacks()
|
|||
// test whether callback is invoked when argument with callback is specified
|
||||
const char *argv[] = { "test", "-t", "val1", "val2" };
|
||||
try {
|
||||
parser.parseArgs(4, argv);
|
||||
parser.parseArgs(4, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
} catch (int i) {
|
||||
CPPUNIT_ASSERT_EQUAL(i, 42);
|
||||
}
|
||||
|
@ -488,7 +489,7 @@ void ArgumentParserTests::testCallbacks()
|
|||
// test whether callback is not invoked when argument with callback is not specified
|
||||
callbackArg.reset();
|
||||
const char *argv2[] = { "test", "-l", "val1", "val2" };
|
||||
parser.parseArgs(4, argv2);
|
||||
parser.parseArgs(4, argv2, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
|
@ -816,7 +817,7 @@ void ArgumentParserTests::testHelp()
|
|||
"\n"
|
||||
"Project website: " APP_URL "\n");
|
||||
EscapeCodes::enabled = true;
|
||||
parser.parseArgs(2, argv);
|
||||
parser.parseArgs(2, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
}
|
||||
|
||||
verboseArg.setDenotesOperation(false);
|
||||
|
@ -846,7 +847,7 @@ void ArgumentParserTests::testHelp()
|
|||
"Project website: " APP_URL "\n");
|
||||
EscapeCodes::enabled = false;
|
||||
parser.resetArgs();
|
||||
parser.parseArgs(2, argv);
|
||||
parser.parseArgs(2, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -942,13 +943,13 @@ void ArgumentParserTests::testValueConversion()
|
|||
try {
|
||||
occurrence.convertValues<string, unsigned int, double, int, int>();
|
||||
CPPUNIT_FAIL("Expected exception");
|
||||
} catch (const Failure &failure) {
|
||||
} catch (const ParseError &failure) {
|
||||
CPPUNIT_ASSERT_EQUAL("Expected 5 top-level values to be present but only 4 have been specified."s, string(failure.what()));
|
||||
}
|
||||
try {
|
||||
occurrence.convertValues<int>();
|
||||
CPPUNIT_FAIL("Expected exception");
|
||||
} catch (const Failure &failure) {
|
||||
} catch (const ParseError &failure) {
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"Conversion of top-level value \"foo\" to type \"i\" failed: The character \"f\" is no valid digit."s, string(failure.what()));
|
||||
}
|
||||
|
@ -956,13 +957,13 @@ void ArgumentParserTests::testValueConversion()
|
|||
try {
|
||||
occurrence.convertValues<string, unsigned int, double, int, int>();
|
||||
CPPUNIT_FAIL("Expected exception");
|
||||
} catch (const Failure &failure) {
|
||||
} catch (const ParseError &failure) {
|
||||
CPPUNIT_ASSERT_EQUAL("Expected 5 values for argument --test to be present but only 4 have been specified."s, string(failure.what()));
|
||||
}
|
||||
try {
|
||||
occurrence.convertValues<int>();
|
||||
CPPUNIT_FAIL("Expected exception");
|
||||
} catch (const Failure &failure) {
|
||||
} catch (const ParseError &failure) {
|
||||
CPPUNIT_ASSERT_EQUAL("Conversion of value \"foo\" (for argument --test) to type \"i\" failed: The character \"f\" is no valid digit."s,
|
||||
string(failure.what()));
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "./testutils.h"
|
||||
|
||||
#include "../application/failure.h"
|
||||
#include "../conversion/stringbuilder.h"
|
||||
#include "../conversion/stringconversion.h"
|
||||
#include "../io/ansiescapecodes.h"
|
||||
#include "../io/misc.h"
|
||||
#include "../io/nativefilestream.h"
|
||||
#include "../io/path.h"
|
||||
#include "../misc/parseerror.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
@ -146,8 +146,8 @@ TestApplication::TestApplication(int argc, const char *const *argv)
|
|||
|
||||
// parse arguments
|
||||
try {
|
||||
m_parser.parseArgs(argc, argv);
|
||||
} catch (const Failure &failure) {
|
||||
m_parser.parseArgs(argc, argv, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks);
|
||||
} catch (const ParseError &failure) {
|
||||
cerr << failure;
|
||||
m_valid = false;
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue