From 4c1b733290231a9e034b81671c1972ed7431af35 Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 10 Jun 2019 16:03:27 +0200 Subject: [PATCH] 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 --- CMakeLists.txt | 4 +- application/argumentparser.cpp | 53 +++++---------------- application/argumentparser.h | 4 +- application/failure.cpp | 60 ------------------------ application/failure.h | 28 ----------- misc/parseerror.cpp | 34 ++++++++++++++ misc/parseerror.h | 38 +++++++++++++++ tests/argumentparsertests.cpp | 85 +++++++++++++++++----------------- tests/testutils.cpp | 6 +-- 9 files changed, 132 insertions(+), 180 deletions(-) delete mode 100644 application/failure.cpp delete mode 100644 application/failure.h create mode 100644 misc/parseerror.cpp create mode 100644 misc/parseerror.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 26deeaf..8aad43a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/application/argumentparser.cpp b/application/argumentparser.cpp index ad637a5..f662f43 100644 --- a/application/argumentparser.cpp +++ b/application/argumentparser.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 #include @@ -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(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 &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.")); diff --git a/application/argumentparser.h b/application/argumentparser.h index 91ff19d..e15d903 100644 --- a/application/argumentparser.h +++ b/application/argumentparser.h @@ -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); diff --git a/application/failure.cpp b/application/failure.cpp deleted file mode 100644 index 29c3937..0000000 --- a/application/failure.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "./failure.h" - -#include "../io/ansiescapecodes.h" - -#include - -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 diff --git a/application/failure.h b/application/failure.h deleted file mode 100644 index 2c6d49f..0000000 --- a/application/failure.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef APPLICATION_UTILITIES_FAILURE_H -#define APPLICATION_UTILITIES_FAILURE_H - -#include "../global.h" - -#include -#include -#include - -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 diff --git a/misc/parseerror.cpp b/misc/parseerror.cpp new file mode 100644 index 0000000..b90a22c --- /dev/null +++ b/misc/parseerror.cpp @@ -0,0 +1,34 @@ +#include "./parseerror.h" + +#include "../io/ansiescapecodes.h" + +#include + +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 diff --git a/misc/parseerror.h b/misc/parseerror.h new file mode 100644 index 0000000..6d579d7 --- /dev/null +++ b/misc/parseerror.h @@ -0,0 +1,38 @@ +#ifndef APPLICATION_UTILITIES_PARSE_ERROR_H +#define APPLICATION_UTILITIES_PARSE_ERROR_H + +#include "../global.h" + +#include +#include + +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 diff --git a/tests/argumentparsertests.cpp b/tests/argumentparsertests.cpp index 44eb883..2921a1a 100644 --- a/tests/argumentparsertests.cpp +++ b/tests/argumentparsertests.cpp @@ -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 @@ -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(); 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(); 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(); 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(); 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())); } diff --git a/tests/testutils.cpp b/tests/testutils.cpp index 051ae49..514ce10 100644 --- a/tests/testutils.cpp +++ b/tests/testutils.cpp @@ -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 #include @@ -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;