poaflocParser Of Arguments For Lines Of Commands |
git clone git://git.dimitrijedobrota.com/poafloc.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 8276d5c6b044d80e2f96ba4b0ac01956bf59ca85 |
parent | 0ccb06358841f2d7b833bce0e5a5dca850597f1c |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Sat, 24 May 2025 10:27:28 +0200 |
Test suite for parser
* Rename error_t to error_code due to a conflict with std
M | include/poafloc/error.hpp | | | +++++++++ --------- |
M | include/poafloc/poafloc.hpp | | | ++++++++++ ---------- |
M | test/CMakeLists.txt | | | +++++ --- |
A | test/source/parser.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 402 insertions(+), 22 deletions(-)
diff --git a/ include/poafloc/error.hpp b/ include/poafloc/error.hpp
@@ -13,22 +13,22 @@
namespace poafloc
#define ENUM_ERROR \
invalid_char, missing_argument, superfluous_argument, unknown_option, \
duplicate_option
BASED_DECLARE_ENUM(error_t, based::bu8, 0, ENUM_ERROR)
BASED_DEFINE_ENUM(error_t, based::bu8, 0, ENUM_ERROR)
BASED_DECLARE_ENUM(error_code, based::bu8, 0, ENUM_ERROR)
BASED_DEFINE_ENUM(error_code, based::bu8, 0, ENUM_ERROR)
#undef ENUM_ERROR
static constexpr const char* error_get_message(error_t::enum_type error)
static constexpr const char* error_get_message(error_code::enum_type error)
{
switch (error()) {
case error_t::invalid_char():
case error_code::invalid_char():
return "Invalid char in option: {}";
case error_t::missing_argument():
case error_code::missing_argument():
return "Missing argument for option: {}";
case error_t::superfluous_argument():
case error_code::superfluous_argument():
return "Option doesn't require an argument: {}";
case error_t::unknown_option():
case error_code::unknown_option():
return "Unknown option: {}";
case error_t::duplicate_option():
case error_code::duplicate_option():
return "Duplicate option: {}";
default:
return "poafloc error, should not happen...";
@@ -44,7 +44,7 @@
public:
}
};
template<error_t::enum_type e>
template<error_code::enum_type e>
class error : public runtime_error
{
public:
diff --git a/ include/poafloc/poafloc.hpp b/ include/poafloc/poafloc.hpp
@@ -143,7 +143,7 @@
struct option_base
static auto convert(char chr)
{
if (!is_valid(chr)) {
throw error<error_t::invalid_char>(chr);
throw error<error_code::invalid_char>(chr);
}
return mapping[static_cast<container_type::size_type>(
@@ -274,12 +274,12 @@
class parser
if (std::size(str) == 1) {
const auto& opt = str[0];
if (!m_opt_short.set(opt, std::size(m_options))) {
throw error<error_t::duplicate_option>(opt);
throw error<error_code::duplicate_option>(opt);
}
} else {
const auto& opt = str;
if (!m_opt_long.set(opt, std::size(m_options))) {
throw error<error_t::duplicate_option>(opt);
throw error<error_code::duplicate_option>(opt);
}
}
}
@@ -291,7 +291,7 @@
class parser
{
const auto idx = m_opt_short.get(opt);
if (!idx.has_value()) {
throw error<error_t::unknown_option>(opt);
throw error<error_code::unknown_option>(opt);
}
return m_options[idx.value()];
}
@@ -300,7 +300,7 @@
class parser
{
const auto idx = m_opt_long.get(opt);
if (!idx.has_value()) {
throw error<error_t::unknown_option>(opt);
throw error<error_code::unknown_option>(opt);
}
return m_options[idx.value()];
}
@@ -330,7 +330,7 @@
class parser
const auto option = get_option(opt);
if (!option.argument()) {
throw error<error_t::superfluous_argument>(opt);
throw error<error_code::superfluous_argument>(opt);
}
if (!value.empty()) {
@@ -338,7 +338,7 @@
class parser
return handle_res::ok;
}
throw error<error_t::missing_argument>(opt);
throw error<error_code::missing_argument>(opt);
}
const auto option = get_option(arg);
@@ -353,7 +353,7 @@
class parser
return handle_res::next;
}
throw error<error_t::missing_argument>(arg);
throw error<error_code::missing_argument>(arg);
}
handle_res handle_short_opts(
@@ -379,7 +379,7 @@
class parser
return handle_res::next;
}
throw error<error_t::missing_argument>(opt);
throw error<error_code::missing_argument>(opt);
}
return handle_res::ok;
@@ -409,7 +409,7 @@
public:
const auto arg_raw = args[arg_idx];
if (arg_raw.size() < 2) {
throw error<error_t::unknown_option>(arg_raw);
throw error<error_code::unknown_option>(arg_raw);
}
if (arg_raw == "--") {
diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -17,14 +17,16 @@
include(Catch)
# ---- Tests ----
function(add_test DIR NAME)
add_executable("${NAME}" "source/${DIR}/${NAME}.cpp")
target_link_libraries("${NAME}" PRIVATE based::based)
function(add_test NAME)
add_executable("${NAME}" "source/${NAME}.cpp")
target_link_libraries("${NAME}" PRIVATE poafloc::poafloc)
target_link_libraries("${NAME}" PRIVATE Catch2::Catch2WithMain)
target_compile_features("${NAME}" PRIVATE cxx_std_20)
catch_discover_tests("${NAME}")
endfunction()
add_test(parser)
# ---- End-of-file commands ----
add_folders(Test)
diff --git a/ test/source/parser.cpp b/ test/source/parser.cpp
@@ -0,0 +1,378 @@
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include <string_view>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "poafloc/poafloc.hpp"
using poafloc::error;
using poafloc::error_code;
using poafloc::option;
using poafloc::parser;
// NOLINTBEGIN(*complexity*)
TEST_CASE("invalid", "[poafloc/parser]")
{
struct arguments
{
bool flag;
int value;
};
SECTION("duplicate short")
{
const auto construct = []()
{
return parser<arguments> {
option {"f flag", &arguments::flag},
option {"f follow", &arguments::value},
};
};
REQUIRE_THROWS_AS(construct(), error<error_code::duplicate_option>);
}
SECTION("duplicate long")
{
const auto construct = []()
{
return parser<arguments> {
option {"f flag", &arguments::flag},
option {"v flag", &arguments::value},
};
};
REQUIRE_THROWS_AS(construct(), error<error_code::duplicate_option>);
}
}
TEST_CASE("flag", "[poafloc/parser]")
{
struct arguments
{
bool flag = false;
} args;
const auto program = parser<arguments> {
option {"f flag", &arguments::flag},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-f"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--flag"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--fl"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag == true);
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag == false);
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag == false);
}
SECTION("long superfluous")
{
std::vector<std::string_view> cmdline = {"--fl=something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_argument>);
REQUIRE(args.flag == false);
}
SECTION("long superfluous missing")
{
std::vector<std::string_view> cmdline = {"--fl="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::superfluous_argument>);
REQUIRE(args.flag == false);
}
}
TEST_CASE("option string", "[poafloc/parser]")
{
struct arguments
{
std::string name = "default";
} args;
const auto program = parser<arguments> {
option {"n name", &arguments::name},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-n", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("short together")
{
std::vector<std::string_view> cmdline = {"-nsomething"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--name", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--na", "something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long equal")
{
std::vector<std::string_view> cmdline = {"--name=something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("long equal partial")
{
std::vector<std::string_view> cmdline = {"--na=something"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.name == "something");
}
SECTION("short missing")
{
std::vector<std::string_view> cmdline = {"-n"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long missing")
{
std::vector<std::string_view> cmdline = {"--name"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long partial missing")
{
std::vector<std::string_view> cmdline = {"--na"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long equal missing")
{
std::vector<std::string_view> cmdline = {"--name="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("long equal partial missing")
{
std::vector<std::string_view> cmdline = {"--na="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.name == "default");
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u", "something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown", "something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
SECTION("long equal unknown")
{
std::vector<std::string_view> cmdline = {"--unknown=something"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.name == "default");
}
}
TEST_CASE("option value", "[poafloc/parser]")
{
struct arguments
{
int value = 0;
} args;
const auto program = parser<arguments> {
option {"v value", &arguments::value},
};
SECTION("short")
{
std::vector<std::string_view> cmdline = {"-v", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("short together")
{
std::vector<std::string_view> cmdline = {"-v135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long")
{
std::vector<std::string_view> cmdline = {"--value", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long partial")
{
std::vector<std::string_view> cmdline = {"--val", "135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long equal")
{
std::vector<std::string_view> cmdline = {"--value=135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("long equal partial")
{
std::vector<std::string_view> cmdline = {"--val=135"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.value == 135);
}
SECTION("short missing")
{
std::vector<std::string_view> cmdline = {"-v"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long missing")
{
std::vector<std::string_view> cmdline = {"--value"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long partial missing")
{
std::vector<std::string_view> cmdline = {"--val"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long equal missing")
{
std::vector<std::string_view> cmdline = {"--value="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("long equal partial missing")
{
std::vector<std::string_view> cmdline = {"--val="};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::missing_argument>);
REQUIRE(args.value == 0);
}
SECTION("short unknown")
{
std::vector<std::string_view> cmdline = {"-u", "135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
SECTION("long unknown")
{
std::vector<std::string_view> cmdline = {"--unknown", "135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
SECTION("long equal unknown")
{
std::vector<std::string_view> cmdline = {"--unknown=135"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.value == 0);
}
}
TEST_CASE("multiple", "[poafloc/parser]")
{
struct arguments
{
bool flag1 = false;
bool flag2 = false;
std::string value1 = "default";
std::string value2 = "default";
} args;
const auto program = parser<arguments> {
option {"f flag1", &arguments::flag1},
option {"F flag2", &arguments::flag2},
option {"v value1", &arguments::value1},
option {"V value2", &arguments::value2},
};
SECTION("valid")
{
std::vector<std::string_view> cmdline = {"--flag1", "--flag2"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == true);
REQUIRE(args.value1 == "default");
REQUIRE(args.value2 == "default");
}
SECTION("partial overlap")
{
std::vector<std::string_view> cmdline = {"--fla", "--fla"};
REQUIRE_THROWS_AS(program(args, cmdline), error<error_code::unknown_option>);
REQUIRE(args.flag1 == false);
REQUIRE(args.flag2 == false);
REQUIRE(args.value1 == "default");
REQUIRE(args.value2 == "default");
}
SECTION("together")
{
std::vector<std::string_view> cmdline = {"-fvF"};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(args.flag1 == true);
REQUIRE(args.flag2 == false);
REQUIRE(args.value1 == "F");
REQUIRE(args.value2 == "default");
}
}
// NOLINTEND(*complexity*)