1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 20:23:55 +00:00
CLI11/tests/TransformTest.cpp
Philip Top e9934e058d add transformer and checkedTransformer (#239)
* add transform and checkedTransform tests

add Transformer and CheckedTransformer validators

* Eliminate the Validator description string, some code cleanup

add tests

Make Validators a full Object and remove friend,  move to descriptions instead of overriding type name.

update validators to actually merge the type strings and use all validators in the type outputs

rework join so it works without the start variable,  allow some forwarding references in the validator types, some tests for non-copyable maps, and transforms

merge the search function and enable use of member search function,  make the pair adapters forwarding instead of copying

* add a few more tests and documentation

fix some gcc 4.7 issues and add a few more test cases and more parts of the README

Work on ReadMe and add Bound validator to clamp values

* updates to README.md

* Add some more in TOC of README and fix style in Option.hpp
2019-03-02 12:24:26 +01:00

434 lines
12 KiB
C++

#include "app_helper.hpp"
#include <unordered_map>
TEST_F(TApp, SimpleTransform) {
int value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", std::string("1")}}));
args = {"-s", "one"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, 1);
}
TEST_F(TApp, SimpleTransformInitList) {
int value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", "1"}}));
args = {"-s", "one"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, 1);
}
TEST_F(TApp, SimpleNumericalTransform) {
int value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer(CLI::TransformPairs<int>{{"one", 1}}));
args = {"-s", "one"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, 1);
}
TEST_F(TApp, EnumTransform) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
test value;
auto opt = app.add_option("-s", value)
->transform(CLI::Transformer(
CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}}));
args = {"-s", "val1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "val2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "val3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val4"};
EXPECT_THROW(run(), CLI::ConversionError);
// transformer doesn't do any checking so this still works
args = {"-s", "5"};
run();
EXPECT_EQ(static_cast<int16_t>(value), int16_t(5));
}
TEST_F(TApp, EnumCheckedTransform) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
test value;
auto opt = app.add_option("-s", value)
->transform(CLI::CheckedTransformer(
CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}}));
args = {"-s", "val1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "val2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "val3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "17"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val4"};
EXPECT_THROW(run(), CLI::ValidationError);
args = {"-s", "5"};
EXPECT_THROW(run(), CLI::ValidationError);
}
TEST_F(TApp, SimpleTransformFn) {
int value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", "1"}}, CLI::ignore_case));
args = {"-s", "ONE"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, 1);
}
TEST_F(TApp, SimpleNumericalTransformFn) {
int value;
auto opt =
app.add_option("-s", value)
->transform(CLI::Transformer(std::vector<std::pair<std::string, int>>{{"one", 1}}, CLI::ignore_case));
args = {"-s", "ONe"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, 1);
}
TEST_F(TApp, EnumTransformFn) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
test value;
auto opt = app.add_option("-s", value)
->transform(CLI::Transformer(
CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}},
CLI::ignore_case,
CLI::ignore_underscore));
args = {"-s", "val_1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "VAL_2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "VAL3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val_4"};
EXPECT_THROW(run(), CLI::ConversionError);
}
TEST_F(TApp, EnumTransformFnMap) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
std::map<std::string, test> map{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}};
test value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer(map, CLI::ignore_case, CLI::ignore_underscore));
args = {"-s", "val_1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "VAL_2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "VAL3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val_4"};
EXPECT_THROW(run(), CLI::ConversionError);
}
TEST_F(TApp, EnumTransformFnPtrMap) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
std::map<std::string, test> map{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}};
test value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer(&map, CLI::ignore_case, CLI::ignore_underscore));
args = {"-s", "val_1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "VAL_2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "VAL3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val_4"};
EXPECT_THROW(run(), CLI::ConversionError);
map["val4"] = test::val4;
run();
EXPECT_EQ(value, test::val4);
}
TEST_F(TApp, EnumTransformFnSharedPtrMap) {
enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
auto map = std::make_shared<std::unordered_map<std::string, test>>();
auto &mp = *map;
mp["val1"] = test::val1;
mp["val2"] = test::val2;
mp["val3"] = test::val3;
test value;
auto opt = app.add_option("-s", value)->transform(CLI::Transformer(map, CLI::ignore_case, CLI::ignore_underscore));
args = {"-s", "val_1"};
run();
EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, opt->count());
EXPECT_EQ(value, test::val1);
args = {"-s", "VAL_2"};
run();
EXPECT_EQ(value, test::val2);
args = {"-s", "VAL3"};
run();
EXPECT_EQ(value, test::val3);
args = {"-s", "val_4"};
EXPECT_THROW(run(), CLI::ConversionError);
mp["val4"] = test::val4;
run();
EXPECT_EQ(value, test::val4);
}
// Test a cascade of transform functions
TEST_F(TApp, TransformCascade) {
std::string output;
auto opt = app.add_option("-s", output);
opt->transform(CLI::Transformer({{"abc", "abcd"}, {"bbc", "bbcd"}, {"cbc", "cbcd"}}, CLI::ignore_case));
opt->transform(
CLI::Transformer({{"ab", "abc"}, {"bc", "bbc"}, {"cb", "cbc"}}, CLI::ignore_case, CLI::ignore_underscore));
opt->transform(CLI::Transformer({{"a", "ab"}, {"b", "bb"}, {"c", "cb"}}, CLI::ignore_case));
opt->check(CLI::IsMember({"abcd", "bbcd", "cbcd"}));
args = {"-s", "abcd"};
run();
EXPECT_EQ(output, "abcd");
args = {"-s", "Bbc"};
run();
EXPECT_EQ(output, "bbcd");
args = {"-s", "C_B"};
run();
EXPECT_EQ(output, "cbcd");
args = {"-s", "A"};
run();
EXPECT_EQ(output, "abcd");
}
// Test a cascade of transform functions
TEST_F(TApp, TransformCascadeDeactivate) {
std::string output;
auto opt = app.add_option("-s", output);
opt->transform(
CLI::Transformer({{"abc", "abcd"}, {"bbc", "bbcd"}, {"cbc", "cbcd"}}, CLI::ignore_case).name("tform1"));
opt->transform(
CLI::Transformer({{"ab", "abc"}, {"bc", "bbc"}, {"cb", "cbc"}}, CLI::ignore_case, CLI::ignore_underscore)
.name("tform2")
.active(false));
opt->transform(CLI::Transformer({{"a", "ab"}, {"b", "bb"}, {"c", "cb"}}, CLI::ignore_case).name("tform3"));
opt->check(CLI::IsMember({"abcd", "bbcd", "cbcd"}).name("check"));
args = {"-s", "abcd"};
run();
EXPECT_EQ(output, "abcd");
args = {"-s", "Bbc"};
run();
EXPECT_EQ(output, "bbcd");
args = {"-s", "C_B"};
EXPECT_THROW(run(), CLI::ValidationError);
auto validator = opt->get_validator("tform2");
EXPECT_FALSE(validator->get_active());
EXPECT_EQ(validator->get_name(), "tform2");
validator->active();
EXPECT_TRUE(validator->get_active());
args = {"-s", "C_B"};
run();
EXPECT_EQ(output, "cbcd");
opt->get_validator("check")->active(false);
args = {"-s", "gsdgsgs"};
run();
EXPECT_EQ(output, "gsdgsgs");
EXPECT_THROW(opt->get_validator("sdfsdf"), CLI::OptionNotFound);
}
TEST_F(TApp, IntTransformFn) {
std::string value;
app.add_option("-s", value)
->transform(
CLI::CheckedTransformer(std::map<int, int>{{15, 5}, {18, 6}, {21, 7}}, [](int in) { return in - 10; }));
args = {"-s", "25"};
run();
EXPECT_EQ(value, "5");
args = {"-s", "6"};
run();
EXPECT_EQ(value, "6");
args = {"-s", "45"};
EXPECT_THROW(run(), CLI::ValidationError);
args = {"-s", "val_4"};
EXPECT_THROW(run(), CLI::ValidationError);
}
TEST_F(TApp, IntTransformNonConvertible) {
std::string value;
app.add_option("-s", value)->transform(CLI::Transformer(std::map<int, int>{{15, 5}, {18, 6}, {21, 7}}));
args = {"-s", "15"};
run();
EXPECT_EQ(value, "5");
args = {"-s", "18"};
run();
EXPECT_EQ(value, "6");
// value can't be converted to int so it is just ignored
args = {"-s", "abcd"};
run();
EXPECT_EQ(value, "abcd");
}
TEST_F(TApp, IntTransformNonMerge) {
std::string value;
app.add_option("-s", value)
->transform(CLI::Transformer(std::map<int, int>{{15, 5}, {18, 6}, {21, 7}}) &
CLI::Transformer(std::map<int, int>{{25, 5}, {28, 6}, {31, 7}}),
"merge");
args = {"-s", "15"};
run();
EXPECT_EQ(value, "5");
args = {"-s", "18"};
run();
EXPECT_EQ(value, "6");
// value can't be converted to int so it is just ignored
args = {"-s", "abcd"};
run();
EXPECT_EQ(value, "abcd");
args = {"-s", "25"};
run();
EXPECT_EQ(value, "5");
args = {"-s", "31"};
run();
EXPECT_EQ(value, "7");
auto help = app.help();
EXPECT_TRUE(help.find("15->5") != std::string::npos);
EXPECT_TRUE(help.find("25->5") != std::string::npos);
auto validator = app.get_option("-s")->get_validator();
help = validator->get_description();
EXPECT_TRUE(help.find("15->5") != std::string::npos);
EXPECT_TRUE(help.find("25->5") != std::string::npos);
auto validator2 = app.get_option("-s")->get_validator("merge");
EXPECT_EQ(validator2, validator);
}
TEST_F(TApp, IntTransformMergeWithCustomValidator) {
std::string value;
auto opt = app.add_option("-s", value)
->transform(CLI::Transformer(std::map<int, int>{{15, 5}, {18, 6}, {21, 7}}) |
CLI::Validator(
[](std::string &element) {
if(element == "frog") {
element = "hops";
}
return std::string{};
},
std::string{}),
"check");
args = {"-s", "15"};
run();
EXPECT_EQ(value, "5");
args = {"-s", "18"};
run();
EXPECT_EQ(value, "6");
// value can't be converted to int so it is just ignored
args = {"-s", "frog"};
run();
EXPECT_EQ(value, "hops");
args = {"-s", "25"};
run();
EXPECT_EQ(value, "25");
auto help = app.help();
EXPECT_TRUE(help.find("15->5") != std::string::npos);
EXPECT_TRUE(help.find("OR") == std::string::npos);
auto validator = opt->get_validator("check");
EXPECT_EQ(validator->get_name(), "check");
validator->active(false);
help = app.help();
EXPECT_TRUE(help.find("15->5") == std::string::npos);
}
TEST_F(TApp, BoundTests) {
double value;
app.add_option("-s", value)->transform(CLI::Bound(3.4, 5.9));
args = {"-s", "15"};
run();
EXPECT_EQ(value, 5.9);
args = {"-s", "3.689"};
run();
EXPECT_EQ(value, std::stod("3.689"));
// value can't be converted to int so it is just ignored
args = {"-s", "abcd"};
EXPECT_THROW(run(), CLI::ValidationError);
args = {"-s", "2.5"};
run();
EXPECT_EQ(value, 3.4);
auto help = app.help();
EXPECT_TRUE(help.find("bounded to") != std::string::npos);
EXPECT_TRUE(help.find("[3.4 - 5.9]") != std::string::npos);
}