Commit fd30b5989f5fd94e1bd0c5b5cc3946a6d8b8901f
Committed by
GitHub
1 parent
dcbcb472
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>
Showing
6 changed files
with
76 additions
and
15 deletions
.github/workflows/tests.yml
| @@ -20,6 +20,8 @@ jobs: | @@ -20,6 +20,8 @@ jobs: | ||
| 20 | precompile: ["ON", "OFF"] | 20 | precompile: ["ON", "OFF"] |
| 21 | steps: | 21 | steps: |
| 22 | - uses: actions/checkout@v3 | 22 | - uses: actions/checkout@v3 |
| 23 | + with: | ||
| 24 | + fetch-depth: 0 | ||
| 23 | 25 | ||
| 24 | - name: Get LCov | 26 | - name: Get LCov |
| 25 | run: | | 27 | run: | |
| @@ -50,12 +52,10 @@ jobs: | @@ -50,12 +52,10 @@ jobs: | ||
| 50 | lcov --list coverage.info | 52 | lcov --list coverage.info |
| 51 | working-directory: build | 53 | working-directory: build |
| 52 | 54 | ||
| 53 | - - name: Upload coverage | ||
| 54 | - run: | | ||
| 55 | - curl -Os https://uploader.codecov.io/latest/linux/codecov | ||
| 56 | - chmod +x codecov | ||
| 57 | - ./codecov | ||
| 58 | - working-directory: build | 55 | + - uses: codecov/codecov-action@v3 |
| 56 | + with: | ||
| 57 | + fail_ci_if_error: true | ||
| 58 | + working-directory: build | ||
| 59 | 59 | ||
| 60 | clang-tidy: | 60 | clang-tidy: |
| 61 | name: Clang-Tidy | 61 | name: Clang-Tidy |
include/CLI/impl/App_inl.hpp
| @@ -1384,16 +1384,23 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t | @@ -1384,16 +1384,23 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t | ||
| 1384 | if(op->empty()) { | 1384 | if(op->empty()) { |
| 1385 | 1385 | ||
| 1386 | if(op->get_expected_min() == 0) { | 1386 | if(op->get_expected_min() == 0) { |
| 1387 | - // Flag parsing | ||
| 1388 | - auto res = config_formatter_->to_flag(item); | ||
| 1389 | - res = op->get_flag_value(item.name, res); | 1387 | + if(item.inputs.size() <= 1) { |
| 1388 | + // Flag parsing | ||
| 1389 | + auto res = config_formatter_->to_flag(item); | ||
| 1390 | + res = op->get_flag_value(item.name, res); | ||
| 1390 | 1391 | ||
| 1391 | - op->add_result(res); | ||
| 1392 | - | ||
| 1393 | - } else { | ||
| 1394 | - op->add_result(item.inputs); | ||
| 1395 | - op->run_callback(); | 1392 | + op->add_result(res); |
| 1393 | + return true; | ||
| 1394 | + } | ||
| 1395 | + if(static_cast<int>(item.inputs.size()) > op->get_items_expected_max()) { | ||
| 1396 | + if(op->get_items_expected_max() > 1) { | ||
| 1397 | + throw ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), item.inputs.size()); | ||
| 1398 | + } | ||
| 1399 | + throw ConversionError::TooManyInputsFlag(item.fullname()); | ||
| 1400 | + } | ||
| 1396 | } | 1401 | } |
| 1402 | + op->add_result(item.inputs); | ||
| 1403 | + op->run_callback(); | ||
| 1397 | } | 1404 | } |
| 1398 | 1405 | ||
| 1399 | return true; | 1406 | return true; |
tests/AppTest.cpp
| @@ -981,7 +981,9 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { | @@ -981,7 +981,9 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { | ||
| 981 | 981 | ||
| 982 | std::vector<std::string> strs; | 982 | std::vector<std::string> strs; |
| 983 | std::vector<std::string> strs2; | 983 | std::vector<std::string> strs2; |
| 984 | + std::vector<std::string> strs3; | ||
| 984 | auto *opt1 = app.add_option("--str", strs)->required()->expected(0, 2); | 985 | auto *opt1 = app.add_option("--str", strs)->required()->expected(0, 2); |
| 986 | + app.add_option("--str3", strs3)->expected(1, 3); | ||
| 985 | app.add_option("--str2", strs2); | 987 | app.add_option("--str2", strs2); |
| 986 | args = {"--str"}; | 988 | args = {"--str"}; |
| 987 | 989 | ||
| @@ -1004,6 +1006,11 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { | @@ -1004,6 +1006,11 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { | ||
| 1004 | 1006 | ||
| 1005 | CHECK_NOTHROW(run()); | 1007 | CHECK_NOTHROW(run()); |
| 1006 | CHECK(strs.empty()); | 1008 | CHECK(strs.empty()); |
| 1009 | + opt1->required(false); | ||
| 1010 | + args = {"--str3", "{}"}; | ||
| 1011 | + | ||
| 1012 | + CHECK_NOTHROW(run()); | ||
| 1013 | + CHECK_FALSE(strs3.empty()); | ||
| 1007 | } | 1014 | } |
| 1008 | 1015 | ||
| 1009 | TEST_CASE_METHOD(TApp, "RequiredOptsDoubleShort", "[app]") { | 1016 | TEST_CASE_METHOD(TApp, "RequiredOptsDoubleShort", "[app]") { |
tests/ConfigFileTest.cpp
| @@ -1017,17 +1017,19 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { | @@ -1017,17 +1017,19 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { | ||
| 1017 | out << "zero1=[]\n"; | 1017 | out << "zero1=[]\n"; |
| 1018 | out << "zero2={}\n"; | 1018 | out << "zero2={}\n"; |
| 1019 | out << "zero3={}\n"; | 1019 | out << "zero3={}\n"; |
| 1020 | + out << "zero4=[\"{}\",\"\"]\n"; | ||
| 1020 | out << "nzero={}\n"; | 1021 | out << "nzero={}\n"; |
| 1021 | out << "one=[\"1\"]\n"; | 1022 | out << "one=[\"1\"]\n"; |
| 1022 | out << "two=[\"2\",\"3\"]\n"; | 1023 | out << "two=[\"2\",\"3\"]\n"; |
| 1023 | out << "three=[\"1\",\"2\",\"3\"]\n"; | 1024 | out << "three=[\"1\",\"2\",\"3\"]\n"; |
| 1024 | } | 1025 | } |
| 1025 | 1026 | ||
| 1026 | - std::vector<std::string> nzero, zero1, zero2, zero3, one, two, three; | 1027 | + std::vector<std::string> nzero, zero1, zero2, zero3, zero4, one, two, three; |
| 1027 | app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}"); | 1028 | app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}"); |
| 1028 | app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::string>{}); | 1029 | app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::string>{}); |
| 1029 | // if no default is specified the argument results in an empty string | 1030 | // if no default is specified the argument results in an empty string |
| 1030 | app.add_option("--zero3", zero3)->required()->expected(0, 99); | 1031 | app.add_option("--zero3", zero3)->required()->expected(0, 99); |
| 1032 | + app.add_option("--zero4", zero4)->required()->expected(0, 99); | ||
| 1031 | app.add_option("--nzero", nzero)->required(); | 1033 | app.add_option("--nzero", nzero)->required(); |
| 1032 | app.add_option("--one", one)->required(); | 1034 | app.add_option("--one", one)->required(); |
| 1033 | app.add_option("--two", two)->required(); | 1035 | app.add_option("--two", two)->required(); |
| @@ -1038,6 +1040,7 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { | @@ -1038,6 +1040,7 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { | ||
| 1038 | CHECK(zero1 == std::vector<std::string>({})); | 1040 | CHECK(zero1 == std::vector<std::string>({})); |
| 1039 | CHECK(zero2 == std::vector<std::string>({})); | 1041 | CHECK(zero2 == std::vector<std::string>({})); |
| 1040 | CHECK(zero3 == std::vector<std::string>({""})); | 1042 | CHECK(zero3 == std::vector<std::string>({""})); |
| 1043 | + CHECK(zero4 == std::vector<std::string>({"{}"})); | ||
| 1041 | CHECK(nzero == std::vector<std::string>({"{}"})); | 1044 | CHECK(nzero == std::vector<std::string>({"{}"})); |
| 1042 | CHECK(one == std::vector<std::string>({"1"})); | 1045 | CHECK(one == std::vector<std::string>({"1"})); |
| 1043 | CHECK(two == std::vector<std::string>({"2", "3"})); | 1046 | CHECK(two == std::vector<std::string>({"2", "3"})); |
| @@ -1735,6 +1738,23 @@ TEST_CASE_METHOD(TApp, "IniFlagDual", "[config]") { | @@ -1735,6 +1738,23 @@ TEST_CASE_METHOD(TApp, "IniFlagDual", "[config]") { | ||
| 1735 | CHECK_THROWS_AS(run(), CLI::ConversionError); | 1738 | CHECK_THROWS_AS(run(), CLI::ConversionError); |
| 1736 | } | 1739 | } |
| 1737 | 1740 | ||
| 1741 | +TEST_CASE_METHOD(TApp, "IniVectorMax", "[config]") { | ||
| 1742 | + | ||
| 1743 | + TempFile tmpini{"TestIniTmp.ini"}; | ||
| 1744 | + | ||
| 1745 | + std::vector<std::string> v1; | ||
| 1746 | + app.config_formatter(std::make_shared<CLI::ConfigINI>()); | ||
| 1747 | + app.add_option("--vec", v1)->expected(0, 2); | ||
| 1748 | + app.set_config("--config", tmpini); | ||
| 1749 | + | ||
| 1750 | + { | ||
| 1751 | + std::ofstream out{tmpini}; | ||
| 1752 | + out << "vec=[a,b,c]" << std::endl; | ||
| 1753 | + } | ||
| 1754 | + | ||
| 1755 | + CHECK_THROWS_AS(run(), CLI::ArgumentMismatch); | ||
| 1756 | +} | ||
| 1757 | + | ||
| 1738 | TEST_CASE_METHOD(TApp, "IniShort", "[config]") { | 1758 | TEST_CASE_METHOD(TApp, "IniShort", "[config]") { |
| 1739 | 1759 | ||
| 1740 | TempFile tmpini{"TestIniTmp.ini"}; | 1760 | TempFile tmpini{"TestIniTmp.ini"}; |
tests/HelpTest.cpp
| @@ -974,6 +974,16 @@ TEST_CASE("THelp: GroupOrder", "[help]") { | @@ -974,6 +974,16 @@ TEST_CASE("THelp: GroupOrder", "[help]") { | ||
| 974 | CHECK(aee_loc > zee_loc); | 974 | CHECK(aee_loc > zee_loc); |
| 975 | } | 975 | } |
| 976 | 976 | ||
| 977 | +TEST_CASE("THelp: GroupNameError", "[help]") { | ||
| 978 | + CLI::App app; | ||
| 979 | + | ||
| 980 | + auto *f1 = app.add_flag("--one"); | ||
| 981 | + auto *f2 = app.add_flag("--two"); | ||
| 982 | + | ||
| 983 | + CHECK_THROWS_AS(f1->group("evil group name\non two lines"), CLI::IncorrectConstruction); | ||
| 984 | + CHECK_THROWS_AS(f2->group(std::string(5, '\0')), CLI::IncorrectConstruction); | ||
| 985 | +} | ||
| 986 | + | ||
| 977 | TEST_CASE("THelp: ValidatorsText", "[help]") { | 987 | TEST_CASE("THelp: ValidatorsText", "[help]") { |
| 978 | CLI::App app; | 988 | CLI::App app; |
| 979 | 989 |
tests/SetTest.cpp
| @@ -492,6 +492,23 @@ TEST_CASE_METHOD(TApp, "FailSet", "[set]") { | @@ -492,6 +492,23 @@ TEST_CASE_METHOD(TApp, "FailSet", "[set]") { | ||
| 492 | CHECK_THROWS_AS(run(), CLI::ValidationError); | 492 | CHECK_THROWS_AS(run(), CLI::ValidationError); |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | +TEST_CASE_METHOD(TApp, "shortStringCheck", "[set]") { | ||
| 496 | + | ||
| 497 | + std::string choice; | ||
| 498 | + app.add_option("-q,--quick", choice)->check([](const std::string &v) { | ||
| 499 | + if(v.size() > 5) { | ||
| 500 | + return std::string{"string too long"}; | ||
| 501 | + } | ||
| 502 | + return std::string{}; | ||
| 503 | + }); | ||
| 504 | + | ||
| 505 | + args = {"--quick", "3"}; | ||
| 506 | + CHECK_NOTHROW(run()); | ||
| 507 | + | ||
| 508 | + args = {"--quick=hello_goodbye"}; | ||
| 509 | + CHECK_THROWS_AS(run(), CLI::ValidationError); | ||
| 510 | +} | ||
| 511 | + | ||
| 495 | TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") { | 512 | TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") { |
| 496 | 513 | ||
| 497 | int choice{0}; | 514 | int choice{0}; |