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:
Martchus 2019-06-10 16:03:27 +02:00
parent 93bdf5b4f1
commit 4c1b733290
9 changed files with 132 additions and 180 deletions

View File

@ -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)

View File

@ -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."));

View File

@ -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);

View File

@ -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

View File

@ -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

34
misc/parseerror.cpp Normal file
View File

@ -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

38
misc/parseerror.h Normal file
View File

@ -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

View File

@ -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()));
}

View File

@ -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;