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 | 20 | precompile: ["ON", "OFF"] |
| 21 | 21 | steps: |
| 22 | 22 | - uses: actions/checkout@v3 |
| 23 | + with: | |
| 24 | + fetch-depth: 0 | |
| 23 | 25 | |
| 24 | 26 | - name: Get LCov |
| 25 | 27 | run: | |
| ... | ... | @@ -50,12 +52,10 @@ jobs: |
| 50 | 52 | lcov --list coverage.info |
| 51 | 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 | 60 | clang-tidy: |
| 61 | 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 | 1384 | if(op->empty()) { |
| 1385 | 1385 | |
| 1386 | 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 | 1406 | return true; | ... | ... |
tests/AppTest.cpp
| ... | ... | @@ -981,7 +981,9 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { |
| 981 | 981 | |
| 982 | 982 | std::vector<std::string> strs; |
| 983 | 983 | std::vector<std::string> strs2; |
| 984 | + std::vector<std::string> strs3; | |
| 984 | 985 | auto *opt1 = app.add_option("--str", strs)->required()->expected(0, 2); |
| 986 | + app.add_option("--str3", strs3)->expected(1, 3); | |
| 985 | 987 | app.add_option("--str2", strs2); |
| 986 | 988 | args = {"--str"}; |
| 987 | 989 | |
| ... | ... | @@ -1004,6 +1006,11 @@ TEST_CASE_METHOD(TApp, "emptyVectorReturn", "[app]") { |
| 1004 | 1006 | |
| 1005 | 1007 | CHECK_NOTHROW(run()); |
| 1006 | 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 | 1016 | TEST_CASE_METHOD(TApp, "RequiredOptsDoubleShort", "[app]") { | ... | ... |
tests/ConfigFileTest.cpp
| ... | ... | @@ -1017,17 +1017,19 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { |
| 1017 | 1017 | out << "zero1=[]\n"; |
| 1018 | 1018 | out << "zero2={}\n"; |
| 1019 | 1019 | out << "zero3={}\n"; |
| 1020 | + out << "zero4=[\"{}\",\"\"]\n"; | |
| 1020 | 1021 | out << "nzero={}\n"; |
| 1021 | 1022 | out << "one=[\"1\"]\n"; |
| 1022 | 1023 | out << "two=[\"2\",\"3\"]\n"; |
| 1023 | 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 | 1028 | app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}"); |
| 1028 | 1029 | app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::string>{}); |
| 1029 | 1030 | // if no default is specified the argument results in an empty string |
| 1030 | 1031 | app.add_option("--zero3", zero3)->required()->expected(0, 99); |
| 1032 | + app.add_option("--zero4", zero4)->required()->expected(0, 99); | |
| 1031 | 1033 | app.add_option("--nzero", nzero)->required(); |
| 1032 | 1034 | app.add_option("--one", one)->required(); |
| 1033 | 1035 | app.add_option("--two", two)->required(); |
| ... | ... | @@ -1038,6 +1040,7 @@ TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") { |
| 1038 | 1040 | CHECK(zero1 == std::vector<std::string>({})); |
| 1039 | 1041 | CHECK(zero2 == std::vector<std::string>({})); |
| 1040 | 1042 | CHECK(zero3 == std::vector<std::string>({""})); |
| 1043 | + CHECK(zero4 == std::vector<std::string>({"{}"})); | |
| 1041 | 1044 | CHECK(nzero == std::vector<std::string>({"{}"})); |
| 1042 | 1045 | CHECK(one == std::vector<std::string>({"1"})); |
| 1043 | 1046 | CHECK(two == std::vector<std::string>({"2", "3"})); |
| ... | ... | @@ -1735,6 +1738,23 @@ TEST_CASE_METHOD(TApp, "IniFlagDual", "[config]") { |
| 1735 | 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 | 1758 | TEST_CASE_METHOD(TApp, "IniShort", "[config]") { |
| 1739 | 1759 | |
| 1740 | 1760 | TempFile tmpini{"TestIniTmp.ini"}; | ... | ... |
tests/HelpTest.cpp
| ... | ... | @@ -974,6 +974,16 @@ TEST_CASE("THelp: GroupOrder", "[help]") { |
| 974 | 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 | 987 | TEST_CASE("THelp: ValidatorsText", "[help]") { |
| 978 | 988 | CLI::App app; |
| 979 | 989 | ... | ... |
tests/SetTest.cpp
| ... | ... | @@ -492,6 +492,23 @@ TEST_CASE_METHOD(TApp, "FailSet", "[set]") { |
| 492 | 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 | 512 | TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") { |
| 496 | 513 | |
| 497 | 514 | int choice{0}; | ... | ... |