1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 12:13:52 +00:00
CLI11/tests/ConfigFileTest.cpp
Philip Top c67ab9dd43 Config file handling refactor. (#362)
Refactor some of the configuration file handling code.  Make it easier to get the actual file that was processed, and allow extras in the config file to be ignored (default now), captured or errored.

fix std::error reference and formatting

add test for required but no default and fix a shadow warning on 'required' from gcc 4.8

Test correctness of config write-read loop

fix config generation for flag definitions

make the config output conform with toml

continue work on the config file interpretation and construction

get all the ini tests working again with the cleaned up features.

update formatting

rename IniTest to ConfigFileTest to better reflect actual tests and add a few more test of the configTOML
disambiguate enable/disable by default to an enumeration, and to make room for a configurable option to allow subcommands to be triggered by a config file.
add a ConfigBase class to generally reflect a broader class of configuration files formats of similar nature to INI files

add configurable to app and allow it to trigger subcommands

add test of ini formatting

add section support to the config files so sections can be opened and closed and the callbacks triggered as appropriate.

add handling of option groups to the config file output

add subcommand and option group configuration to config file output

subsubcom test on config files

fix a few sign comparison warnings and formatting

start working on the book edits for configuration and a few more tests

more test to check for subcommand close in config files

more tests for coverage

generalize section opening and closing

add more tests and some fixes for different configurations

yet more tests of different situations related to configuration files

test more paths for configuration file sections

remove some unused code and fix some codacy warnings

update readme with updates from configuration files

more book edits and README formatting

remove extra space

Apply suggestions from code review

Co-Authored-By: Henry Schreiner <HenrySchreinerIII@gmail.com>

fix some comments and documentation

fix spacing

Rename size_t -> std::size_t

Fix compiler warnings with -Wsign-conversion

Fix new warnings with -Wsign-conversion in PR
2019-12-31 11:28:25 -05:00

1758 lines
45 KiB
C++

#include "app_helper.hpp"
#include "gmock/gmock.h"
#include <cstdio>
#include <sstream>
using ::testing::HasSubstr;
using ::testing::Not;
TEST(StringBased, convert_arg_for_ini) {
EXPECT_EQ(CLI::detail::convert_arg_for_ini(std::string{}), "\"\"");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("true"), "true");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("nan"), "nan");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("happy hippo"), "\"happy hippo\"");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("47"), "47");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("47.365225"), "47.365225");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("+3.28e-25"), "+3.28e-25");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("-22E14"), "-22E14");
EXPECT_EQ(CLI::detail::convert_arg_for_ini("a"), "'a'");
// hex
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0x5461FAED"), "0x5461FAED");
// hex fail
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0x5461FAEG"), "\"0x5461FAEG\"");
// octal
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0o546123567"), "0o546123567");
// octal fail
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0o546123587"), "\"0o546123587\"");
// binary
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0b01101110010"), "0b01101110010");
// binary fail
EXPECT_EQ(CLI::detail::convert_arg_for_ini("0b01102110010"), "\"0b01102110010\"");
}
TEST(StringBased, IniJoin) {
std::vector<std::string> items = {"one", "two", "three four"};
std::string result = "\"one\" \"two\" \"three four\"";
EXPECT_EQ(CLI::detail::ini_join(items, ' ', '\0', '\0'), result);
result = "[\"one\", \"two\", \"three four\"]";
EXPECT_EQ(CLI::detail::ini_join(items), result);
result = "{\"one\"; \"two\"; \"three four\"}";
EXPECT_EQ(CLI::detail::ini_join(items, ';', '{', '}'), result);
}
TEST(StringBased, First) {
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);
EXPECT_EQ(2u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name);
EXPECT_EQ(1u, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0));
}
TEST(StringBased, FirstWithComments) {
std::stringstream ofile;
ofile << ";this is a comment\n";
ofile << "one=three\n";
ofile << "two=four\n";
ofile << "; and another one\n";
ofile.seekg(0, std::ios::beg);
std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
EXPECT_EQ(2u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name);
EXPECT_EQ(1u, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0));
}
TEST(StringBased, Quotes) {
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);
EXPECT_EQ(3u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name);
EXPECT_EQ(1u, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0));
EXPECT_EQ("five", output.at(2).name);
EXPECT_EQ(1u, output.at(2).inputs.size());
EXPECT_EQ("six and seven", output.at(2).inputs.at(0));
}
TEST(StringBased, Vector) {
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);
EXPECT_EQ(3u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name);
EXPECT_EQ(1u, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0));
EXPECT_EQ("five", output.at(2).name);
EXPECT_EQ(3u, output.at(2).inputs.size());
EXPECT_EQ("six", output.at(2).inputs.at(0));
EXPECT_EQ("and", output.at(2).inputs.at(1));
EXPECT_EQ("seven", output.at(2).inputs.at(2));
}
TEST(StringBased, Spaces) {
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);
EXPECT_EQ(2u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name);
EXPECT_EQ(1u, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0));
}
TEST(StringBased, Sections) {
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);
EXPECT_EQ(4u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(2).name);
EXPECT_EQ("second", output.at(2).parents.at(0));
EXPECT_EQ(1u, output.at(2).inputs.size());
EXPECT_EQ("four", output.at(2).inputs.at(0));
EXPECT_EQ("second.two", output.at(2).fullname());
}
TEST(StringBased, SpacesSections) {
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);
EXPECT_EQ(4u, output.size());
EXPECT_EQ("one", output.at(0).name);
EXPECT_EQ(1u, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("second", output.at(1).parents.at(0));
EXPECT_EQ("++", output.at(1).name);
EXPECT_EQ("two", output.at(2).name);
EXPECT_EQ(1u, output.at(2).parents.size());
EXPECT_EQ("second", output.at(2).parents.at(0));
EXPECT_EQ(1u, output.at(2).inputs.size());
EXPECT_EQ("four", output.at(2).inputs.at(0));
EXPECT_EQ("second", output.at(3).parents.at(0));
EXPECT_EQ("--", 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(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(StringBased, Layers) {
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
EXPECT_EQ(10u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, LayersSkip) {
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
EXPECT_EQ(10u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, LayersSkipOrdered) {
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
EXPECT_EQ(12u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, LayersChange) {
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
EXPECT_EQ(8u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, Layers2LevelChange) {
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
EXPECT_EQ(12u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, Layers3LevelChange) {
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
EXPECT_EQ(15u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, newSegment) {
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
EXPECT_EQ(12u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, LayersDirect) {
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
EXPECT_EQ(10u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, LayersComplex) {
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
EXPECT_EQ(16u, output.size());
EXPECT_TRUE(checkSections(output));
}
TEST(StringBased, file_error) {
EXPECT_THROW(std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_file("nonexist_file"), CLI::FileError);
}
TEST_F(TApp, IniNotRequired) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(99, two);
EXPECT_EQ(3, three);
one = two = three = 0;
args = {"--one=1", "--two=2"};
run();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(app["--config"]->as<std::string>(), "TestIniTmp.ini");
}
TEST_F(TApp, IniSuccessOnUnknownOption) {
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();
EXPECT_EQ(99, two);
}
TEST_F(TApp, IniGetRemainingOption) {
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);
ASSERT_NO_THROW(run());
std::vector<std::string> ExpectedRemaining = {ExtraOption};
EXPECT_EQ(app.remaining(), ExpectedRemaining);
}
TEST_F(TApp, IniGetNoRemaining) {
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);
ASSERT_NO_THROW(run());
EXPECT_EQ(app.remaining().size(), 0u);
}
TEST_F(TApp, IniRequiredNoDefault) {
app.set_config("--config")->required();
int two = 0;
app.add_option("--two", two);
ASSERT_THROW(run(), CLI::FileError);
}
TEST_F(TApp, IniNotRequiredNoDefault) {
app.set_config("--config");
int two = 0;
app.add_option("--two", two);
ASSERT_NO_THROW(run());
}
/// Define a class for testing purposes that does bad things
class EvilConfig : public CLI::Config {
public:
EvilConfig() = default;
virtual std::string to_config(const CLI::App *, bool, bool, std::string) const { throw CLI::FileError("evil"); }
virtual std::vector<CLI::ConfigItem> from_config(std::istream &) const { throw CLI::FileError("evil"); }
};
TEST_F(TApp, IniRequiredbadConfigurator) {
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);
ASSERT_THROW(run(), CLI::FileError);
}
TEST_F(TApp, IniNotRequiredbadConfigurator) {
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);
ASSERT_NO_THROW(run());
}
TEST_F(TApp, IniNotRequiredNotDefault) {
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();
EXPECT_EQ(app["--config"]->as<std::string>(), tmpini.c_str());
EXPECT_EQ(99, two);
EXPECT_EQ(3, three);
args = {"--config", tmpini2};
run();
EXPECT_EQ(98, two);
EXPECT_EQ(4, three);
EXPECT_EQ(app.get_config_ptr()->as<std::string>(), tmpini2.c_str());
}
TEST_F(TApp, IniRequiredNotFound) {
std::string noini = "TestIniNotExist.ini";
app.set_config("--config", noini, "", true);
EXPECT_THROW(run(), CLI::FileError);
}
TEST_F(TApp, IniNotRequiredPassedNotFound) {
std::string noini = "TestIniNotExist.ini";
app.set_config("--config", "", "", false);
args = {"--config", noini};
EXPECT_THROW(run(), CLI::FileError);
}
TEST_F(TApp, IniOverwrite) {
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();
EXPECT_EQ(99, two);
}
TEST_F(TApp, IniRequired) {
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();
EXPECT_EQ(one, 1);
EXPECT_EQ(two, 99);
EXPECT_EQ(three, 3);
one = two = three = 0;
args = {"--one=1", "--two=2"};
EXPECT_NO_THROW(run());
EXPECT_EQ(one, 1);
EXPECT_EQ(two, 2);
EXPECT_EQ(three, 3);
args = {};
EXPECT_THROW(run(), CLI::RequiredError);
args = {"--two=2"};
EXPECT_THROW(run(), CLI::RequiredError);
}
TEST_F(TApp, IniVector) {
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();
EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
}
TEST_F(TApp, TOMLVector) {
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,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();
EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
}
TEST_F(TApp, ColonValueSep) {
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, three;
app.add_option("--two", two);
app.add_option("--three", three);
app.get_config_formatter_base()->valueSeparator(':');
run();
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
}
TEST_F(TApp, TOMLVectordirect) {
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();
EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
}
TEST_F(TApp, IniVectorCsep) {
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,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();
EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
}
TEST_F(TApp, IniVectorMultiple) {
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();
EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
}
TEST_F(TApp, IniLayered) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(subcom->count(), 0U);
EXPECT_FALSE(*subcom);
}
TEST_F(TApp, IniLayeredDotSection) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(subcom->count(), 0U);
EXPECT_FALSE(*subcom);
}
TEST_F(TApp, IniSubcommandConfigurable) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(subcom->count(), 1U);
EXPECT_TRUE(*subcom);
EXPECT_TRUE(app.got_subcommand(subcom));
}
TEST_F(TApp, IniSubcommandConfigurablePreParse) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(0, four);
EXPECT_EQ(parse_c.size(), 1U);
EXPECT_EQ(parse_c[0], 2U);
EXPECT_EQ(subcom2->count(), 0U);
}
TEST_F(TApp, IniSubcommandConfigurableParseComplete) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(0, four);
ASSERT_EQ(parse_c.size(), 2u);
EXPECT_EQ(parse_c[0], 68U);
EXPECT_EQ(parse_c[1], 58U);
EXPECT_EQ(subsubcom->count(), 1u);
EXPECT_EQ(subcom2->count(), 0u);
}
TEST_F(TApp, IniSubcommandMultipleSections) {
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();
EXPECT_EQ(1, one);
EXPECT_EQ(2, two);
EXPECT_EQ(3, three);
EXPECT_EQ(4, four);
ASSERT_EQ(parse_c.size(), 2u);
EXPECT_EQ(parse_c[0], 68U);
EXPECT_EQ(parse_c[1], 58U);
EXPECT_EQ(subsubcom->count(), 1u);
EXPECT_EQ(subcom2->count(), 0u); // not configurable but value is updated
}
TEST_F(TApp, DuplicateSubcommandCallbacks) {
TempFile tmpini{"TestIniTmp.ini"};
app.set_config("--config", tmpini);
{
std::ofstream out{tmpini};
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();
EXPECT_TRUE(foo->get_immediate_callback());
foo->configurable();
run();
EXPECT_EQ(count, 3);
}
TEST_F(TApp, IniFailure) {
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;
}
EXPECT_THROW(run(), CLI::ConfigError);
}
TEST_F(TApp, IniConfigurable) {
TempFile tmpini{"TestIniTmp.ini"};
app.set_config("--config", tmpini);
bool value;
app.add_flag("--val", value)->configurable(true);
{
std::ofstream out{tmpini};
out << "[default]" << std::endl;
out << "val=1" << std::endl;
}
ASSERT_NO_THROW(run());
EXPECT_TRUE(value);
}
TEST_F(TApp, IniNotConfigurable) {
TempFile tmpini{"TestIniTmp.ini"};
app.set_config("--config", tmpini);
bool value;
app.add_flag("--val", value)->configurable(false);
{
std::ofstream out{tmpini};
out << "[default]" << std::endl;
out << "val=1" << std::endl;
}
EXPECT_THROW(run(), CLI::ConfigError);
}
TEST_F(TApp, IniSubFailure) {
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;
}
EXPECT_THROW(run(), CLI::ConfigError);
}
TEST_F(TApp, IniNoSubFailure) {
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;
}
EXPECT_THROW(run(), CLI::ConfigError);
}
TEST_F(TApp, IniFlagConvertFailure) {
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;
auto *opt = app.get_option("--flag");
EXPECT_THROW(opt->results(result), CLI::ConversionError);
std::string res;
opt->results(res);
EXPECT_EQ(res, "moobook");
}
TEST_F(TApp, IniFlagNumbers) {
TempFile tmpini{"TestIniTmp.ini"};
bool boo;
app.add_flag("--flag", boo);
app.set_config("--config", tmpini);
{
std::ofstream out{tmpini};
out << "flag=3" << std::endl;
}
ASSERT_NO_THROW(run());
EXPECT_TRUE(boo);
}
TEST_F(TApp, IniFlagDual) {
TempFile tmpini{"TestIniTmp.ini"};
bool boo;
app.add_flag("--flag", boo);
app.set_config("--config", tmpini);
{
std::ofstream out{tmpini};
out << "flag=1 1" << std::endl;
}
EXPECT_THROW(run(), CLI::ConversionError);
}
TEST_F(TApp, IniFlagText) {
TempFile tmpini{"TestIniTmp.ini"};
bool flag1, flag2, flag3, flag4;
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();
EXPECT_TRUE(flag1);
EXPECT_TRUE(flag2);
EXPECT_FALSE(flag3);
EXPECT_TRUE(flag4);
}
TEST_F(TApp, IniFlags) {
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;
bool three, four, five;
app.add_flag("--two", two);
app.add_flag("--three", three);
app.add_flag("--four", four);
app.add_flag("--five", five);
run();
EXPECT_EQ(2, two);
EXPECT_TRUE(three);
EXPECT_TRUE(four);
EXPECT_TRUE(five);
}
TEST_F(TApp, IniFalseFlags) {
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;
bool three, four, five;
app.add_flag("--two", two);
app.add_flag("--three", three);
app.add_flag("--four", four);
app.add_flag("--five", five);
run();
EXPECT_EQ(-2, two);
EXPECT_FALSE(three);
EXPECT_TRUE(four);
EXPECT_TRUE(five);
}
TEST_F(TApp, IniFalseFlagsDef) {
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;
bool three, four, five;
app.add_flag("--two{false}", two);
app.add_flag("--three", three);
app.add_flag("!--four", four);
app.add_flag("--five", five);
run();
EXPECT_EQ(-2, two);
EXPECT_TRUE(three);
EXPECT_FALSE(four);
EXPECT_TRUE(five);
}
TEST_F(TApp, IniFalseFlagsDefDisableOverrideError) {
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;
bool four, five;
app.add_flag("--two{false}", two)->disable_flag_override();
app.add_flag("!--four", four);
app.add_flag("--five", five);
EXPECT_THROW(run(), CLI::ArgumentMismatch);
}
TEST_F(TApp, IniFalseFlagsDefDisableOverrideSuccess) {
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, four, val;
app.add_flag("--two{2}", two)->disable_flag_override();
app.add_flag("--four{4}", four)->disable_flag_override();
app.add_flag("--val", val);
run();
EXPECT_EQ(2, two);
EXPECT_EQ(4, four);
EXPECT_EQ(15, val);
}
TEST_F(TApp, IniOutputSimple) {
int v;
app.add_option("--simple", v);
args = {"--simple=3"};
run();
std::string str = app.config_to_str();
EXPECT_EQ("simple=3\n", str);
}
TEST_F(TApp, IniOutputNoConfigurable) {
int v1, v2;
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();
EXPECT_EQ("simple=3\n", str);
}
TEST_F(TApp, IniOutputShortSingleDescription) {
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);
EXPECT_THAT(str, HasSubstr("; " + description + "\n" + flag + "=false\n"));
}
TEST_F(TApp, IniOutputShortDoubleDescription) {
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);
EXPECT_THAT(
str, HasSubstr("; " + description1 + "\n" + flag1 + "=false\n\n; " + description2 + "\n" + flag2 + "=false\n"));
}
TEST_F(TApp, IniOutputGroups) {
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);
EXPECT_THAT(str, HasSubstr("group1"));
EXPECT_THAT(str, HasSubstr("group2"));
}
TEST_F(TApp, IniOutputHiddenOptions) {
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, "", true)->group("");
run();
std::string str = app.config_to_str(true, true);
EXPECT_THAT(str, HasSubstr("group1"));
EXPECT_THAT(str, HasSubstr("group2"));
EXPECT_THAT(str, HasSubstr("dval=12.7"));
auto loc = str.find("dval=12.7");
auto locg1 = str.find("group1");
EXPECT_GT(locg1, loc);
// make sure it doesn't come twice
loc = str.find("dval=12.7", loc + 4);
EXPECT_EQ(loc, std::string::npos);
}
TEST_F(TApp, IniOutputMultiLineDescription) {
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);
EXPECT_THAT(str, HasSubstr("; Some short description.\n"));
EXPECT_THAT(str, HasSubstr("; That has lines.\n"));
EXPECT_THAT(str, HasSubstr(flag + "=false\n"));
}
TEST_F(TApp, IniOutputOptionGroup) {
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, "", true)->group("");
run();
std::string str = app.config_to_str(true, true);
EXPECT_THAT(str, HasSubstr("group1"));
EXPECT_THAT(str, HasSubstr("group2"));
EXPECT_THAT(str, HasSubstr("dval=12.7"));
EXPECT_THAT(str, HasSubstr("group3"));
EXPECT_THAT(str, HasSubstr("g3 desc"));
auto loc = str.find("dval=12.7");
auto locg1 = str.find("group1");
auto locg3 = str.find("group3");
EXPECT_LT(locg1, loc);
// make sure it doesn't come twice
loc = str.find("dval=12.7", loc + 4);
EXPECT_EQ(loc, std::string::npos);
EXPECT_GT(locg3, locg1);
}
TEST_F(TApp, IniOutputVector) {
std::vector<int> v;
app.add_option("--vector", v);
args = {"--vector", "1", "2", "3"};
run();
std::string str = app.config_to_str();
EXPECT_EQ("vector=1 2 3\n", str);
}
TEST_F(TApp, IniOutputVectorTOML) {
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();
EXPECT_EQ("vector=[1, 2, 3]\n", str);
}
TEST_F(TApp, IniOutputVectorCustom) {
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();
EXPECT_EQ("vector:{1; 2; 3}\n", str);
}
TEST_F(TApp, IniOutputFlag) {
int v, q;
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();
EXPECT_THAT(str, HasSubstr("simple=3"));
EXPECT_THAT(str, Not(HasSubstr("nothing")));
EXPECT_THAT(str, HasSubstr("onething=true"));
EXPECT_THAT(str, HasSubstr("something=true true"));
str = app.config_to_str(true);
EXPECT_THAT(str, HasSubstr("nothing"));
}
TEST_F(TApp, IniOutputSet) {
int v;
app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
args = {"--simple=2"};
run();
std::string str = app.config_to_str();
EXPECT_THAT(str, HasSubstr("simple=2"));
}
TEST_F(TApp, IniOutputDefault) {
int v = 7;
app.add_option("--simple", v, "", true);
run();
std::string str = app.config_to_str();
EXPECT_THAT(str, Not(HasSubstr("simple=7")));
str = app.config_to_str(true);
EXPECT_THAT(str, HasSubstr("simple=7"));
}
TEST_F(TApp, IniOutputSubcom) {
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();
EXPECT_THAT(str, HasSubstr("simple=true"));
EXPECT_THAT(str, HasSubstr("other.newer=true"));
}
TEST_F(TApp, IniOutputSubcomConfigurable) {
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();
EXPECT_THAT(str, HasSubstr("simple=true"));
EXPECT_THAT(str, HasSubstr("[other]"));
EXPECT_THAT(str, HasSubstr("newer=true"));
EXPECT_EQ(str.find("other.newer=true"), std::string::npos);
}
TEST_F(TApp, IniOutputSubsubcom) {
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();
EXPECT_THAT(str, HasSubstr("simple=true"));
EXPECT_THAT(str, HasSubstr("other.newer=true"));
EXPECT_THAT(str, HasSubstr("other.sub2.newest=true"));
}
TEST_F(TApp, IniOutputSubsubcomConfigurable) {
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();
EXPECT_THAT(str, HasSubstr("simple=true"));
EXPECT_THAT(str, HasSubstr("[other]"));
EXPECT_THAT(str, HasSubstr("newer=true"));
EXPECT_THAT(str, HasSubstr("[other.sub2]"));
EXPECT_THAT(str, HasSubstr("newest=true"));
EXPECT_EQ(str.find("sub2.newest=true"), std::string::npos);
}
TEST_F(TApp, IniOutputSubsubcomConfigurableDeep) {
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();
EXPECT_THAT(str, HasSubstr("simple=true"));
EXPECT_THAT(str, HasSubstr("[other.sub2.sub-level2.sub-level3]"));
EXPECT_THAT(str, HasSubstr("absolute_newest=true"));
EXPECT_EQ(str.find(".absolute_newest=true"), std::string::npos);
}
TEST_F(TApp, IniQuotedOutput) {
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();
EXPECT_EQ("I am a string", val1);
EXPECT_EQ("I am a \"confusing\" string", val2);
std::string str = app.config_to_str();
EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
}
TEST_F(TApp, DefaultsIniQuotedOutput) {
std::string val1{"I am a string"};
app.add_option("--val1", val1, "", true);
std::string val2{R"(I am a "confusing" string)"};
app.add_option("--val2", val2, "", true);
run();
std::string str = app.config_to_str(true);
EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
}
// #298
TEST_F(TApp, StopReadingConfigOnClear) {
TempFile tmpini{"TestIniTmp.ini"};
app.set_config("--config", tmpini);
auto ptr = app.set_config(); // Should *not* read config file
EXPECT_EQ(ptr, nullptr);
{
std::ofstream out{tmpini};
out << "volume=1" << std::endl;
}
int volume = 0;
app.add_option("--volume", volume, "volume1");
run();
EXPECT_EQ(volume, 0);
}
TEST_F(TApp, ConfigWriteReadWrite) {
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);
EXPECT_EQ(config1, config2);
}