mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
This adds a round trip test for config file generation to the fuzzer. (the next step after this PR will be a fuzzer that verifies that the round trip actually matches the results. This change ended up requiring quite a few minor changes to fix the ambiguities between the config file generation and config file reader. 1). There was a number of potential conflicts between positional names and regular option names that could be triggered in config files, this required a number of additional checks on the positional naming to ensure no conflicts. 2). flag options with disable flag override can produce output results that are not valid by themselves, resolving this required flag input to be able to handle an array and output the original value set of results. 3). strings with non-printable characters could cause all sorts of chaos in the config files. This was resolved by generating a binary string conversion format and handling multiline comments and characters, and handling escaped characters. Note; I think a better solution is to move to fully supporting string formatting and escaping along with the binary strings from TOML now that TOML 1.0 is finalized. That will not be this PR though, maybe the next one. 4). Lot of ambiguities and edge cases in the string splitter, this was reworked 5). handling of comments was not done well, especially comment characters in the name of the option which is allowed. 6). non printable characters in the option naming. This would be weird in practice but it also cause some big holes in the config file generation, so the restricted character set for option naming was expanded. (don't allow spaces or control characters). --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
3477 lines
95 KiB
C++
3477 lines
95 KiB
C++
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
|
// under NSF AWARD 1414736 and by the respective contributors.
|
|
// All rights reserved.
|
|
//
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "app_helper.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
|
|
TEST_CASE("StringBased: convert_arg_for_ini", "[config]") {
|
|
|
|
CHECK("\"\"" == CLI::detail::convert_arg_for_ini(std::string{}));
|
|
|
|
CHECK("true" == CLI::detail::convert_arg_for_ini("true"));
|
|
|
|
CHECK("nan" == CLI::detail::convert_arg_for_ini("nan"));
|
|
|
|
CHECK("\"happy hippo\"" == CLI::detail::convert_arg_for_ini("happy hippo"));
|
|
|
|
CHECK("47" == CLI::detail::convert_arg_for_ini("47"));
|
|
|
|
CHECK("47.365225" == CLI::detail::convert_arg_for_ini("47.365225"));
|
|
|
|
CHECK("+3.28e-25" == CLI::detail::convert_arg_for_ini("+3.28e-25"));
|
|
CHECK("-22E14" == CLI::detail::convert_arg_for_ini("-22E14"));
|
|
|
|
CHECK("'a'" == CLI::detail::convert_arg_for_ini("a"));
|
|
// hex
|
|
CHECK("0x5461FAED" == CLI::detail::convert_arg_for_ini("0x5461FAED"));
|
|
// hex fail
|
|
CHECK("\"0x5461FAEG\"" == CLI::detail::convert_arg_for_ini("0x5461FAEG"));
|
|
|
|
// octal
|
|
CHECK("0o546123567" == CLI::detail::convert_arg_for_ini("0o546123567"));
|
|
// octal fail
|
|
CHECK("\"0o546123587\"" == CLI::detail::convert_arg_for_ini("0o546123587"));
|
|
|
|
// binary
|
|
CHECK("0b01101110010" == CLI::detail::convert_arg_for_ini("0b01101110010"));
|
|
// binary fail
|
|
CHECK("\"0b01102110010\"" == CLI::detail::convert_arg_for_ini("0b01102110010"));
|
|
}
|
|
|
|
TEST_CASE("StringBased: IniJoin", "[config]") {
|
|
std::vector<std::string> items = {"one", "two", "three four"};
|
|
std::string result = R"("one" "two" "three four")";
|
|
|
|
CHECK(result == CLI::detail::ini_join(items, ' ', '\0', '\0'));
|
|
|
|
result = R"(["one", "two", "three four"])";
|
|
|
|
CHECK(result == CLI::detail::ini_join(items));
|
|
|
|
result = R"({"one"; "two"; "three four"})";
|
|
|
|
CHECK(result == CLI::detail::ini_join(items, ';', '{', '}'));
|
|
}
|
|
|
|
TEST_CASE("StringBased: First", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one=three\n";
|
|
ofile << "two=four\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 2u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
}
|
|
|
|
TEST_CASE("StringBased: FirstWithComments", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << ";this is a comment\n";
|
|
ofile << "one=three\n";
|
|
ofile << "two=four\n";
|
|
ofile << "; and another one\n";
|
|
ofile << " ; and yet another one\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 2u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
}
|
|
|
|
TEST_CASE("StringBased: Quotes", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << R"(one = "three")" << '\n';
|
|
ofile << R"(two = 'four')" << '\n';
|
|
ofile << R"(five = "six and seven")" << '\n';
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
CHECK(output.at(2).name == "five");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "six and seven");
|
|
}
|
|
|
|
TEST_CASE("StringBased: Vector", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = three\n";
|
|
ofile << "two = four\n";
|
|
ofile << "five = six and seven\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
CHECK(output.at(2).name == "five");
|
|
CHECK(output.at(2).inputs.size() == 3u);
|
|
CHECK(output.at(2).inputs.at(0) == "six");
|
|
CHECK(output.at(2).inputs.at(1) == "and");
|
|
CHECK(output.at(2).inputs.at(2) == "seven");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlVector", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = [four]\n";
|
|
ofile << "five = [six, and, seven]\n";
|
|
ofile << "eight = [nine, \n"
|
|
"ten, eleven, twelve \n"
|
|
"]\n";
|
|
ofile << "one_more = [one, \n"
|
|
"two, three ] \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 5u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
CHECK(output.at(2).name == "five");
|
|
CHECK(output.at(2).inputs.size() == 3u);
|
|
CHECK(output.at(2).inputs.at(0) == "six");
|
|
CHECK(output.at(2).inputs.at(1) == "and");
|
|
CHECK(output.at(2).inputs.at(2) == "seven");
|
|
CHECK(output.at(3).name == "eight");
|
|
CHECK(output.at(3).inputs.size() == 4u);
|
|
CHECK(output.at(3).inputs.at(0) == "nine");
|
|
CHECK(output.at(3).inputs.at(1) == "ten");
|
|
CHECK(output.at(3).inputs.at(2) == "eleven");
|
|
CHECK(output.at(3).inputs.at(3) == "twelve");
|
|
CHECK(output.at(4).name == "one_more");
|
|
CHECK(output.at(4).inputs.size() == 3u);
|
|
CHECK(output.at(4).inputs.at(0) == "one");
|
|
CHECK(output.at(4).inputs.at(1) == "two");
|
|
CHECK(output.at(4).inputs.at(2) == "three");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlMultiLineString1", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = \"\"\"test\n";
|
|
ofile << "five = [six, and, seven]\n";
|
|
ofile << "eight\"\"\"\n";
|
|
ofile << "three=7 \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "test\nfive = [six, and, seven]\neight");
|
|
CHECK(output.at(2).name == "three");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "7");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlMultiLineString2", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = '''test \n";
|
|
ofile << "five = [six, and, seven] \n";
|
|
ofile << "'''\n";
|
|
ofile << "three=7 \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "test \nfive = [six, and, seven] ");
|
|
CHECK(output.at(2).name == "three");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "7");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlMultiLineString3", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = \"\"\"\n";
|
|
ofile << "test \\\n";
|
|
ofile << " five = [six, and, seven] \\\n";
|
|
ofile << "eight\"\"\"\n";
|
|
ofile << "three=7 \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "test five = [six, and, seven] eight");
|
|
CHECK(output.at(2).name == "three");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "7");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlMultiLineString4", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = \"\"\"\n";
|
|
ofile << "test\n";
|
|
ofile << "five = [six, and, seven]\n";
|
|
ofile << "\"\"\"\n";
|
|
ofile << "three=7 \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "test\nfive = [six, and, seven]");
|
|
CHECK(output.at(2).name == "three");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "7");
|
|
}
|
|
|
|
TEST_CASE("StringBased: TomlMultiLineString5", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = [three]\n";
|
|
ofile << "two = \"\"\" mline \\\n";
|
|
ofile << "test\n";
|
|
ofile << '\n';
|
|
ofile << "five = [six, and, seven]\n";
|
|
ofile << "\"\"\"\n";
|
|
ofile << "three=7 \n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 3u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == " mline test\n\nfive = [six, and, seven]");
|
|
CHECK(output.at(2).name == "three");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "7");
|
|
}
|
|
|
|
TEST_CASE("StringBased: Spaces", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one = three\n";
|
|
ofile << "two = four";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 2u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).name == "two");
|
|
CHECK(output.at(1).inputs.size() == 1u);
|
|
CHECK(output.at(1).inputs.at(0) == "four");
|
|
}
|
|
|
|
TEST_CASE("StringBased: Sections", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one=three\n";
|
|
ofile << "[second]\n";
|
|
ofile << " two=four\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 4u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(2).name == "two");
|
|
CHECK(output.at(2).parents.at(0) == "second");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "four");
|
|
CHECK(output.at(2).fullname() == "second.two");
|
|
}
|
|
|
|
TEST_CASE("StringBased: SpacesSections", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "one=three\n\n";
|
|
ofile << "[second] \n";
|
|
ofile << " \n";
|
|
ofile << " two=four\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
CHECK(output.size() == 4u);
|
|
CHECK(output.at(0).name == "one");
|
|
CHECK(output.at(0).inputs.size() == 1u);
|
|
CHECK(output.at(0).inputs.at(0) == "three");
|
|
CHECK(output.at(1).parents.at(0) == "second");
|
|
CHECK(output.at(1).name == "++");
|
|
CHECK(output.at(2).name == "two");
|
|
CHECK(output.at(2).parents.size() == 1u);
|
|
CHECK(output.at(2).parents.at(0) == "second");
|
|
CHECK(output.at(2).inputs.size() == 1u);
|
|
CHECK(output.at(2).inputs.at(0) == "four");
|
|
CHECK(output.at(3).parents.at(0) == "second");
|
|
CHECK(output.at(3).name == "--");
|
|
}
|
|
|
|
// check function to make sure that open sections match close sections
|
|
bool checkSections(const std::vector<CLI::ConfigItem> &output) {
|
|
std::set<std::string> open;
|
|
for(const auto &ci : output) {
|
|
if(ci.name == "++") {
|
|
auto nm = ci.fullname();
|
|
nm.pop_back();
|
|
nm.pop_back();
|
|
auto rv = open.insert(nm);
|
|
if(!rv.second) {
|
|
return false;
|
|
}
|
|
}
|
|
if(ci.name == "--") {
|
|
auto nm = ci.fullname();
|
|
nm.pop_back();
|
|
nm.pop_back();
|
|
auto rv = open.erase(nm);
|
|
if(rv != 1U) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return open.empty();
|
|
}
|
|
TEST_CASE("StringBased: Layers", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other]\n";
|
|
ofile << "[other.sub2]\n";
|
|
ofile << "[other.sub2.sub-level2]\n";
|
|
ofile << "[other.sub2.sub-level2.sub-level3]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 4 openings and 4 closings
|
|
CHECK(output.size() == 10u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: LayersSkip", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2]\n";
|
|
ofile << "[other.sub2.sub-level2.sub-level3]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 4 openings and 4 closings
|
|
CHECK(output.size() == 10u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: LayersSkipOrdered", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2.sub-level2.sub-level3]\n";
|
|
ofile << "[other.sub2]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 4 openings and 4 closings
|
|
CHECK(output.size() == 12u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: LayersChange", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2]\n";
|
|
ofile << "[other.sub3]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 3 openings and 3 closings
|
|
CHECK(output.size() == 8u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: Layers2LevelChange", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2.cmd]\n";
|
|
ofile << "[other.sub3.cmd]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 5 openings and 5 closings
|
|
CHECK(output.size() == 12u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: Layers3LevelChange", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "[other.sub2.subsub.cmd]\n";
|
|
ofile << "[other.sub3.subsub.cmd]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 1 flags and 7 openings and 7 closings
|
|
CHECK(output.size() == 15u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: newSegment", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "[other.sub2.subsub.cmd]\n";
|
|
ofile << "flag = true\n";
|
|
ofile << "[another]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 5 openings and 5 closings
|
|
CHECK(output.size() == 12u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: LayersDirect", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2.sub-level2.sub-level3]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 2 flags and 4 openings and 4 closings
|
|
CHECK(output.size() == 10u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: LayersComplex", "[config]") {
|
|
std::stringstream ofile;
|
|
|
|
ofile << "simple = true\n\n";
|
|
ofile << "[other.sub2.sub-level2.sub-level3]\n";
|
|
ofile << "absolute_newest = true\n";
|
|
ofile << "[other.sub2.sub-level2]\n";
|
|
ofile << "still_newer = true\n";
|
|
ofile << "[other.sub2]\n";
|
|
ofile << "newest = true\n";
|
|
|
|
ofile.seekg(0, std::ios::beg);
|
|
|
|
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
|
|
|
|
// 4 flags and 6 openings and 6 closings
|
|
CHECK(output.size() == 16u);
|
|
CHECK(checkSections(output));
|
|
}
|
|
|
|
TEST_CASE("StringBased: file_error", "[config]") {
|
|
CHECK_THROWS_AS(CLI::ConfigINI().from_file("nonexist_file"), CLI::FileError);
|
|
}
|
|
|
|
static const int fclear1 = fileClear("TestIniTmp.ini");
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotRequired", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
int one = 0, two = 0, three = 0;
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
args = {"--one=1"};
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
|
|
one = two = three = 0;
|
|
args = {"--one=1", "--two=2"};
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
CHECK("TestIniTmp.ini" == app["--config"]->as<std::string>());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSuccessOnUnknownOption", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "three=3" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
run();
|
|
CHECK(two == 99);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniGetRemainingOption", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(true);
|
|
|
|
std::string ExtraOption = "three";
|
|
std::string ExtraOptionValue = "3";
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << ExtraOption << "=" << ExtraOptionValue << std::endl;
|
|
out << "two=99" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_NOTHROW(run());
|
|
std::vector<std::string> ExpectedRemaining = {ExtraOption, "3"};
|
|
CHECK(ExpectedRemaining == app.remaining());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniRemainingSub", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
auto *map = app.add_subcommand("map");
|
|
map->allow_config_extras();
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[map]\n";
|
|
out << "a = 1\n";
|
|
out << "b=[1,2,3]\n";
|
|
out << "c = 3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
std::vector<std::string> rem = map->remaining();
|
|
REQUIRE(rem.size() == 8U);
|
|
CHECK(rem[0] == "map.a");
|
|
CHECK(rem[2] == "map.b");
|
|
CHECK(rem[6] == "map.c");
|
|
CHECK(rem[5] == "3");
|
|
|
|
int a{0};
|
|
int c{0};
|
|
std::vector<int> b;
|
|
map->add_option("-a", a);
|
|
map->add_option("-b", b);
|
|
map->add_option("-c", c);
|
|
|
|
CHECK_NOTHROW(app.parse(app.remaining_for_passthrough()));
|
|
CHECK(a == 1);
|
|
CHECK(c == 3);
|
|
REQUIRE(b.size() == 3U);
|
|
CHECK(b[1] == 2);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniGetNoRemaining", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "two=99" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(app.remaining().empty());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniRequiredNoDefault", "[config]") {
|
|
|
|
app.set_config("--config")->required();
|
|
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_THROWS_AS(run(), CLI::FileError);
|
|
// test to make sure help still gets called correctly
|
|
// GitHub issue #533 https://github.com/CLIUtils/CLI11/issues/553
|
|
args = {"--help"};
|
|
REQUIRE_THROWS_AS(run(), CLI::CallForHelp);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotRequiredNoDefault", "[config]") {
|
|
|
|
app.set_config("--config");
|
|
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_NOTHROW(run());
|
|
}
|
|
|
|
/// Define a class for testing purposes that does bad things
|
|
class EvilConfig : public CLI::Config {
|
|
public:
|
|
EvilConfig() = default;
|
|
std::string to_config(const CLI::App *, bool, bool, std::string) const override { throw CLI::FileError("evil"); }
|
|
|
|
std::vector<CLI::ConfigItem> from_config(std::istream &) const override { throw CLI::FileError("evil"); }
|
|
};
|
|
|
|
TEST_CASE_METHOD(TApp, "IniRequiredbadConfigurator", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
app.set_config("--config", tmpini)->required();
|
|
app.config_formatter(std::make_shared<EvilConfig>());
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_THROWS_AS(run(), CLI::FileError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotRequiredbadConfigurator", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.config_formatter(std::make_shared<EvilConfig>());
|
|
int two{0};
|
|
app.add_option("--two", two);
|
|
REQUIRE_NOTHROW(run());
|
|
}
|
|
|
|
static const int fclear2 = fileClear("TestIniTmp2.ini");
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotRequiredNotDefault", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "two=98" << std::endl;
|
|
out << "three=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
run();
|
|
CHECK(tmpini.c_str() == app["--config"]->as<std::string>());
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
|
|
args = {"--config", tmpini2};
|
|
run();
|
|
|
|
CHECK(two == 98);
|
|
CHECK(three == 4);
|
|
CHECK(tmpini2.c_str() == app.get_config_ptr()->as<std::string>());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniEnvironmentalFileName", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", "")->envname("CONFIG")->required();
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
put_env("CONFIG", tmpini);
|
|
|
|
CHECK_NOTHROW(run());
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
|
|
unset_env("CONFIG");
|
|
|
|
CHECK_THROWS_AS(run(), CLI::FileError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "MultiConfig", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
|
|
app.set_config("--config")->expected(1, 3);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "one=55" << std::endl;
|
|
out << "three=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
args = {"--config", tmpini2, "--config", tmpini};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
CHECK(one == 55);
|
|
|
|
args = {"--config", tmpini, "--config", tmpini2};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 4);
|
|
CHECK(one == 55);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "MultiConfig_takelast", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
|
|
app.set_config("--config")->multi_option_policy(CLI::MultiOptionPolicy::TakeLast)->expected(1, 3);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "one=55" << std::endl;
|
|
out << "three=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
args = {"--config", tmpini, "--config", tmpini2};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
CHECK(one == 55);
|
|
|
|
two = 0;
|
|
args = {"--config", tmpini2, "--config", tmpini};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 4);
|
|
CHECK(one == 55);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "MultiConfig_takeAll", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
|
|
app.set_config("--config")->multi_option_policy(CLI::MultiOptionPolicy::TakeAll);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "one=55" << std::endl;
|
|
out << "three=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
args = {"--config", tmpini, "--config", tmpini2};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
CHECK(one == 55);
|
|
|
|
two = 0;
|
|
args = {"--config", tmpini2, "--config", tmpini};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 4);
|
|
CHECK(one == 55);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "MultiConfig_single", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
|
|
app.set_config("--config")->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "one=55" << std::endl;
|
|
out << "three=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
args = {"--config", tmpini2, "--config", tmpini};
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
CHECK(three == 3);
|
|
CHECK(one == 0);
|
|
|
|
two = 0;
|
|
args = {"--config", tmpini, "--config", tmpini2};
|
|
run();
|
|
|
|
CHECK(two == 0);
|
|
CHECK(three == 4);
|
|
CHECK(one == 55);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniRequiredNotFound", "[config]") {
|
|
|
|
std::string noini = "TestIniNotExist.ini";
|
|
app.set_config("--config", noini, "", true);
|
|
|
|
CHECK_THROWS_AS(run(), CLI::FileError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotRequiredPassedNotFound", "[config]") {
|
|
|
|
std::string noini = "TestIniNotExist.ini";
|
|
app.set_config("--config", "", "", false);
|
|
|
|
args = {"--config", noini};
|
|
CHECK_THROWS_AS(run(), CLI::FileError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOverwrite", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
}
|
|
|
|
std::string orig = "filename_not_exist.ini";
|
|
std::string next = "TestIniTmp.ini";
|
|
app.set_config("--config", orig);
|
|
// Make sure this can be overwritten
|
|
app.set_config("--conf", next);
|
|
int two{7};
|
|
app.add_option("--two", two);
|
|
|
|
run();
|
|
|
|
CHECK(two == 99);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniRequired", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one)->required();
|
|
app.add_option("--two", two)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
args = {"--one=1"};
|
|
|
|
run();
|
|
CHECK(1 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
|
|
one = two = three = 0;
|
|
args = {"--one=1", "--two=2"};
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(1 == one);
|
|
CHECK(2 == two);
|
|
CHECK(3 == three);
|
|
|
|
args = {};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
|
|
args = {"--two=2"};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniInlineComment", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99 ; this is a two" << std::endl;
|
|
out << "three=3; this is a three" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one)->required();
|
|
app.add_option("--two", two)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
args = {"--one=1"};
|
|
|
|
run();
|
|
CHECK(1 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
|
|
one = two = three = 0;
|
|
args = {"--one=1", "--two=2"};
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(1 == one);
|
|
CHECK(2 == two);
|
|
CHECK(3 == three);
|
|
|
|
args = {};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
|
|
args = {"--two=2"};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlInlineComment", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99 # this is a two" << std::endl;
|
|
out << "three=3# this is a three" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one)->required();
|
|
app.add_option("--two", two)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
args = {"--one=1"};
|
|
|
|
run();
|
|
CHECK(1 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
|
|
one = two = three = 0;
|
|
args = {"--one=1", "--two=2"};
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(1 == one);
|
|
CHECK(2 == two);
|
|
CHECK(3 == three);
|
|
|
|
args = {};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
|
|
args = {"--two=2"};
|
|
|
|
CHECK_THROWS_AS(run(), CLI::RequiredError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlDocStringComment", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
out << R"(""")" << std::endl;
|
|
out << "one=35" << std::endl;
|
|
out << R"(""")" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(0 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlDocStringComment2", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "'''" << std::endl;
|
|
out << "one=35" << std::endl;
|
|
out << "last comment line three=6 '''" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(0 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlDocStringComment3", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini, "", true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=99" << std::endl;
|
|
out << "three=3" << std::endl;
|
|
out << "'''" << std::endl;
|
|
out << "one=35" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--one", one);
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
CHECK_NOTHROW(run());
|
|
CHECK(0 == one);
|
|
CHECK(99 == two);
|
|
CHECK(3 == three);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "ConfigModifiers", "[config]") {
|
|
|
|
app.set_config("--config", "test.ini", "", true);
|
|
|
|
auto cfgptr = app.get_config_formatter_base();
|
|
|
|
cfgptr->section("test");
|
|
CHECK(cfgptr->section() == "test");
|
|
|
|
CHECK(cfgptr->sectionRef() == "test");
|
|
auto &sref = cfgptr->sectionRef();
|
|
sref = "this";
|
|
CHECK(cfgptr->section() == "this");
|
|
|
|
cfgptr->index(5);
|
|
CHECK(cfgptr->index() == 5);
|
|
|
|
CHECK(cfgptr->indexRef() == 5);
|
|
auto &iref = cfgptr->indexRef();
|
|
iref = 7;
|
|
CHECK(cfgptr->index() == 7);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniVector", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2 3" << std::endl;
|
|
out << "three=1 2 3" << std::endl;
|
|
}
|
|
|
|
std::vector<int> two, three;
|
|
app.add_option("--two", two)->expected(2)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(two == std::vector<int>({2, 3}));
|
|
CHECK(three == std::vector<int>({1, 2, 3}));
|
|
}
|
|
TEST_CASE_METHOD(TApp, "TOMLVector", "[config]") {
|
|
|
|
TempFile tmptoml{"TestTomlTmp.toml"};
|
|
|
|
app.set_config("--config", tmptoml);
|
|
|
|
{
|
|
std::ofstream out{tmptoml};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "two=[2,3]\n";
|
|
out << "three=[1,2,3]\n";
|
|
}
|
|
|
|
std::vector<int> two, three;
|
|
app.add_option("--two", two)->expected(2)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(two == std::vector<int>({2, 3}));
|
|
CHECK(three == std::vector<int>({1, 2, 3}));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "ColonValueSep", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "two:2\n";
|
|
out << "three:3\n";
|
|
}
|
|
|
|
int two{0}, three{0};
|
|
app.add_option("--two", two);
|
|
app.add_option("--three", three);
|
|
|
|
app.get_config_formatter_base()->valueSeparator(':');
|
|
|
|
run();
|
|
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TOMLVectordirect", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
app.config_formatter(std::make_shared<CLI::ConfigTOML>());
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "two=[2,3]\n";
|
|
out << "three=[1,2,3]\n";
|
|
}
|
|
|
|
std::vector<int> two, three;
|
|
app.add_option("--two", two)->expected(2)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(two == std::vector<int>({2, 3}));
|
|
CHECK(three == std::vector<int>({1, 2, 3}));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TOMLStringVector", "[config]") {
|
|
|
|
TempFile tmptoml{"TestTomlTmp.toml"};
|
|
|
|
app.set_config("--config", tmptoml);
|
|
|
|
{
|
|
std::ofstream out{tmptoml};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "zero1=[]\n";
|
|
out << "zero2={}\n";
|
|
out << "zero3={}\n";
|
|
out << "zero4=[\"{}\",\"\"]\n";
|
|
out << "nzero={}\n";
|
|
out << "one=[\"1\"]\n";
|
|
out << "two=[\"2\",\"3\"]\n";
|
|
out << "three=[\"1\",\"2\",\"3\"]\n";
|
|
}
|
|
|
|
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("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<std::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("--zero4", zero4)->required()->expected(0, 99);
|
|
app.add_option("--nzero", nzero)->required();
|
|
app.add_option("--one", one)->required();
|
|
app.add_option("--two", two)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(zero1 == std::vector<std::string>({}));
|
|
CHECK(zero2 == std::vector<std::string>({}));
|
|
CHECK(zero3 == std::vector<std::string>({""}));
|
|
CHECK(zero4 == std::vector<std::string>({"{}"}));
|
|
CHECK(nzero == std::vector<std::string>({"{}"}));
|
|
CHECK(one == std::vector<std::string>({"1"}));
|
|
CHECK(two == std::vector<std::string>({"2", "3"}));
|
|
CHECK(three == std::vector<std::string>({"1", "2", "3"}));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniVectorCsep", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "zero1=[]\n";
|
|
out << "zero2=[]\n";
|
|
out << "one=[1]\n";
|
|
out << "two=[2,3]\n";
|
|
out << "three=1,2,3\n";
|
|
}
|
|
|
|
std::vector<int> zero1, zero2, one, two, three;
|
|
app.add_option("--zero1", zero1)->required()->expected(0, 99)->default_str("{}");
|
|
app.add_option("--zero2", zero2)->required()->expected(0, 99)->default_val(std::vector<int>{});
|
|
app.add_option("--one", one)->required();
|
|
app.add_option("--two", two)->expected(2)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(zero1 == std::vector<int>({}));
|
|
CHECK(zero2 == std::vector<int>({}));
|
|
CHECK(one == std::vector<int>({1}));
|
|
CHECK(two == std::vector<int>({2, 3}));
|
|
CHECK(three == std::vector<int>({1, 2, 3}));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniVectorMultiple", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "#this is a comment line\n";
|
|
out << "[default]\n";
|
|
out << "two=2\n";
|
|
out << "two=3\n";
|
|
out << "three=1\n";
|
|
out << "three=2\n";
|
|
out << "three=3\n";
|
|
}
|
|
|
|
std::vector<int> two, three;
|
|
app.add_option("--two", two)->expected(2)->required();
|
|
app.add_option("--three", three)->required();
|
|
|
|
run();
|
|
|
|
CHECK(two == std::vector<int>({2, 3}));
|
|
CHECK(three == std::vector<int>({1, 2, 3}));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniLayered", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
subcom->add_option("--val", two);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
|
|
CHECK(0U == subcom->count());
|
|
CHECK(!*subcom);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniLayeredStream", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
subcom->add_option("--val", two);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
std::ifstream in{tmpini};
|
|
app.parse_from_stream(in);
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
|
|
CHECK(0U == subcom->count());
|
|
CHECK(!*subcom);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniLayeredDotSection", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "[subcom.subsubcom]" << std::endl;
|
|
out << "val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
subcom->add_option("--val", two);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
|
|
CHECK(0U == subcom->count());
|
|
CHECK(!*subcom);
|
|
|
|
three = 0;
|
|
// check maxlayers
|
|
app.get_config_formatter_base()->maxLayers(1);
|
|
run();
|
|
CHECK(three == 0);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniLayeredCustomSectionSeparator", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "[subcom|subsubcom]" << std::endl;
|
|
out << "val=3" << std::endl;
|
|
}
|
|
app.get_config_formatter_base()->parentSeparator('|');
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
subcom->add_option("--val", two);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
|
|
CHECK(0U == subcom->count());
|
|
CHECK(!*subcom);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniLayeredOptionGroupAlias", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[ogroup]" << std::endl;
|
|
out << "val2=2" << std::endl;
|
|
}
|
|
int one{0}, two{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_option_group("ogroup")->alias("ogroup");
|
|
subcom->add_option("--val2", two);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSubcommandConfigurable", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
subcom->configurable();
|
|
subcom->add_option("--val", two);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
|
|
CHECK(1U == subcom->count());
|
|
CHECK(*subcom);
|
|
CHECK(app.got_subcommand(subcom));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSubcommandConfigurablePreParse", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0}, four{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
auto *subcom2 = app.add_subcommand("subcom2");
|
|
subcom->configurable();
|
|
std::vector<std::size_t> parse_c;
|
|
subcom->preparse_callback([&parse_c](std::size_t cnt) { parse_c.push_back(cnt); });
|
|
subcom->add_option("--val", two);
|
|
subcom2->add_option("--val", four);
|
|
subcom2->preparse_callback([&parse_c](std::size_t cnt) { parse_c.push_back(cnt + 2623); });
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
CHECK(four == 0);
|
|
|
|
CHECK(1U == parse_c.size());
|
|
CHECK(2U == parse_c[0]);
|
|
|
|
CHECK(0U == subcom2->count());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSection", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.get_config_formatter_base()->section("config");
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[config]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
int val{0};
|
|
app.add_option("--val", val);
|
|
|
|
run();
|
|
|
|
CHECK(2 == val);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSection2", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.get_config_formatter_base()->section("config");
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[config]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int val{0};
|
|
app.add_option("--val", val);
|
|
|
|
run();
|
|
|
|
CHECK(2 == val);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "jsonLikeParsing", "[config]") {
|
|
|
|
TempFile tmpjson{"TestJsonTmp.json"};
|
|
|
|
app.set_config("--config", tmpjson);
|
|
app.get_config_formatter_base()->valueSeparator(':');
|
|
|
|
{
|
|
std::ofstream out{tmpjson};
|
|
out << "{" << std::endl;
|
|
out << "\"val\":1," << std::endl;
|
|
out << R"("val2":"test",)" << std::endl;
|
|
out << "\"flag\":true" << std::endl;
|
|
out << "}" << std::endl;
|
|
}
|
|
|
|
int val{0};
|
|
app.add_option("--val", val);
|
|
std::string val2{0};
|
|
app.add_option("--val2", val2);
|
|
|
|
bool flag{false};
|
|
app.add_flag("--flag", flag);
|
|
|
|
run();
|
|
|
|
CHECK(1 == val);
|
|
CHECK(val2 == "test");
|
|
CHECK(flag);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlSectionNumber", "[config]") {
|
|
|
|
TempFile tmpini{"TestTomlTmp.toml"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.get_config_formatter_base()->section("config")->index(0);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[[config]]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
out << "[[config]]" << std::endl;
|
|
out << "val=4" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
out << "[[config]]" << std::endl;
|
|
out << "val=6" << std::endl;
|
|
out << "subsubcom.val=3" << std::endl;
|
|
}
|
|
|
|
int val{0};
|
|
app.add_option("--val", val);
|
|
|
|
run();
|
|
|
|
CHECK(2 == val);
|
|
|
|
auto &index = app.get_config_formatter_base()->indexRef();
|
|
index = 1;
|
|
run();
|
|
|
|
CHECK(4 == val);
|
|
|
|
index = -1;
|
|
run();
|
|
// Take the first section in this case
|
|
CHECK(2 == val);
|
|
index = 2;
|
|
run();
|
|
|
|
CHECK(6 == val);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSubcommandConfigurableParseComplete", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "[subcom.subsubcom]" << std::endl;
|
|
out << "val=3" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0}, four{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
auto *subcom2 = app.add_subcommand("subcom2");
|
|
subcom->configurable();
|
|
std::vector<std::size_t> parse_c;
|
|
subcom->parse_complete_callback([&parse_c]() { parse_c.push_back(58); });
|
|
subcom->add_option("--val", two);
|
|
subcom2->add_option("--val", four);
|
|
subcom2->parse_complete_callback([&parse_c]() { parse_c.push_back(2623); });
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
// configurable should be inherited
|
|
subsubcom->parse_complete_callback([&parse_c]() { parse_c.push_back(68); });
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
CHECK(four == 0);
|
|
|
|
REQUIRE(2u == parse_c.size());
|
|
CHECK(68U == parse_c[0]);
|
|
CHECK(58U == parse_c[1]);
|
|
CHECK(1u == subsubcom->count());
|
|
CHECK(0u == subcom2->count());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSubcommandMultipleSections", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
out << "[subcom]" << std::endl;
|
|
out << "val=2" << std::endl;
|
|
out << "[subcom.subsubcom]" << std::endl;
|
|
out << "val=3" << std::endl;
|
|
out << "[subcom2]" << std::endl;
|
|
out << "val=4" << std::endl;
|
|
}
|
|
|
|
int one{0}, two{0}, three{0}, four{0};
|
|
app.add_option("--val", one);
|
|
auto *subcom = app.add_subcommand("subcom");
|
|
auto *subcom2 = app.add_subcommand("subcom2");
|
|
subcom->configurable();
|
|
std::vector<std::size_t> parse_c;
|
|
subcom->parse_complete_callback([&parse_c]() { parse_c.push_back(58); });
|
|
subcom->add_option("--val", two);
|
|
subcom2->add_option("--val", four);
|
|
subcom2->parse_complete_callback([&parse_c]() { parse_c.push_back(2623); });
|
|
subcom2->configurable(false);
|
|
auto *subsubcom = subcom->add_subcommand("subsubcom");
|
|
// configurable should be inherited
|
|
subsubcom->parse_complete_callback([&parse_c]() { parse_c.push_back(68); });
|
|
subsubcom->add_option("--val", three);
|
|
|
|
run();
|
|
|
|
CHECK(one == 1);
|
|
CHECK(two == 2);
|
|
CHECK(three == 3);
|
|
CHECK(four == 4);
|
|
|
|
REQUIRE(2u == parse_c.size());
|
|
CHECK(68U == parse_c[0]);
|
|
CHECK(58U == parse_c[1]);
|
|
CHECK(1u == subsubcom->count());
|
|
CHECK(0u == subcom2->count());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "DuplicateSubcommandCallbacks", "[config]") {
|
|
|
|
TempFile tmptoml{"TesttomlTmp.toml"};
|
|
|
|
app.set_config("--config", tmptoml);
|
|
|
|
{
|
|
std::ofstream out{tmptoml};
|
|
out << "[[foo]]" << std::endl;
|
|
out << "[[foo]]" << std::endl;
|
|
out << "[[foo]]" << std::endl;
|
|
}
|
|
|
|
auto *foo = app.add_subcommand("foo");
|
|
int count{0};
|
|
foo->callback([&count]() { ++count; });
|
|
foo->immediate_callback();
|
|
CHECK(foo->get_immediate_callback());
|
|
foo->configurable();
|
|
|
|
run();
|
|
CHECK(3 == count);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "SubcommandCallbackSingle", "[config]") {
|
|
|
|
TempFile tmptoml{"Testtomlcallback.toml"};
|
|
|
|
app.set_config("--config", tmptoml);
|
|
|
|
{
|
|
std::ofstream out{tmptoml};
|
|
out << "[foo]" << std::endl;
|
|
}
|
|
int count{0};
|
|
auto *foo = app.add_subcommand("foo");
|
|
foo->configurable();
|
|
foo->callback([&count]() { ++count; });
|
|
|
|
run();
|
|
CHECK(1 == count);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFailure", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(false);
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
CHECK_THROWS_AS(run(), CLI::ConfigError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniConfigurable", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
bool value{false};
|
|
app.add_flag("--val", value)->configurable(true);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(value);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNotConfigurable", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
bool value{false};
|
|
app.add_flag("--val", value)->configurable(false);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
CHECK_THROWS_AS(run(), CLI::ConfigError);
|
|
app.allow_config_extras(CLI::config_extras_mode::ignore_all);
|
|
CHECK_NOTHROW(run());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagDisableOverrideFlagArray", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
int value{0};
|
|
app.add_flag("--val", value)->configurable(true)->disable_flag_override();
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=[1,true,false,true]" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(value == 2);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagInvalidDisableOverrideFlagArray", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
int value{0};
|
|
app.add_flag("--val", value)->configurable(true)->disable_flag_override();
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "val=[1,true,false,not_valid]" << std::endl;
|
|
}
|
|
|
|
CHECK_THROWS_AS(run(), CLI::InvalidError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniSubFailure", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.add_subcommand("other");
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(false);
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[other]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
CHECK_THROWS_AS(run(), CLI::ConfigError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniNoSubFailure", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
app.allow_config_extras(CLI::config_extras_mode::error);
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[other]" << std::endl;
|
|
out << "val=1" << std::endl;
|
|
}
|
|
|
|
CHECK_THROWS_AS(run(), CLI::ConfigError);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagConvertFailure", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.add_flag("--flag");
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "flag=moobook" << std::endl;
|
|
}
|
|
run();
|
|
bool result{false};
|
|
auto *opt = app.get_option("--flag");
|
|
CHECK_THROWS_AS(opt->results(result), CLI::ConversionError);
|
|
std::string res;
|
|
opt->results(res);
|
|
CHECK("moobook" == res);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagNumbers", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
bool boo{false};
|
|
app.add_flag("--flag", boo);
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "flag=3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(boo);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagDual", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
bool boo{false};
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
app.add_flag("--flag", boo);
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "flag=1 1" << std::endl;
|
|
}
|
|
|
|
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]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("--flag,-f", key);
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "f=3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniDefaultPath", "[config]") {
|
|
|
|
TempFile tmpini{"../TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("--flag,-f", key);
|
|
app.set_config("--config", "TestIniTmp.ini")->transform(CLI::FileOnDefaultPath("../"));
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "f=3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniMultipleDefaultPath", "[config]") {
|
|
|
|
TempFile tmpini{"../TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("--flag,-f", key);
|
|
auto *cfgOption = app.set_config("--config", "doesnotexist.ini")
|
|
->transform(CLI::FileOnDefaultPath("../"))
|
|
->transform(CLI::FileOnDefaultPath("../other", false));
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "f=3" << std::endl;
|
|
}
|
|
|
|
args = {"--config", "TestIniTmp.ini"};
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
CHECK(cfgOption->as<std::string>() == "../TestIniTmp.ini");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniMultipleDefaultPathAlternate", "[config]") {
|
|
|
|
TempFile tmpini{"../TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("--flag,-f", key);
|
|
auto *cfgOption = app.set_config("--config", "doesnotexist.ini")
|
|
->transform(CLI::FileOnDefaultPath("../other") | CLI::FileOnDefaultPath("../"));
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "f=3" << std::endl;
|
|
}
|
|
|
|
args = {"--config", "TestIniTmp.ini"};
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
CHECK(cfgOption->as<std::string>() == "../TestIniTmp.ini");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniPositional", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("key", key);
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "key=3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniEnvironmental", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
int key{0};
|
|
app.add_option("key", key)->envname("CLI11_TEST_ENV_KEY_TMP");
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "CLI11_TEST_ENV_KEY_TMP=3" << std::endl;
|
|
}
|
|
|
|
REQUIRE_NOTHROW(run());
|
|
CHECK(3 == key);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagText", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
bool flag1{false}, flag2{false}, flag3{false}, flag4{false};
|
|
app.add_flag("--flag1", flag1);
|
|
app.add_flag("--flag2", flag2);
|
|
app.add_flag("--flag3", flag3);
|
|
app.add_flag("--flag4", flag4);
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "flag1=true" << std::endl;
|
|
out << "flag2=on" << std::endl;
|
|
out << "flag3=off" << std::endl;
|
|
out << "flag4=1" << std::endl;
|
|
}
|
|
|
|
run();
|
|
|
|
CHECK(flag1);
|
|
CHECK(flag2);
|
|
CHECK(!flag3);
|
|
CHECK(flag4);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlags", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2" << std::endl;
|
|
out << "three=true" << std::endl;
|
|
out << "four=on" << std::endl;
|
|
out << "five" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
bool three{false}, four{false}, five{false};
|
|
app.add_flag("--two", two);
|
|
app.add_flag("--three", three);
|
|
app.add_flag("--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
run();
|
|
|
|
CHECK(two == 2);
|
|
CHECK(three);
|
|
CHECK(four);
|
|
CHECK(five);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagsComment", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2 # comment 1" << std::endl;
|
|
out << "three=true" << std::endl;
|
|
out << "four=on #comment 2" << std::endl;
|
|
out << "five #comment 3" << std::endl;
|
|
out << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
bool three{false}, four{false}, five{false};
|
|
app.add_flag("--two", two);
|
|
app.add_flag("--three", three);
|
|
app.add_flag("--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
run();
|
|
|
|
CHECK(two == 2);
|
|
CHECK(three);
|
|
CHECK(four);
|
|
CHECK(five);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFlagsAltComment", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2 % comment 1" << std::endl;
|
|
out << "three=true" << std::endl;
|
|
out << "four=on %% comment 2" << std::endl;
|
|
out << "five %= 3" << std::endl;
|
|
out << std::endl;
|
|
}
|
|
|
|
auto config = app.get_config_formatter_base();
|
|
config->comment('%');
|
|
int two{0};
|
|
bool three{false}, four{false}, five{false};
|
|
app.add_flag("--two", two);
|
|
app.add_flag("--three", three);
|
|
app.add_flag("--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
run();
|
|
|
|
CHECK(two == 2);
|
|
CHECK(three);
|
|
CHECK(four);
|
|
CHECK(five);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFalseFlags", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=-2" << std::endl;
|
|
out << "three=false" << std::endl;
|
|
out << "four=1" << std::endl;
|
|
out << "five" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
bool three{false}, four{false}, five{false};
|
|
app.add_flag("--two", two);
|
|
app.add_flag("--three", three);
|
|
app.add_flag("--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
run();
|
|
|
|
CHECK(two == -2);
|
|
CHECK(!three);
|
|
CHECK(four);
|
|
CHECK(five);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFalseFlagsDef", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2" << std::endl;
|
|
out << "three=true" << std::endl;
|
|
out << "four=on" << std::endl;
|
|
out << "five" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
bool three{false}, four{false}, five{false};
|
|
app.add_flag("--two{false}", two);
|
|
app.add_flag("--three", three);
|
|
app.add_flag("!--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
run();
|
|
|
|
CHECK(two == -2);
|
|
CHECK(three);
|
|
CHECK(!four);
|
|
CHECK(five);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFalseFlagsDefDisableOverrideError", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2" << std::endl;
|
|
out << "four=on" << std::endl;
|
|
out << "five" << std::endl;
|
|
}
|
|
|
|
int two{0};
|
|
bool four{false}, five{false};
|
|
app.add_flag("--two{false}", two)->disable_flag_override();
|
|
app.add_flag("!--four", four);
|
|
app.add_flag("--five", five);
|
|
|
|
CHECK_THROWS_AS(run(), CLI::ArgumentMismatch);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniFalseFlagsDefDisableOverrideSuccess", "[config]") {
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2" << std::endl;
|
|
out << "four={}" << std::endl;
|
|
out << "val=15" << std::endl;
|
|
}
|
|
|
|
int two{0}, four{0}, val{0};
|
|
app.add_flag("--two{2}", two)->disable_flag_override();
|
|
app.add_flag("--four{4}", four)->disable_flag_override();
|
|
app.add_flag("--val", val);
|
|
|
|
run();
|
|
|
|
CHECK(two == 2);
|
|
CHECK(four == 4);
|
|
CHECK(val == 15);
|
|
}
|
|
|
|
static const int fclear3 = fileClear("TestIniTmp3.ini");
|
|
|
|
TEST_CASE_METHOD(TApp, "IniDisableFlagOverride", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
|
TempFile tmpini3{"TestIniTmp3.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "[default]" << std::endl;
|
|
out << "two=2" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini2};
|
|
out << "[default]" << std::endl;
|
|
out << "two=7" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::ofstream out{tmpini3};
|
|
out << "[default]" << std::endl;
|
|
out << "three=true" << std::endl;
|
|
}
|
|
|
|
int val{0};
|
|
app.add_flag("--one{1},--two{2},--three{3}", val)->disable_flag_override();
|
|
|
|
run();
|
|
CHECK(tmpini.c_str() == app["--config"]->as<std::string>());
|
|
CHECK(val == 2);
|
|
|
|
args = {"--config", tmpini2};
|
|
CHECK_THROWS_AS(run(), CLI::ArgumentMismatch);
|
|
|
|
args = {"--config", tmpini3};
|
|
run();
|
|
|
|
CHECK(val == 3);
|
|
CHECK(tmpini3.c_str() == app.get_config_ptr()->as<std::string>());
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSimple", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("--simple", v);
|
|
|
|
args = {"--simple=3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "simple=3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputShort", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("-s", v);
|
|
|
|
args = {"-s3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "s=3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputPositional", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("pos", v);
|
|
|
|
args = {"3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "pos=3\n");
|
|
}
|
|
|
|
// try the output with environmental only arguments
|
|
TEST_CASE_METHOD(TApp, "TomlOutputEnvironmental", "[config]") {
|
|
|
|
put_env("CLI11_TEST_ENV_TMP", "2");
|
|
|
|
int val{1};
|
|
app.add_option(std::string{}, val)->envname("CLI11_TEST_ENV_TMP");
|
|
|
|
run();
|
|
|
|
CHECK(val == 2);
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "CLI11_TEST_ENV_TMP=2\n");
|
|
|
|
unset_env("CLI11_TEST_ENV_TMP");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputNoConfigurable", "[config]") {
|
|
|
|
int v1{0}, v2{0};
|
|
app.add_option("--simple", v1);
|
|
app.add_option("--noconf", v2)->configurable(false);
|
|
|
|
args = {"--simple=3", "--noconf=2"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "simple=3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputShortSingleDescription", "[config]") {
|
|
std::string flag = "some_flag";
|
|
const std::string description = "Some short description.";
|
|
app.add_flag("--" + flag, description);
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# " + description + "\n" + flag + "=false\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputdefaultOptionString", "[config]") {
|
|
std::string option = "some_option";
|
|
const std::string description = "Some short description.";
|
|
app.add_option("--" + option, description)->run_callback_for_default();
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# " + description + "\n" + option + "=\"\"\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputShortDoubleDescription", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1);
|
|
app.add_flag("--" + flag2, description2);
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
std::string ans = "# " + description1 + "\n" + flag1 + "=false\n\n# " + description2 + "\n" + flag2 + "=false\n";
|
|
CHECK_THAT(str, Contains(ans));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputGroups", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputHiddenOptions", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
double val{12.7};
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
app.add_option("--dval", val)->capture_default_str()->group("");
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
CHECK_THAT(str, Contains("dval=12.7"));
|
|
auto loc = str.find("dval=12.7");
|
|
auto locg1 = str.find("group1");
|
|
CHECK(loc < locg1);
|
|
// make sure it doesn't come twice
|
|
loc = str.find("dval=12.7", loc + 4);
|
|
CHECK(std::string::npos == loc);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputAppMultiLineDescription", "[config]") {
|
|
app.description("Some short app description.\n"
|
|
"That has multiple lines.");
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# Some short app description.\n"));
|
|
CHECK_THAT(str, Contains("# That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputMultiLineDescription", "[config]") {
|
|
std::string flag = "some_flag";
|
|
const std::string description = "Some short description.\nThat has lines.";
|
|
app.add_flag("--" + flag, description);
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# Some short description.\n"));
|
|
CHECK_THAT(str, Contains("# That has lines.\n"));
|
|
CHECK_THAT(str, Contains(flag + "=false\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputOptionGroupMultiLineDescription", "[config]") {
|
|
std::string flag = "flag";
|
|
const std::string description = "Short flag description.\n";
|
|
auto *og = app.add_option_group("group");
|
|
og->description("Option group description.\n"
|
|
"That has multiple lines.");
|
|
og->add_flag("--" + flag, description);
|
|
args = {"--" + flag};
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# Option group description.\n"));
|
|
CHECK_THAT(str, Contains("# That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputMultilineString", "[config]") {
|
|
std::string desc = "flag";
|
|
app.add_option("--opt", desc);
|
|
|
|
std::string argString = "this is a very long string \n that covers multiple lines \n and should be long";
|
|
args = {"--opt", argString};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
|
|
std::istringstream nfile(str);
|
|
|
|
app.clear();
|
|
desc = "";
|
|
app.parse_from_stream(nfile);
|
|
CHECK(desc == argString);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubcommandMultiLineDescription", "[config]") {
|
|
std::string flag = "flag";
|
|
const std::string description = "Short flag description.\n";
|
|
auto *subcom = app.add_subcommand("subcommand");
|
|
subcom->configurable();
|
|
subcom->description("Subcommand description.\n"
|
|
"That has multiple lines.");
|
|
subcom->add_flag("--" + flag, description);
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("# Subcommand description.\n"));
|
|
CHECK_THAT(str, Contains("# That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputOptionGroup", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
double val{12.7};
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
auto *og = app.add_option_group("group3", "g3 desc");
|
|
og->add_option("--dval", val)->capture_default_str()->group("");
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
CHECK_THAT(str, Contains("dval=12.7"));
|
|
CHECK_THAT(str, Contains("group3"));
|
|
CHECK_THAT(str, Contains("g3 desc"));
|
|
auto loc = str.find("dval=12.7");
|
|
auto locg1 = str.find("group1");
|
|
auto locg3 = str.find("group3");
|
|
CHECK(loc > locg1);
|
|
// make sure it doesn't come twice
|
|
loc = str.find("dval=12.7", loc + 4);
|
|
CHECK(std::string::npos == loc);
|
|
CHECK(locg1 < locg3);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputVector", "[config]") {
|
|
|
|
std::vector<int> v;
|
|
app.add_option("--vector", v);
|
|
app.config_formatter(std::make_shared<CLI::ConfigTOML>());
|
|
args = {"--vector", "1", "2", "3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "vector=[1, 2, 3]\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputTuple", "[config]") {
|
|
|
|
std::tuple<double, double, double, double> t;
|
|
app.add_option("--tuple", t);
|
|
app.config_formatter(std::make_shared<CLI::ConfigTOML>());
|
|
args = {"--tuple", "1", "2", "3", "4"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "tuple=[1, 2, 3, 4]\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "ConfigOutputVectorCustom", "[config]") {
|
|
|
|
std::vector<int> v;
|
|
app.add_option("--vector", v);
|
|
auto V = std::make_shared<CLI::ConfigBase>();
|
|
V->arrayBounds('{', '}')->arrayDelimiter(';')->valueSeparator(':');
|
|
app.config_formatter(V);
|
|
args = {"--vector", "1", "2", "3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "vector:{1; 2; 3}\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputFlag", "[config]") {
|
|
|
|
int v{0}, q{0};
|
|
app.add_option("--simple", v);
|
|
app.add_flag("--nothing");
|
|
app.add_flag("--onething");
|
|
app.add_flag("--something", q);
|
|
|
|
args = {"--simple=3", "--onething", "--something", "--something"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=3"));
|
|
CHECK_THAT(str, !Contains("nothing"));
|
|
CHECK_THAT(str, Contains("onething=true"));
|
|
CHECK_THAT(str, Contains("something=2"));
|
|
|
|
str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("nothing"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSet", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
|
|
|
|
args = {"--simple=2"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=2"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputDefault", "[config]") {
|
|
|
|
int v{7};
|
|
app.add_option("--simple", v)->capture_default_str();
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, !Contains("simple=7"));
|
|
|
|
str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("simple=7"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubcom", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
|
|
args = {"--simple", "other", "--newer"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other.newer=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubcomConfigurable", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
args = {"--simple", "other", "--newer"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other]"));
|
|
CHECK_THAT(str, Contains("newer=true"));
|
|
CHECK(std::string::npos == str.find("other.newer=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubsubcom", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other.newer=true"));
|
|
CHECK_THAT(str, Contains("other.sub2.newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubsubcomConfigurable", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other]"));
|
|
CHECK_THAT(str, Contains("newer=true"));
|
|
CHECK_THAT(str, Contains("[other.sub2]"));
|
|
CHECK_THAT(str, Contains("newest=true"));
|
|
CHECK(std::string::npos == str.find("sub2.newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubcomNonConfigurable", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other", "other_descriptor")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
auto *subcom2 = app.add_subcommand("sub2", "descriptor2");
|
|
subcom2->add_flag("--newest")->configurable(false);
|
|
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("other_descriptor"));
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other]"));
|
|
CHECK_THAT(str, Contains("newer=true"));
|
|
CHECK_THAT(str, !Contains("newest"));
|
|
CHECK_THAT(str, !Contains("descriptor2"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputSubsubcomConfigurableDeep", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
auto *sssscom = subsubcom->add_subcommand("sub-level2");
|
|
subsubcom->add_flag("--still_newer");
|
|
auto *s5com = sssscom->add_subcommand("sub-level3");
|
|
s5com->add_flag("--absolute_newest");
|
|
|
|
args = {"--simple", "other", "sub2", "sub-level2", "sub-level3", "--absolute_newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other.sub2.sub-level2.sub-level3]"));
|
|
CHECK_THAT(str, Contains("absolute_newest=true"));
|
|
CHECK(std::string::npos == str.find(".absolute_newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "TomlOutputQuoted", "[config]") {
|
|
|
|
std::string val1;
|
|
app.add_option("--val1", val1);
|
|
|
|
std::string val2;
|
|
app.add_option("--val2", val2);
|
|
|
|
args = {"--val1", "I am a string", "--val2", R"(I am a "confusing" string)"};
|
|
|
|
run();
|
|
|
|
CHECK(val1 == "I am a string");
|
|
CHECK(val2 == "I am a \"confusing\" string");
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("val1=\"I am a string\""));
|
|
CHECK_THAT(str, Contains("val2=\"I am a \\\"confusing\\\" string\""));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "DefaultsTomlOutputQuoted", "[config]") {
|
|
|
|
std::string val1{"I am a string"};
|
|
app.add_option("--val1", val1)->capture_default_str();
|
|
|
|
std::string val2{R"(I am a "confusing" string)"};
|
|
app.add_option("--val2", val2)->capture_default_str();
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("val1=\"I am a string\""));
|
|
CHECK_THAT(str, Contains("val2=\"I am a \\\"confusing\\\" string\""));
|
|
}
|
|
|
|
// #298
|
|
TEST_CASE_METHOD(TApp, "StopReadingConfigOnClear", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.set_config("--config", tmpini);
|
|
auto *ptr = app.set_config(); // Should *not* read config file
|
|
CHECK(nullptr == ptr);
|
|
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << "volume=1" << std::endl;
|
|
}
|
|
|
|
int volume{0};
|
|
app.add_option("--volume", volume, "volume1");
|
|
|
|
run();
|
|
|
|
CHECK(0 == volume);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "ConfigWriteReadWrite", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
|
|
app.add_flag("--flag");
|
|
run();
|
|
|
|
// Save config, with default values too
|
|
std::string config1 = app.config_to_str(true, true);
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << config1 << std::endl;
|
|
}
|
|
|
|
app.set_config("--config", tmpini, "Read an ini file", true);
|
|
run();
|
|
|
|
std::string config2 = app.config_to_str(true, true);
|
|
|
|
CHECK(config2 == config1);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "ConfigWriteReadNegated", "[config]") {
|
|
|
|
TempFile tmpini{"TestIniTmp.ini"};
|
|
bool flag{true};
|
|
app.add_flag("!--no-flag", flag);
|
|
args = {"--no-flag"};
|
|
run();
|
|
|
|
// Save config, with default values too
|
|
std::string config1 = app.config_to_str(false, false);
|
|
{
|
|
std::ofstream out{tmpini};
|
|
out << config1 << std::endl;
|
|
}
|
|
CHECK_FALSE(flag);
|
|
args.clear();
|
|
flag = true;
|
|
app.set_config("--config", tmpini, "Read an ini file", true);
|
|
run();
|
|
|
|
CHECK_FALSE(flag);
|
|
}
|
|
|
|
/////// INI output tests
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSimple", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("--simple", v);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple=3"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "simple=3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputNoConfigurable", "[config]") {
|
|
|
|
int v1{0}, v2{0};
|
|
app.add_option("--simple", v1);
|
|
app.add_option("--noconf", v2)->configurable(false);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple=3", "--noconf=2"};
|
|
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "simple=3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputShortSingleDescription", "[config]") {
|
|
std::string flag = "some_flag";
|
|
const std::string description = "Some short description.";
|
|
app.add_flag("--" + flag, description);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("; " + description + "\n" + flag + "=false\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputShortDoubleDescription", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1);
|
|
app.add_flag("--" + flag2, description2);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
std::string ans = "; " + description1 + "\n" + flag1 + "=false\n\n; " + description2 + "\n" + flag2 + "=false\n";
|
|
CHECK_THAT(str, Contains(ans));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputGroups", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputHiddenOptions", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
double val{12.7};
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
app.add_option("--dval", val)->capture_default_str()->group("");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
CHECK_THAT(str, Contains("dval=12.7"));
|
|
auto loc = str.find("dval=12.7");
|
|
auto locg1 = str.find("group1");
|
|
CHECK(loc < locg1);
|
|
// make sure it doesn't come twice
|
|
loc = str.find("dval=12.7", loc + 4);
|
|
CHECK(std::string::npos == loc);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputAppMultiLineDescription", "[config]") {
|
|
app.description("Some short app description.\n"
|
|
"That has multiple lines.");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("; Some short app description.\n"));
|
|
CHECK_THAT(str, Contains("; That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputMultiLineDescription", "[config]") {
|
|
std::string flag = "some_flag";
|
|
const std::string description = "Some short description.\nThat has lines.";
|
|
app.add_flag("--" + flag, description);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("; Some short description.\n"));
|
|
CHECK_THAT(str, Contains("; That has lines.\n"));
|
|
CHECK_THAT(str, Contains(flag + "=false\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputOptionGroupMultiLineDescription", "[config]") {
|
|
std::string flag = "flag";
|
|
const std::string description = "Short flag description.\n";
|
|
auto *og = app.add_option_group("group");
|
|
og->description("Option group description.\n"
|
|
"That has multiple lines.");
|
|
og->add_flag("--" + flag, description);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("; Option group description.\n"));
|
|
CHECK_THAT(str, Contains("; That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubcommandMultiLineDescription", "[config]") {
|
|
std::string flag = "flag";
|
|
const std::string description = "Short flag description.\n";
|
|
auto *subcom = app.add_subcommand("subcommand");
|
|
subcom->configurable();
|
|
subcom->description("Subcommand description.\n"
|
|
"That has multiple lines.");
|
|
subcom->add_flag("--" + flag, description);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("; Subcommand description.\n"));
|
|
CHECK_THAT(str, Contains("; That has multiple lines.\n"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputOptionGroup", "[config]") {
|
|
std::string flag1 = "flagnr1";
|
|
std::string flag2 = "flagnr2";
|
|
double val{12.7};
|
|
const std::string description1 = "First description.";
|
|
const std::string description2 = "Second description.";
|
|
app.add_flag("--" + flag1, description1)->group("group1");
|
|
app.add_flag("--" + flag2, description2)->group("group2");
|
|
auto *og = app.add_option_group("group3", "g3 desc");
|
|
og->add_option("--dval", val)->capture_default_str()->group("");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true, true);
|
|
CHECK_THAT(str, Contains("group1"));
|
|
CHECK_THAT(str, Contains("group2"));
|
|
CHECK_THAT(str, Contains("dval=12.7"));
|
|
CHECK_THAT(str, Contains("group3"));
|
|
CHECK_THAT(str, Contains("g3 desc"));
|
|
auto loc = str.find("dval=12.7");
|
|
auto locg1 = str.find("group1");
|
|
auto locg3 = str.find("group3");
|
|
CHECK(loc > locg1);
|
|
// make sure it doesn't come twice
|
|
loc = str.find("dval=12.7", loc + 4);
|
|
CHECK(std::string::npos == loc);
|
|
CHECK(locg1 < locg3);
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputVector", "[config]") {
|
|
|
|
std::vector<int> v;
|
|
app.add_option("--vector", v);
|
|
|
|
args = {"--vector", "1", "2", "3"};
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK(str == "vector=1 2 3\n");
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputFlag", "[config]") {
|
|
|
|
int v{0}, q{0};
|
|
app.add_option("--simple", v);
|
|
app.add_flag("--nothing");
|
|
app.add_flag("--onething");
|
|
app.add_flag("--something", q);
|
|
|
|
args = {"--simple=3", "--onething", "--something", "--something"};
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=3"));
|
|
CHECK_THAT(str, !Contains("nothing"));
|
|
CHECK_THAT(str, Contains("onething=true"));
|
|
CHECK_THAT(str, Contains("something=2"));
|
|
|
|
str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("nothing"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSet", "[config]") {
|
|
|
|
int v{0};
|
|
app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
|
|
|
|
args = {"--simple=2"};
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=2"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputDefault", "[config]") {
|
|
|
|
int v{7};
|
|
app.add_option("--simple", v)->capture_default_str();
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, !Contains("simple=7"));
|
|
|
|
str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("simple=7"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubcom", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple", "other", "--newer"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other.newer=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubcomCustomSep", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
app.get_config_formatter_base()->parentSeparator(':');
|
|
args = {"--simple", "other", "--newer"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other:newer=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubcomConfigurable", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple", "other", "--newer"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other]"));
|
|
CHECK_THAT(str, Contains("newer=true"));
|
|
CHECK(std::string::npos == str.find("other.newer=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubsubcom", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other.newer=true"));
|
|
CHECK_THAT(str, Contains("other.sub2.newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubsubcomCustomSep", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other");
|
|
subcom->add_flag("--newer");
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
app.get_config_formatter_base()->parentSeparator('|');
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("other|newer=true"));
|
|
CHECK_THAT(str, Contains("other|sub2|newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubsubcomConfigurable", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple", "other", "--newer", "sub2", "--newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other]"));
|
|
CHECK_THAT(str, Contains("newer=true"));
|
|
CHECK_THAT(str, Contains("[other.sub2]"));
|
|
CHECK_THAT(str, Contains("newest=true"));
|
|
CHECK(std::string::npos == str.find("sub2.newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputSubsubcomConfigurableDeep", "[config]") {
|
|
|
|
app.add_flag("--simple");
|
|
auto *subcom = app.add_subcommand("other")->configurable();
|
|
subcom->add_flag("--newer");
|
|
|
|
auto *subsubcom = subcom->add_subcommand("sub2");
|
|
subsubcom->add_flag("--newest");
|
|
auto *sssscom = subsubcom->add_subcommand("sub-level2");
|
|
subsubcom->add_flag("--still_newer");
|
|
auto *s5com = sssscom->add_subcommand("sub-level3");
|
|
s5com->add_flag("--absolute_newest");
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--simple", "other", "sub2", "sub-level2", "sub-level3", "--absolute_newest"};
|
|
run();
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("simple=true"));
|
|
CHECK_THAT(str, Contains("[other.sub2.sub-level2.sub-level3]"));
|
|
CHECK_THAT(str, Contains("absolute_newest=true"));
|
|
CHECK(std::string::npos == str.find(".absolute_newest=true"));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "IniOutputQuoted", "[config]") {
|
|
|
|
std::string val1;
|
|
app.add_option("--val1", val1);
|
|
|
|
std::string val2;
|
|
app.add_option("--val2", val2);
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
args = {"--val1", "I am a string", "--val2", R"(I am a "confusing" string)"};
|
|
|
|
run();
|
|
|
|
CHECK(val1 == "I am a string");
|
|
CHECK(val2 == "I am a \"confusing\" string");
|
|
|
|
std::string str = app.config_to_str();
|
|
CHECK_THAT(str, Contains("val1=\"I am a string\""));
|
|
CHECK_THAT(str, Contains("val2=\"I am a \\\"confusing\\\" string\""));
|
|
}
|
|
|
|
TEST_CASE_METHOD(TApp, "DefaultsIniOutputQuoted", "[config]") {
|
|
|
|
std::string val1{"I am a string"};
|
|
app.add_option("--val1", val1)->capture_default_str();
|
|
|
|
std::string val2{R"(I am a "confusing" string)"};
|
|
app.add_option("--val2", val2)->capture_default_str();
|
|
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
|
run();
|
|
|
|
std::string str = app.config_to_str(true);
|
|
CHECK_THAT(str, Contains("val1=\"I am a string\""));
|
|
CHECK_THAT(str, Contains("val2=\"I am a \\\"confusing\\\" string\""));
|
|
}
|