1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 20:53:52 +00:00

fix: improve some confusing error situations with config files (#781)

* add some more tests for coverage and fix some confusing error situations with config files.

* style: pre-commit.ci fixes

* fix warning

* ci: fix coverage

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
This commit is contained in:
Philip Top 2022-09-29 08:48:03 -07:00 committed by GitHub
parent dcbcb4721d
commit fd30b5989f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 15 deletions

View File

@ -20,6 +20,8 @@ jobs:
precompile: ["ON", "OFF"] precompile: ["ON", "OFF"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get LCov - name: Get LCov
run: | run: |
@ -50,12 +52,10 @@ jobs:
lcov --list coverage.info lcov --list coverage.info
working-directory: build working-directory: build
- name: Upload coverage - uses: codecov/codecov-action@v3
run: | with:
curl -Os https://uploader.codecov.io/latest/linux/codecov fail_ci_if_error: true
chmod +x codecov working-directory: build
./codecov
working-directory: build
clang-tidy: clang-tidy:
name: Clang-Tidy name: Clang-Tidy

View File

@ -1384,16 +1384,23 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
if(op->empty()) { if(op->empty()) {
if(op->get_expected_min() == 0) { if(op->get_expected_min() == 0) {
// Flag parsing if(item.inputs.size() <= 1) {
auto res = config_formatter_->to_flag(item); // Flag parsing
res = op->get_flag_value(item.name, res); auto res = config_formatter_->to_flag(item);
res = op->get_flag_value(item.name, res);
op->add_result(res); op->add_result(res);
return true;
} else { }
op->add_result(item.inputs); if(static_cast<int>(item.inputs.size()) > op->get_items_expected_max()) {
op->run_callback(); if(op->get_items_expected_max() > 1) {
throw ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), item.inputs.size());
}
throw ConversionError::TooManyInputsFlag(item.fullname());
}
} }
op->add_result(item.inputs);
op->run_callback();
} }
return true; return true;

View File

@ -981,7 +981,9 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") {
std::vector<std::string> strs; std::vector<std::string> strs;
std::vector<std::string> strs2; std::vector<std::string> strs2;
std::vector<std::string> strs3;
auto *opt1 = app.add_option("--str", strs)->required()->expected(0, 2); auto *opt1 = app.add_option("--str", strs)->required()->expected(0, 2);
app.add_option("--str3", strs3)->expected(1, 3);
app.add_option("--str2", strs2); app.add_option("--str2", strs2);
args = {"--str"}; args = {"--str"};
@ -1004,6 +1006,11 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") {
CHECK_NOTHROW(run()); CHECK_NOTHROW(run());
CHECK(strs.empty()); CHECK(strs.empty());
opt1->required(false);
args = {"--str3", "{}"};
CHECK_NOTHROW(run());
CHECK_FALSE(strs3.empty());
} }
TEST_CASE_METHOD(TApp, "RequiredOptsDoubleShort", "[app]") { TEST_CASE_METHOD(TApp, "RequiredOptsDoubleShort", "[app]") {

View File

@ -1017,17 +1017,19 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") {
out << "zero1=[]\n"; out << "zero1=[]\n";
out << "zero2={}\n"; out << "zero2={}\n";
out << "zero3={}\n"; out << "zero3={}\n";
out << "zero4=[\"{}\",\"\"]\n";
out << "nzero={}\n"; out << "nzero={}\n";
out << "one=[\"1\"]\n"; out << "one=[\"1\"]\n";
out << "two=[\"2\",\"3\"]\n"; out << "two=[\"2\",\"3\"]\n";
out << "three=[\"1\",\"2\",\"3\"]\n"; out << "three=[\"1\",\"2\",\"3\"]\n";
} }
std::vector<std::string> nzero, zero1, zero2, zero3, one, two, three; std::vector<std::string> nzero, zero1, zero2, zero3, zero4, one, two, three;
app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}"); app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}");
app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::string>{}); app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::string>{});
// if no default is specified the argument results in an empty string // if no default is specified the argument results in an empty string
app.add_option("--zero3", zero3)->required()->expected(0, 99); app.add_option("--zero3", zero3)->required()->expected(0, 99);
app.add_option("--zero4", zero4)->required()->expected(0, 99);
app.add_option("--nzero", nzero)->required(); app.add_option("--nzero", nzero)->required();
app.add_option("--one", one)->required(); app.add_option("--one", one)->required();
app.add_option("--two", two)->required(); app.add_option("--two", two)->required();
@ -1038,6 +1040,7 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") {
CHECK(zero1 == std::vector<std::string>({})); CHECK(zero1 == std::vector<std::string>({}));
CHECK(zero2 == std::vector<std::string>({})); CHECK(zero2 == std::vector<std::string>({}));
CHECK(zero3 == std::vector<std::string>({""})); CHECK(zero3 == std::vector<std::string>({""}));
CHECK(zero4 == std::vector<std::string>({"{}"}));
CHECK(nzero == std::vector<std::string>({"{}"})); CHECK(nzero == std::vector<std::string>({"{}"}));
CHECK(one == std::vector<std::string>({"1"})); CHECK(one == std::vector<std::string>({"1"}));
CHECK(two == std::vector<std::string>({"2", "3"})); CHECK(two == std::vector<std::string>({"2", "3"}));
@ -1735,6 +1738,23 @@ TEST_CASE_METHOD(TApp, "IniFlagDual", "[config]") {
CHECK_THROWS_AS(run(), CLI::ConversionError); CHECK_THROWS_AS(run(), CLI::ConversionError);
} }
TEST_CASE_METHOD(TApp, "IniVectorMax", "[config]") {
TempFile tmpini{"TestIniTmp.ini"};
std::vector<std::string> v1;
app.config_formatter(std::make_shared<CLI::ConfigINI>());
app.add_option("--vec", v1)->expected(0, 2);
app.set_config("--config", tmpini);
{
std::ofstream out{tmpini};
out << "vec=[a,b,c]" << std::endl;
}
CHECK_THROWS_AS(run(), CLI::ArgumentMismatch);
}
TEST_CASE_METHOD(TApp, "IniShort", "[config]") { TEST_CASE_METHOD(TApp, "IniShort", "[config]") {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};

View File

@ -974,6 +974,16 @@ TEST_CASE("THelp: GroupOrder", "[help]") {
CHECK(aee_loc > zee_loc); CHECK(aee_loc > zee_loc);
} }
TEST_CASE("THelp: GroupNameError", "[help]") {
CLI::App app;
auto *f1 = app.add_flag("--one");
auto *f2 = app.add_flag("--two");
CHECK_THROWS_AS(f1->group("evil group name\non two lines"), CLI::IncorrectConstruction);
CHECK_THROWS_AS(f2->group(std::string(5, '\0')), CLI::IncorrectConstruction);
}
TEST_CASE("THelp: ValidatorsText", "[help]") { TEST_CASE("THelp: ValidatorsText", "[help]") {
CLI::App app; CLI::App app;

View File

@ -492,6 +492,23 @@ TEST_CASE_METHOD(TApp, "FailSet", "[set]") {
CHECK_THROWS_AS(run(), CLI::ValidationError); CHECK_THROWS_AS(run(), CLI::ValidationError);
} }
TEST_CASE_METHOD(TApp, "shortStringCheck", "[set]") {
std::string choice;
app.add_option("-q,--quick", choice)->check([](const std::string &v) {
if(v.size() > 5) {
return std::string{"string too long"};
}
return std::string{};
});
args = {"--quick", "3"};
CHECK_NOTHROW(run());
args = {"--quick=hello_goodbye"};
CHECK_THROWS_AS(run(), CLI::ValidationError);
}
TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") { TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") {
int choice{0}; int choice{0};