mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 15:14:39 +00:00
Also fixes the incompatibility between store_into and scan and action: when the three methods above were called, being all based on the (unique) action, the last one would overwrite the previous ones. This issue was making the parser strictly dependant on the order of the scan/store_into/action calls making them mutually exclusive.
476 lines
15 KiB
C++
476 lines
15 KiB
C++
#ifdef WITH_MODULE
|
|
import argparse;
|
|
#else
|
|
#include <argparse/argparse.hpp>
|
|
#endif
|
|
#include <doctest.hpp>
|
|
#include <stdint.h>
|
|
|
|
using doctest::test_suite;
|
|
|
|
TEST_CASE_TEMPLATE("Parse a decimal integer argument" * test_suite("scan"), T,
|
|
int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t,
|
|
uint32_t, uint64_t) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'d', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", "0"});
|
|
REQUIRE(program.get<T>("-n") == 0);
|
|
}
|
|
|
|
SUBCASE("non-negative") {
|
|
program.parse_args({"test", "-n", "5"});
|
|
REQUIRE(program.get<T>("-n") == 5);
|
|
}
|
|
|
|
SUBCASE("negative") {
|
|
if constexpr (std::is_signed_v<T>) {
|
|
program.parse_args({"test", "-n", "-128"});
|
|
REQUIRE(program.get<T>("-n") == -128);
|
|
} else {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-135"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
|
|
SUBCASE("left-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", " 32"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("right-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "12 "}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+12"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(
|
|
program.parse_args({"test", "-n", "987654321987654321987654321"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse an octal integer argument" * test_suite("scan"), T,
|
|
uint8_t, uint16_t, uint32_t, uint64_t) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'o', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", "0"});
|
|
REQUIRE(program.get<T>("-n") == 0);
|
|
}
|
|
|
|
SUBCASE("with octal base") {
|
|
program.parse_args({"test", "-n", "066"});
|
|
REQUIRE(program.get<T>("-n") == 066);
|
|
}
|
|
|
|
SUBCASE("minus sign produces an optional argument") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-003"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+012"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(
|
|
program.parse_args({"test", "-n", "02000000000000000000001"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse a hexadecimal integer argument" * test_suite("scan"),
|
|
T, uint8_t, uint16_t, uint32_t, uint64_t) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'X', T>();
|
|
|
|
SUBCASE("with hex digit") {
|
|
program.parse_args({"test", "-n", "0x1a"});
|
|
REQUIRE(program.get<T>("-n") == 0x1a);
|
|
}
|
|
|
|
SUBCASE("minus sign produces an optional argument") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0x1"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+0x1a"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0XFFFFFFFFFFFFFFFF1"}),
|
|
std::range_error);
|
|
}
|
|
|
|
SUBCASE("with hex digit without prefix") {
|
|
program.parse_args({"test", "-n", "1a"});
|
|
REQUIRE(program.get<T>("-n") == 0x1a);
|
|
}
|
|
|
|
SUBCASE("minus sign without prefix produces an optional argument") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-1"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("plus sign without prefix is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+1a"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("without prefix does not fit") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "FFFFFFFFFFFFFFFF1"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Parse multiple hex numbers without prefix" * test_suite("scan")) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-x", "--hex")
|
|
.help("bytes in hex separated by spaces")
|
|
.nargs(1, std::numeric_limits<std::size_t>::max())
|
|
.scan<'x', uint8_t>();
|
|
|
|
REQUIRE_NOTHROW(
|
|
program.parse_args({"test", "-x", "f2", "b2", "10", "80", "64"}));
|
|
const auto &input_bytes = program.get<std::vector<uint8_t>>("-x");
|
|
REQUIRE((input_bytes == std::vector<uint8_t>{0xf2, 0xb2, 0x10, 0x80, 0x64}));
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse integer argument of any format" * test_suite("scan"),
|
|
T, int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t,
|
|
uint32_t, uint64_t) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'i', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", "0"});
|
|
REQUIRE(program.get<T>("-n") == 0);
|
|
}
|
|
|
|
SUBCASE("octal") {
|
|
program.parse_args({"test", "-n", "077"});
|
|
REQUIRE(program.get<T>("-n") == 077);
|
|
}
|
|
|
|
SUBCASE("no negative octal") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0777"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("hex") {
|
|
program.parse_args({"test", "-n", "0X2c"});
|
|
REQUIRE(program.get<T>("-n") == 0X2c);
|
|
}
|
|
|
|
SUBCASE("no negative hex") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0X2A"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("decimal") {
|
|
program.parse_args({"test", "-n", "98"});
|
|
REQUIRE(program.get<T>("-n") == 98);
|
|
}
|
|
|
|
SUBCASE("negative decimal") {
|
|
if constexpr (std::is_signed_v<T>) {
|
|
program.parse_args({"test", "-n", "-39"});
|
|
REQUIRE(program.get<T>("-n") == -39);
|
|
} else {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-39"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
|
|
SUBCASE("left-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "\t32"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("right-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "32\n"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+670"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse a binary argument" * test_suite("scan"), T, uint8_t,
|
|
uint16_t, uint32_t, uint64_t) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'b', T>();
|
|
|
|
SUBCASE("with binary digit") {
|
|
program.parse_args({"test", "-n", "0b101"});
|
|
REQUIRE(program.get<T>("-n") == 0b101);
|
|
}
|
|
|
|
SUBCASE("minus sign produces an optional argument") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0b101"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+0b101"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(
|
|
program.parse_args(
|
|
{"test", "-n",
|
|
"0b111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
|
"1111111111111111111111111111111111111111111111111111111111111111"
|
|
"1"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
#define FLOAT_G(t, literal) \
|
|
([] { \
|
|
if constexpr (std::is_same_v<t, float>) \
|
|
return literal##f; \
|
|
else if constexpr (std::is_same_v<t, double>) \
|
|
return literal; \
|
|
else if constexpr (std::is_same_v<t, long double>) \
|
|
return literal##l; \
|
|
}())
|
|
|
|
TEST_CASE_TEMPLATE("Parse floating-point argument of general format" *
|
|
test_suite("scan"),
|
|
T, float, double, long double) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'g', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", "0"});
|
|
REQUIRE(program.get<T>("-n") == 0.);
|
|
}
|
|
|
|
SUBCASE("non-negative") {
|
|
program.parse_args({"test", "-n", "3.14"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, 3.14));
|
|
}
|
|
|
|
SUBCASE("negative") {
|
|
program.parse_args({"test", "-n", "-0.12"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, -0.12));
|
|
}
|
|
|
|
SUBCASE("left-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "\t.32"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("right-padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", ".32\n"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+.12"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("plus sign after padding is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", " +.12"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("hexfloat is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0x1a.3p+1"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "1.3e+5000"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse hexadecimal floating-point argument" *
|
|
test_suite("scan"),
|
|
T, float, double, long double) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'a', T>();
|
|
|
|
SUBCASE("zero") {
|
|
// binary-exponent-part is not optional in C++ grammar
|
|
program.parse_args({"test", "-n", "0x0"});
|
|
REQUIRE(program.get<T>("-n") == 0x0.p0);
|
|
}
|
|
|
|
SUBCASE("non-negative") {
|
|
program.parse_args({"test", "-n", "0x1a.3p+1"});
|
|
REQUIRE(program.get<T>("-n") == 0x1a.3p+1);
|
|
}
|
|
|
|
SUBCASE("minus sign produces an optional argument") {
|
|
// XXX may worth a fix
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0x0.12p1"}),
|
|
std::runtime_error);
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+0x1p0"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("general format is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "3.14"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse floating-point argument of scientific format" *
|
|
test_suite("scan"),
|
|
T, float, double, long double) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'e', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", "0e0"});
|
|
REQUIRE(program.get<T>("-n") == 0e0);
|
|
}
|
|
|
|
SUBCASE("non-negative") {
|
|
program.parse_args({"test", "-n", "3.14e-1"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, 3.14e-1));
|
|
}
|
|
|
|
SUBCASE("negative") {
|
|
program.parse_args({"test", "-n", "-0.12e+1"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, -0.12e+1));
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+.12e+1"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("fixed format is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "3.14"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("hexfloat is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0x1.33p+0"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("does not fit") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "1.3e+5000"}),
|
|
std::range_error);
|
|
}
|
|
}
|
|
|
|
TEST_CASE_TEMPLATE("Parse floating-point argument of fixed format" *
|
|
test_suite("scan"),
|
|
T, float, double, long double) {
|
|
argparse::ArgumentParser program("test");
|
|
program.add_argument("-n").scan<'f', T>();
|
|
|
|
SUBCASE("zero") {
|
|
program.parse_args({"test", "-n", ".0"});
|
|
REQUIRE(program.get<T>("-n") == .0);
|
|
}
|
|
|
|
SUBCASE("non-negative") {
|
|
program.parse_args({"test", "-n", "3.14"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, 3.14));
|
|
}
|
|
|
|
SUBCASE("negative") {
|
|
program.parse_args({"test", "-n", "-0.12"});
|
|
REQUIRE(program.get<T>("-n") == FLOAT_G(T, -0.12));
|
|
}
|
|
|
|
SUBCASE("plus sign is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+.12"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("scientific format is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "3.14e+0"}),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
SUBCASE("hexfloat is not allowed") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0x1.33p+0"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Test that scan also works with a custom action" *
|
|
test_suite("scan")) {
|
|
|
|
GIVEN("an argument with scan followed by a custom action") {
|
|
argparse::ArgumentParser program("test");
|
|
int res;
|
|
program.add_argument("--int").scan<'i', int>().action([&](const auto &s) {res = std::stoi(s);});
|
|
|
|
WHEN("the argument is parsed") {
|
|
|
|
SUBCASE("with a valid value") {
|
|
program.parse_args({"./test.exe", "--int", "3"});
|
|
THEN("the value is stored") {
|
|
REQUIRE(res == 3);
|
|
}
|
|
}
|
|
|
|
SUBCASE("with an invalid value") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
}
|
|
|
|
GIVEN("an argument with a custom action followed by scan") {
|
|
argparse::ArgumentParser program("test");
|
|
int res;
|
|
program.add_argument("--int").action([&](const auto &s) {res = std::stoi(s);}).scan<'i', int>();
|
|
|
|
WHEN("the argument is parsed") {
|
|
|
|
SUBCASE("with a valid value") {
|
|
program.parse_args({"./test.exe", "--int", "3"});
|
|
THEN("the value is stored") {
|
|
REQUIRE(res == 3);
|
|
}
|
|
}
|
|
|
|
SUBCASE("with an invalid value") {
|
|
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
|
|
std::invalid_argument);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|