#include "argparser/argparser.h" #include "gtest/gtest.h" // TODO: test empty input for all types TEST(Types, AutomaticUint8Type) { argparser::parser p{}; argparser::type_handle_impl uint8Type; argparser::parse_result pr; argparser::single_arg_handle arg; uint8_t val; EXPECT_NO_THROW({ uint8Type = p.basic_type("uint8"); arg = p.arg("a", uint8Type); pr = p.parse({"250"}); val = arg->get(pr); }); EXPECT_EQ(val, 250); } TEST(Types, AutomaticInt8Type) { argparser::parser p{}; argparser::type_handle_impl int8Type; argparser::parse_result pr; argparser::single_arg_handle arg; int8_t val; EXPECT_NO_THROW({ int8Type = p.basic_type("int8"); arg = p.arg("a", int8Type); pr = p.parse({"-126"}); val = arg->get(pr); }); EXPECT_EQ(val, -126); } TEST(Types, AutomaticUint16Type) { argparser::parser p{}; argparser::type_handle_impl uint16Type; argparser::parse_result pr; argparser::single_arg_handle arg; uint16_t val; EXPECT_NO_THROW({ uint16Type = p.basic_type("uint16"); arg = p.arg("a", uint16Type); pr = p.parse({"65530"}); val = arg->get(pr); }); EXPECT_EQ(val, 65530); } TEST(Types, AutomaticInt16Type) { argparser::parser p{}; argparser::type_handle_impl int16Type; argparser::parse_result pr; argparser::single_arg_handle arg; int16_t val; EXPECT_NO_THROW({ int16Type = p.basic_type("int16"); arg = p.arg("a", int16Type); pr = p.parse({"-32760"}); val = arg->get(pr); }); EXPECT_EQ(val, -32760); } TEST(Types, AutomaticUint32Type) { argparser::parser p{}; argparser::type_handle_impl uint32Type; argparser::parse_result pr; argparser::single_arg_handle arg; uint32_t val; EXPECT_NO_THROW({ uint32Type = p.basic_type("uint32"); arg = p.arg("a", uint32Type); pr = p.parse({"4294967290"}); val = arg->get(pr); }); EXPECT_EQ(val, 4294967290); } TEST(Types, AutomaticInt32Type) { argparser::parser p{}; argparser::type_handle_impl int32Type; argparser::parse_result pr; argparser::single_arg_handle arg; int32_t val; EXPECT_NO_THROW({ int32Type = p.basic_type("int32"); arg = p.arg("a", int32Type); pr = p.parse({"-2147483645"}); val = arg->get(pr); }); EXPECT_EQ(val, -2147483645); } TEST(Types, AutomaticUint64Type) { argparser::parser p{}; argparser::type_handle_impl uint64Type; argparser::parse_result pr; argparser::single_arg_handle arg; uint64_t val; EXPECT_NO_THROW({ uint64Type = p.basic_type("uint64"); arg = p.arg("a", uint64Type); pr = p.parse({"18446744073709551612"}); val = arg->get(pr); }); EXPECT_EQ(val, 18446744073709551612ull); } TEST(Types, AutomaticInt64Type) { argparser::parser p{}; argparser::type_handle_impl int64Type; argparser::parse_result pr; argparser::single_arg_handle arg; int64_t val; EXPECT_NO_THROW({ int64Type = p.basic_type("int64"); arg = p.arg("a", int64Type); pr = p.parse({"-9223372036854775803"}); val = arg->get(pr); }); EXPECT_EQ(val, -9223372036854775803); } TEST(Types, AutomaticFloatType) { argparser::parser p{}; argparser::type_handle_impl floatType; argparser::parse_result pr; argparser::single_arg_handle arg; float val; EXPECT_NO_THROW({ floatType = p.basic_type("float"); arg = p.arg("a", floatType); pr = p.parse({"23.45"}); val = arg->get(pr); }); EXPECT_EQ(val, 23.45f); } TEST(Types, AutomaticDoubleType) { argparser::parser p{}; argparser::type_handle_impl doubleType; argparser::parse_result pr; argparser::single_arg_handle arg; double val; EXPECT_NO_THROW({ doubleType = p.basic_type("double"); arg = p.arg("a", doubleType); pr = p.parse({"23.45"}); val = arg->get(pr); }); EXPECT_EQ(val, 23.45); } TEST(Types, AutomaticStringType) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"foo bar"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"'foo bar'"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"\"foo bar\""})); EXPECT_EQ(arg->get(pr), "foo bar"); } TEST(Types, AutomaticStringTypeBareEmptyString) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({""})); EXPECT_EQ(arg->get(pr), ""); } TEST(Types, AutomaticStringTypeSingleQuotedEmptyString) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"''"})); EXPECT_EQ(arg->get(pr), ""); EXPECT_THROW(pr = p.parse({""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringTypeDoubleQuotedEmptyString) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"\"\""})); EXPECT_EQ(arg->get(pr), ""); EXPECT_THROW(pr = p.parse({""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringTypeBrokenQuote) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_THROW(pr = p.parse({"'foobar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"\"foobar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"foo'bar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"foo\"bar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"foobar'"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"foobar\""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringTypeBareComma) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"foo,bar"})); EXPECT_EQ(arg->get(pr), "foo,bar"); } TEST(Types, AutomaticStringTypeNoBareCommaInTuple) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("tuple", stringType, uintType); auto arg = p.arg("a", tupleType); argparser::parse_result pr; EXPECT_THROW(pr = p.parse({"tuple(foo,bar, 2)"}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringTypeBareWithWhitespaceInTuple) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("tuple", stringType, uintType); auto arg = p.arg("a", tupleType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"tuple( foo bar , 2)"})); EXPECT_EQ(arg->get(pr), std::make_tuple(" foo bar ", 2)); } TEST(Types, AutomaticStringTypeExplicitAny) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"foo bar"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"'foo bar'"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"\"foo bar\""})); EXPECT_EQ(arg->get(pr), "foo bar"); } TEST(Types, AutomaticStringOnlyBare) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"foo bar"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"'foo bar'"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"\"foo bar\""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringOnlySingleQuoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"'foo bar'"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"foo bar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"\"foo bar\""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringOnlyDoubleQuoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"\"foo bar\""})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"foo bar"}), argparser::errors::type_parsing_error); EXPECT_THROW(pr = p.parse({"'foo bar'"}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringOnlyQuoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"\"foo bar\""})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"'foo bar'"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"foo bar"}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringOnlyBareOrSingleQuoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"'foo bar'"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"foo bar"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"\"foo bar\""}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringOnlyBareOrDoubleQuoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); argparser::parse_result pr; EXPECT_NO_THROW(pr = p.parse({"\"foo bar\""})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_NO_THROW(pr = p.parse({"foo bar"})); EXPECT_EQ(arg->get(pr), "foo bar"); EXPECT_THROW(pr = p.parse({"'foo bar'"}), argparser::errors::type_parsing_error); } TEST(Types, AutomaticStringTypeUnquotedInTuple) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto tupleType = p.tuple_type("tuple", stringType, stringType); auto arg = p.arg("a", tupleType); auto pr = p.parse({"(foo bar, asdf)"}); auto val = arg->get(pr); EXPECT_EQ(val, std::make_tuple("foo bar", " asdf")); } TEST(Types, AutomaticStringTypeTrim) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); auto pr = p.parse({" foo "}); EXPECT_EQ(arg->get(pr), "foo"); pr = p.parse({"' foo '"}); EXPECT_EQ(arg->get(pr), "foo"); pr = p.parse({"\" foo \""}); EXPECT_EQ(arg->get(pr), "foo"); } TEST(Types, AutomaticStringTypeTrimOnlyWhitespace) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); auto pr = p.parse({" "}); EXPECT_EQ(arg->get(pr), ""); pr = p.parse({"' '"}); EXPECT_EQ(arg->get(pr), ""); pr = p.parse({"\" \""}); EXPECT_EQ(arg->get(pr), ""); } TEST(Types, AutomaticStringTypeTrimUnquoted) { argparser::parser p{}; auto stringType = p.basic_type("string"); auto arg = p.arg("a", stringType); auto pr = p.parse({" foo "}); EXPECT_EQ(arg->get(pr), "foo"); pr = p.parse({"' foo '"}); EXPECT_EQ(arg->get(pr), " foo "); pr = p.parse({"\" foo \""}); EXPECT_EQ(arg->get(pr), " foo "); } TEST(Types, EnumType) { argparser::parser p; auto boolType = p.enum_type("bool", {{"true", true}, {"yes", true}, {"false", false}, {"no", false}}); auto fooOption = p.option("--foo", boolType); auto parsed = p.parse({"--foo", "true"}); EXPECT_EQ(fooOption->get(parsed), true); parsed = p.parse({"--foo", "false"}); EXPECT_EQ(fooOption->get(parsed), false); parsed = p.parse({"--foo", "yes"}); EXPECT_EQ(fooOption->get(parsed), true); parsed = p.parse({"--foo", "no"}); EXPECT_EQ(fooOption->get(parsed), false); } TEST(Types, EnumTypeEmptyError) { argparser::parser p; EXPECT_THROW(p.enum_type("enum1", {}), argparser::errors::empty_enum_map_error); EXPECT_THROW(p.enum_type("enum1", {}), argparser::errors::empty_enum_map_error); } TEST(Types, EnumTypeLongestMatching) { argparser::parser p; auto enumType = p.enum_type("enum", { {"a", 1}, {"ab", 2}, }); auto fooOption = p.option("--foo", enumType); auto parsed = p.parse({"--foo", "a"}); EXPECT_EQ(fooOption->get(parsed), 1); parsed = p.parse({"--foo", "ab"}); EXPECT_EQ(fooOption->get(parsed), 2); } TEST(Types, EnumTypeLongestMatchingReverseOrder) { argparser::parser p; auto enumType = p.enum_type("enum", { {"ab", 2}, {"a", 1}, }); auto fooOption = p.option("--foo", enumType); auto parsed = p.parse({"--foo", "a"}); EXPECT_EQ(fooOption->get(parsed), 1); parsed = p.parse({"--foo", "ab"}); EXPECT_EQ(fooOption->get(parsed), 2); } TEST(Types, EnumTypeInvalidValue) { argparser::parser p; auto enumType = p.enum_type("enum", {{"a", 1}, {"b", 2}, {"c", 3}}); auto _1 = p.option("--foo", enumType); EXPECT_THROW(auto _ = p.parse({"--foo", "x"}), argparser::errors::type_parsing_error); EXPECT_THROW(auto _ = p.parse({"--foo", "aa"}), argparser::errors::type_parsing_error); } TEST(Types, StringEnumType) { argparser::parser p; auto enumType = p.enum_type("enum", {"foo", "bar"}); auto fooOption = p.option("--foo", enumType); auto parsed = p.parse({"--foo", "foo"}); EXPECT_EQ(fooOption->get(parsed), "foo"); parsed = p.parse({"--foo", "bar"}); EXPECT_EQ(fooOption->get(parsed), "bar"); } TEST(Types, UnionType) { argparser::parser p; auto enumType1 = p.enum_type("enum", {"foo", "bar"}); auto enumType2 = p.enum_type("bool", {{"true", true}, {"false", false}}); auto unionType = p.union_type("enum-or-bool", enumType1, enumType2); auto fooOption = p.option("--foo", unionType); auto parsed = p.parse({"--foo", "bar"}); auto val = fooOption->get(parsed); EXPECT_EQ(std::holds_alternative(val), true); EXPECT_EQ(std::get(val), "bar"); parsed = p.parse({"--foo", "true"}); val = fooOption->get(parsed); EXPECT_EQ(std::holds_alternative(val), true); EXPECT_EQ(std::get(val), true); } TEST(Types, UnionTypeWithSameUnderlyingBaseType) { argparser::parser p; auto enumType1 = p.enum_type("enum", {"foo", "bar"}); auto enumType2 = p.enum_type("enum", {"hello", "world"}); auto unionType = p.union_type("enum-or-bool", enumType1, enumType2); auto fooOption = p.option("--foo", unionType); auto parsed = p.parse({"--foo", "bar"}); auto val = fooOption->get(parsed); EXPECT_EQ(val, "bar"); parsed = p.parse({"--foo", "hello"}); val = fooOption->get(parsed); EXPECT_EQ(val, "hello"); } TEST(Types, UnionTypeError) { argparser::parser p; auto enumType1 = p.enum_type("enum", {"foo", "bar"}); auto enumType2 = p.enum_type("bool", {{"true", true}, {"false", false}}); auto unionType = p.union_type("union", enumType1, enumType2); auto fooOption = p.option("--foo", unionType); EXPECT_THROW(auto _ = p.parse({"--foo", "123"}), argparser::errors::type_parsing_error); } TEST(Types, UnionTypeErrorOnAmbiguity) { argparser::parser p; auto enumType1 = p.enum_type("enum1", {{"foo", 1}, {"bar", 2}}); auto enumType2 = p.enum_type("enum2", {{"bar", true}, {"hello-world", false}}); auto unionType = p.union_type("union", enumType1, enumType2); auto fooOption = p.option("--foo", unionType); EXPECT_THROW(auto _1 = p.parse({"--foo", "bar"});, argparser::errors::ambiguous_parse_error); auto enumType3 = p.enum_type("enum3", {{"bar", 1}, {"hello-world", 2}}); auto unionType2 = p.union_type("union", enumType1, enumType3); auto barOption = p.option("--bar", unionType2); EXPECT_THROW(auto _1 = p.parse({"--bar", "bar"});, argparser::errors::ambiguous_parse_error); } TEST(Types, UnionTypeNoAmbiguityErrorIfPossibleValuesIdentical) { argparser::parser p; auto enumType1 = p.enum_type("enum1", {{"foo", 1}, {"bar", 2}}); auto enumType2 = p.enum_type("enum2", {{"foo", 1}, {"foobar", 3}}); auto unionType = p.union_type("union", enumType1, enumType2); auto fooOption = p.option("--foo", unionType); argparser::parse_result parsed; EXPECT_NO_THROW(parsed = p.parse({"--foo", "foo"})); ASSERT_EQ(fooOption->get(parsed), 1); } TEST(Types, ListType) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); auto parsed = p.parse({"--foo", "[1,2,3,4]"}); auto val = fooOption->get(parsed); EXPECT_EQ(val.size(), 4); EXPECT_EQ(val[0], 1); EXPECT_EQ(val[1], 2); EXPECT_EQ(val[2], 3); EXPECT_EQ(val[3], 4); } TEST(Types, ListTypeWithoutBracketsAtRoot) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); auto parsed = p.parse({"--foo", "1,2,3,4"}); auto val = fooOption->get(parsed); EXPECT_EQ(val.size(), 4); EXPECT_EQ(val[0], 1); EXPECT_EQ(val[1], 2); EXPECT_EQ(val[2], 3); EXPECT_EQ(val[3], 4); } TEST(Types, ListTypeTrailingComma) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); auto parsed = p.parse({"--foo", "[1,2,3,]"}); auto val = fooOption->get(parsed); EXPECT_EQ(val.size(), 3); EXPECT_EQ(val[0], 1); EXPECT_EQ(val[1], 2); EXPECT_EQ(val[2], 3); parsed = p.parse({"--foo", "1,2,3,"}); val = fooOption->get(parsed); EXPECT_EQ(val.size(), 3); EXPECT_EQ(val[0], 1); EXPECT_EQ(val[1], 2); EXPECT_EQ(val[2], 3); } TEST(Types, ListTypeOnlyOneTrailingComma) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); EXPECT_THROW(auto _1 = p.parse({"--foo", "[1,2,3,,]"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,3,,"});, argparser::errors::type_parsing_error); } TEST(Types, ListTypeNoPrecedingComma) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); EXPECT_THROW(auto _1 = p.parse({"--foo", "[,1,2,3]"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", ",1,2,3"});, argparser::errors::type_parsing_error); } TEST(Types, ListTypeNoDoubleComma) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); EXPECT_THROW(auto _1 = p.parse({"--foo", "[1,,2,3]"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "[1, ,2,3]"});, argparser::errors::type_parsing_error); } TEST(Types, ListTypeInvalidBrackets) { argparser::parser p; auto uintType = p.basic_type("uint"); auto listType = p.list_type("uint[]", uintType); auto fooOption = p.option("--foo", listType); EXPECT_THROW(auto _1 = p.parse({"--foo", "[1,2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,3]"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1[,2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,[2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2],3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,]3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "[1,2],3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "[1,2,]3"});, argparser::errors::type_parsing_error); } TEST(Types, TupleType) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type("tuple", uintType, uintType, boolType); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "(1,2,true)"}); auto val = fooOption->get(parsed); EXPECT_EQ(std::get<0>(val), 1); EXPECT_EQ(std::get<1>(val), 2); EXPECT_EQ(std::get<2>(val), true); } TEST(Types, TupleTypeWithoutParenthesesAtRoot) { argparser::parser p; auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("tuple", uintType, uintType, uintType); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "1,2,3"}); auto val = fooOption->get(parsed); EXPECT_EQ(std::get<0>(val), 1); EXPECT_EQ(std::get<1>(val), 2); EXPECT_EQ(std::get<2>(val), 3); } TEST(Types, TupleTypeInvalidParentheses) { argparser::parser p; auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("tuple", uintType, uintType, uintType); auto fooOption = p.option("--foo", tupleType); EXPECT_THROW(auto _1 = p.parse({"--foo", "(1,2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,3)"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1(,2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,(2,3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2),3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,)3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "(1,2),3"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "(1,2,)3"});, argparser::errors::type_parsing_error); } TEST(Types, TupleTypeNoInvalidComma) { argparser::parser p; auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("tuple", uintType, uintType, uintType); auto fooOption = p.option("--foo", tupleType); EXPECT_THROW(auto _2 = p.parse({"--foo", "(1,2,3,)"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "(,1,2,3)"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "(1,,2,3)"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "(1, ,2,3)"});, argparser::errors::type_parsing_error); EXPECT_THROW(auto _2 = p.parse({"--foo", "1,2,3,"});, argparser::errors::type_parsing_error); } struct SomeStruct { uint32_t foo; uint32_t bar; bool foobar; }; TEST(Types, TupleTypeWithCustomType) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type("tuple", uintType, uintType, boolType); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "(1,2,true)"}); auto val = fooOption->get(parsed); EXPECT_EQ(val.foo, 1); EXPECT_EQ(val.bar, 2); EXPECT_EQ(val.foobar, true); } TEST(Types, TupleWithCustomTypeAndCustomConstructor) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type( "tuple", std::make_tuple(uintType, uintType, boolType), [](uint32_t a, uint32_t b, bool c) { return SomeStruct{a * 2, b * 3, !c}; }); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "(1,2,true)"}); auto val = fooOption->get(parsed); EXPECT_EQ(val.foo, 2); EXPECT_EQ(val.bar, 6); EXPECT_EQ(val.foobar, false); } TEST(Types, TupleWithSharedPointerAndConstructor) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type>( "tuple", std::make_tuple(uintType, uintType, boolType), [](uint32_t a, uint32_t b, bool c) { return std::make_shared(a, b, c); }); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "(1,2,true)"}); auto val = fooOption->get(parsed); EXPECT_EQ(val->foo, 1); EXPECT_EQ(val->bar, 2); EXPECT_EQ(val->foobar, true); } class SomeVirtualClass { public: virtual int foo() = 0; }; class SomeClass : public SomeVirtualClass { public: SomeClass(int a, int b) : a(a), b(b) {} int a{}; int b{}; int foo() override { return a + b; } }; TEST(Types, TupleWithSharedPointerToVirtualClass) { argparser::parser p; auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type>( "tuple", std::make_tuple(uintType, uintType), [](uint32_t a, uint32_t b) { return std::make_shared(a, b); }); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "(1,2)"}); auto val = fooOption->get(parsed); EXPECT_EQ(val->foo(), 3); } TEST(Types, TuplesCanBeNamePrefixed) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type("tuple", uintType, uintType, boolType); auto fooOption = p.option("--foo", tupleType); auto parsed = p.parse({"--foo", "tuple(1,2,true)"}); auto val = fooOption->get(parsed); EXPECT_EQ(std::get<0>(val), 1); EXPECT_EQ(std::get<1>(val), 2); EXPECT_EQ(std::get<2>(val), true); } TEST(Types, TupleNamePrefixMustBeCorrect) { argparser::parser p; auto uintType = p.basic_type("uint"); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto tupleType = p.tuple_type("tuple", uintType, uintType, boolType); auto fooOption = p.option("--foo", tupleType); EXPECT_THROW(auto _1 = p.parse({"--foo", "foobar(1,2,true)"});, argparser::errors::type_parsing_error); } TEST(Types, ComplexNestedTypes) { argparser::parser p; auto uintType = p.basic_type("uint"); auto tupleType = p.tuple_type("uint-tuple", uintType, uintType, uintType); auto enumType = p.enum_type("enum", {"foo", "bar", "hello", "world"}); auto unionType = p.union_type("union", tupleType, enumType); auto listType = p.list_type("uint-tuple-list", unionType); auto fooOption = p.option("--foo", listType); auto parsed = p.parse({"--foo", "[foo,(1,2,3),hello,world,(2,3,4),(4,5,6)]"}); auto val = fooOption->get(parsed); using tuplet = std::tuple; EXPECT_EQ(val.size(), 6); EXPECT_TRUE(std::holds_alternative(val[0])); EXPECT_EQ(std::get(val[0]), "foo"); EXPECT_TRUE(std::holds_alternative(val[1])); EXPECT_EQ(std::get<0>(std::get(val[1])), 1); EXPECT_EQ(std::get<1>(std::get(val[1])), 2); EXPECT_EQ(std::get<2>(std::get(val[1])), 3); EXPECT_TRUE(std::holds_alternative(val[2])); EXPECT_EQ(std::get(val[2]), "hello"); EXPECT_TRUE(std::holds_alternative(val[3])); EXPECT_EQ(std::get(val[3]), "world"); EXPECT_TRUE(std::holds_alternative(val[4])); EXPECT_EQ(std::get<0>(std::get(val[4])), 2); EXPECT_EQ(std::get<1>(std::get(val[4])), 3); EXPECT_EQ(std::get<2>(std::get(val[4])), 4); EXPECT_TRUE(std::holds_alternative(val[5])); EXPECT_EQ(std::get<0>(std::get(val[5])), 4); EXPECT_EQ(std::get<1>(std::get(val[5])), 5); EXPECT_EQ(std::get<2>(std::get(val[5])), 6); }