Skip to content

Commit a7aec7a

Browse files
committed
Merge bitcoin#15934: Merge settings one place instead of five places
083c954 Add settings_tests (Russell Yanofsky) 7f40528 Deduplicate settings merge code (Russell Yanofsky) 9dcb952 Add util::Settings struct and helper functions. (Russell Yanofsky) e2e37cf Remove includeconf nested scope (Russell Yanofsky) 5a84aa8 Rename includeconf variables for clarity (Russell Yanofsky) dc8e1e7 Clarify emptyIncludeConf logic (Russell Yanofsky) Pull request description: This is a refactoring-only change that makes it easier to add a new settings source. This PR doesn't change behavior. The [`util_ArgsMerge`](https://github.com/bitcoin/bitcoin/blob/deb2327b435925c6a39ca654a79283b8eb6aeb86/src/test/util_tests.cpp#L626-L822) and [`util_ChainMerge`](https://github.com/bitcoin/bitcoin/blob/deb2327b435925c6a39ca654a79283b8eb6aeb86/src/test/util_tests.cpp#L843-L924) tests added in bitcoin#15869 and bitcoin#15988 were written specifically to confirm that ArgsManager settings are parsed, merged, and returned the same way before and after this change. This change: - Makes it easier to add new settings sources that can get merged with existing sources (see 70675c3 from bitcoin#15935). - Separates parsing of settings from merging of settings, and deduplicates merging code so it doesn't happen five different places ([GetArg](https://github.com/bitcoin/bitcoin/blob/c459c5f70176928adcee4935813a2dbe7f4dbd51/src/util/system.cpp#L221-L244), [GetNetBoolArg](https://github.com/bitcoin/bitcoin/blob/c459c5f70176928adcee4935813a2dbe7f4dbd51/src/util/system.cpp#L255-L261), [GetArgs](https://github.com/bitcoin/bitcoin/blob/c459c5f70176928adcee4935813a2dbe7f4dbd51/src/util/system.cpp#L460-L467), [IsArgNegated](https://github.com/bitcoin/bitcoin/blob/c459c5f70176928adcee4935813a2dbe7f4dbd51/src/util/system.cpp#L482-L491), [GetUnsuitableSectionOnlyArgs](https://github.com/bitcoin/bitcoin/blob/c459c5f70176928adcee4935813a2dbe7f4dbd51/src/util/system.cpp#L343-L352)) in inconsistent ways. - Documents and tests current strange merging behaviors, so they be cleaned up in the future if resulting code simplifications and UX improvements warrant loss of backwards compatibility. The newly documented behaviors are: command line [ignored arguments](https://github.com/ryanofsky/bitcoin/blob/69d44f3cc75a68d404ca0e1ca2b4831fd2bac4bb/src/util/system.cpp#L323-L326) and [more ignored arguments](https://github.com/ryanofsky/bitcoin/blob/69d44f3cc75a68d404ca0e1ca2b4831fd2bac4bb/src/util/settings.cpp#L67-L72), and config file [reverse precedence](https://github.com/ryanofsky/bitcoin/blob/69d44f3cc75a68d404ca0e1ca2b4831fd2bac4bb/src/util/settings.cpp#L61-L65), [inconsistently applied top-level settings](https://github.com/ryanofsky/bitcoin/blob/69d44f3cc75a68d404ca0e1ca2b4831fd2bac4bb/src/util/settings.cpp#L55-L59), and [zombie values](https://github.com/ryanofsky/bitcoin/blob/69d44f3cc75a68d404ca0e1ca2b4831fd2bac4bb/src/util/settings.cpp#L101-L108). The original motivation for this change was to make it easy to add a new persistent setting source without introducing more bugs and inconsistencies. Two commits building on top of this to add a persistent `-wallet` setting are pretty straightforward and show how the new code can be extended: * 70675c3 from bitcoin#15935 – _Add \<datadir>/settings.json persistent settings storage_ * 04c80c4 from bitcoin#15937 – _Add loadwallet and createwallet RPC load_on_startup options_ ACKs for top commit: ariard: ACK 083c954 jnewbery: ACK 083c954 jamesob: ACK 083c954 Tree-SHA512: 5d106746a44d64d3963c4ef3f4a2fa668a4bedcc9018d3ea12c86beae2fda48a0b036241665837f68685712366f70f2e1faba84d193fa1f456013503097b7659
2 parents adceca2 + 083c954 commit a7aec7a

File tree

8 files changed

+613
-286
lines changed

8 files changed

+613
-286
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ BITCOIN_CORE_H = \
219219
util/memory.h \
220220
util/moneystr.h \
221221
util/rbf.h \
222+
util/settings.h \
222223
util/string.h \
223224
util/threadnames.h \
224225
util/time.h \
@@ -513,6 +514,7 @@ libbitcoin_util_a_SOURCES = \
513514
util/system.cpp \
514515
util/moneystr.cpp \
515516
util/rbf.cpp \
517+
util/settings.cpp \
516518
util/threadnames.cpp \
517519
util/spanparsing.cpp \
518520
util/strencodings.cpp \

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ BITCOIN_TESTS =\
152152
test/script_standard_tests.cpp \
153153
test/scriptnum_tests.cpp \
154154
test/serialize_tests.cpp \
155+
test/settings_tests.cpp \
155156
test/sighash_tests.cpp \
156157
test/sigopcount_tests.cpp \
157158
test/skiplist_tests.cpp \

src/test/settings_tests.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright (c) 2011-2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <util/settings.h>
6+
7+
#include <test/util.h>
8+
#include <test/util/setup_common.h>
9+
10+
#include <boost/test/unit_test.hpp>
11+
#include <univalue.h>
12+
#include <util/strencodings.h>
13+
#include <vector>
14+
15+
BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup)
16+
17+
//! Check settings struct contents against expected json strings.
18+
static void CheckValues(const util::Settings& settings, const std::string& single_val, const std::string& list_val)
19+
{
20+
util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false);
21+
util::SettingsValue list_value(util::SettingsValue::VARR);
22+
for (const auto& item : GetSettingsList(settings, "section", "name", false)) {
23+
list_value.push_back(item);
24+
}
25+
BOOST_CHECK_EQUAL(single_value.write().c_str(), single_val);
26+
BOOST_CHECK_EQUAL(list_value.write().c_str(), list_val);
27+
};
28+
29+
// Simple settings merge test case.
30+
BOOST_AUTO_TEST_CASE(Simple)
31+
{
32+
util::Settings settings;
33+
settings.command_line_options["name"].push_back("val1");
34+
settings.command_line_options["name"].push_back("val2");
35+
settings.ro_config["section"]["name"].push_back(2);
36+
37+
// The last given arg takes precedence when specified via commandline.
38+
CheckValues(settings, R"("val2")", R"(["val1","val2",2])");
39+
40+
util::Settings settings2;
41+
settings2.ro_config["section"]["name"].push_back("val2");
42+
settings2.ro_config["section"]["name"].push_back("val3");
43+
44+
// The first given arg takes precedence when specified via config file.
45+
CheckValues(settings2, R"("val2")", R"(["val2","val3"])");
46+
}
47+
48+
// Test different ways settings can be merged, and verify results. This test can
49+
// be used to confirm that updates to settings code don't change behavior
50+
// unintentionally.
51+
struct MergeTestingSetup : public BasicTestingSetup {
52+
//! Max number of actions to sequence together. Can decrease this when
53+
//! debugging to make test results easier to understand.
54+
static constexpr int MAX_ACTIONS = 3;
55+
56+
enum Action { END, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
57+
using ActionList = Action[MAX_ACTIONS];
58+
59+
//! Enumerate all possible test configurations.
60+
template <typename Fn>
61+
void ForEachMergeSetup(Fn&& fn)
62+
{
63+
ActionList arg_actions = {};
64+
// command_line_options do not have sections. Only iterate over SET and NEGATE
65+
ForEachNoDup(arg_actions, SET, NEGATE, [&]{
66+
ActionList conf_actions = {};
67+
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&]{
68+
for (bool force_set : {false, true}) {
69+
for (bool ignore_default_section_config : {false, true}) {
70+
fn(arg_actions, conf_actions, force_set, ignore_default_section_config);
71+
}
72+
}
73+
});
74+
});
75+
}
76+
};
77+
78+
// Regression test covering different ways config settings can be merged. The
79+
// test parses and merges settings, representing the results as strings that get
80+
// compared against an expected hash. To debug, the result strings can be dumped
81+
// to a file (see comments below).
82+
BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup)
83+
{
84+
CHash256 out_sha;
85+
FILE* out_file = nullptr;
86+
if (const char* out_path = getenv("SETTINGS_MERGE_TEST_OUT")) {
87+
out_file = fsbridge::fopen(out_path, "w");
88+
if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
89+
}
90+
91+
const std::string& network = CBaseChainParams::MAIN;
92+
ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool force_set,
93+
bool ignore_default_section_config) {
94+
std::string desc;
95+
int value_suffix = 0;
96+
util::Settings settings;
97+
98+
const std::string& name = ignore_default_section_config ? "wallet" : "server";
99+
auto push_values = [&](Action action, const char* value_prefix, const std::string& name_prefix,
100+
std::vector<util::SettingsValue>& dest) {
101+
if (action == SET || action == SECTION_SET) {
102+
for (int i = 0; i < 2; ++i) {
103+
dest.push_back(value_prefix + std::to_string(++value_suffix));
104+
desc += " " + name_prefix + name + "=" + dest.back().get_str();
105+
}
106+
} else if (action == NEGATE || action == SECTION_NEGATE) {
107+
dest.push_back(false);
108+
desc += " " + name_prefix + "no" + name;
109+
}
110+
};
111+
112+
if (force_set) {
113+
settings.forced_settings[name] = "forced";
114+
desc += " " + name + "=forced";
115+
}
116+
for (Action arg_action : arg_actions) {
117+
push_values(arg_action, "a", "-", settings.command_line_options[name]);
118+
}
119+
for (Action conf_action : conf_actions) {
120+
bool use_section = conf_action == SECTION_SET || conf_action == SECTION_NEGATE;
121+
push_values(conf_action, "c", use_section ? network + "." : "",
122+
settings.ro_config[use_section ? network : ""][name]);
123+
}
124+
125+
desc += " || ";
126+
desc += GetSetting(settings, network, name, ignore_default_section_config, /* get_chain_name= */ false).write();
127+
desc += " |";
128+
for (const auto& s : GetSettingsList(settings, network, name, ignore_default_section_config)) {
129+
desc += " ";
130+
desc += s.write();
131+
}
132+
desc += " |";
133+
if (OnlyHasDefaultSectionSetting(settings, network, name)) desc += " ignored";
134+
desc += "\n";
135+
136+
out_sha.Write((const unsigned char*)desc.data(), desc.size());
137+
if (out_file) {
138+
BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
139+
}
140+
});
141+
142+
if (out_file) {
143+
if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
144+
out_file = nullptr;
145+
}
146+
147+
unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
148+
out_sha.Finalize(out_sha_bytes);
149+
std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
150+
151+
// If check below fails, should manually dump the results with:
152+
//
153+
// SETTINGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=settings_tests/Merge
154+
//
155+
// And verify diff against previous results to make sure the changes are expected.
156+
//
157+
// Results file is formatted like:
158+
//
159+
// <input> || GetSetting() | GetSettingsList() | OnlyHasDefaultSectionSetting()
160+
BOOST_CHECK_EQUAL(out_sha_hex, "79db02d74e3e193196541b67c068b40ebd0c124a24b3ecbe9cbf7e85b1c4ba7a");
161+
}
162+
163+
BOOST_AUTO_TEST_SUITE_END()

src/test/util_tests.cpp

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <stdint.h>
1919
#include <thread>
20+
#include <univalue.h>
2021
#include <utility>
2122
#include <vector>
2223
#ifndef WIN32
@@ -166,14 +167,12 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
166167
struct TestArgsManager : public ArgsManager
167168
{
168169
TestArgsManager() { m_network_only_args.clear(); }
169-
std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; }
170-
std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; }
171170
void ReadConfigString(const std::string str_config)
172171
{
173172
std::istringstream streamConfig(str_config);
174173
{
175174
LOCK(cs_args);
176-
m_config_args.clear();
175+
m_settings.ro_config.clear();
177176
m_config_sections.clear();
178177
}
179178
std::string error;
@@ -193,6 +192,7 @@ struct TestArgsManager : public ArgsManager
193192
using ArgsManager::ReadConfigStream;
194193
using ArgsManager::cs_args;
195194
using ArgsManager::m_network;
195+
using ArgsManager::m_settings;
196196
};
197197

198198
BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -206,28 +206,29 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
206206
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
207207

208208
std::string error;
209+
LOCK(testArgs.cs_args);
209210
testArgs.SetupArgs({a, b, ccc, d});
210211
BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
211-
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
212+
BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
212213

213214
BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
214-
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
215+
BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
215216

216217
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
217218
// expectation: -ignored is ignored (program name argument),
218219
// -a, -b and -ccc end up in map, -d ignored because it is after
219220
// a non-option argument (non-GNU option parsing)
220-
BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty());
221+
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty());
221222
BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
222223
&& !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
223-
BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc")
224-
&& !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d"));
225-
226-
BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1);
227-
BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == "");
228-
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2);
229-
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument");
230-
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple");
224+
BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc")
225+
&& !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d"));
226+
227+
BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
228+
BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == "");
229+
BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2);
230+
BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument");
231+
BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple");
231232
BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
232233
}
233234

@@ -298,6 +299,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
298299
const char *argv_test[] = {
299300
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
300301
std::string error;
302+
LOCK(testArgs.cs_args);
301303
testArgs.SetupArgs({a, b, c, d, e, f});
302304
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
303305

@@ -306,8 +308,8 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
306308
BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
307309

308310
// Nothing else should be in the map
309-
BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 &&
310-
testArgs.GetConfigArgs().empty());
311+
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 &&
312+
testArgs.m_settings.ro_config.empty());
311313

312314
// The -no prefix should get stripped on the way in.
313315
BOOST_CHECK(!testArgs.IsArgSet("-nob"));
@@ -403,6 +405,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
403405
"iii=2\n";
404406

405407
TestArgsManager test_args;
408+
LOCK(test_args.cs_args);
406409
const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
407410
const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
408411
const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING);
@@ -419,22 +422,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
419422
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map
420423
// so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii
421424

422-
BOOST_CHECK(test_args.GetOverrideArgs().empty());
423-
BOOST_CHECK(test_args.GetConfigArgs().size() == 13);
424-
425-
BOOST_CHECK(test_args.GetConfigArgs().count("-a")
426-
&& test_args.GetConfigArgs().count("-b")
427-
&& test_args.GetConfigArgs().count("-ccc")
428-
&& test_args.GetConfigArgs().count("-d")
429-
&& test_args.GetConfigArgs().count("-fff")
430-
&& test_args.GetConfigArgs().count("-ggg")
431-
&& test_args.GetConfigArgs().count("-h")
432-
&& test_args.GetConfigArgs().count("-i")
425+
BOOST_CHECK(test_args.m_settings.command_line_options.empty());
426+
BOOST_CHECK(test_args.m_settings.ro_config.size() == 3);
427+
BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8);
428+
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
429+
BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
430+
431+
BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")
432+
&& test_args.m_settings.ro_config[""].count("b")
433+
&& test_args.m_settings.ro_config[""].count("ccc")
434+
&& test_args.m_settings.ro_config[""].count("d")
435+
&& test_args.m_settings.ro_config[""].count("fff")
436+
&& test_args.m_settings.ro_config[""].count("ggg")
437+
&& test_args.m_settings.ro_config[""].count("h")
438+
&& test_args.m_settings.ro_config[""].count("i")
433439
);
434-
BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc")
435-
&& test_args.GetConfigArgs().count("-sec1.h")
436-
&& test_args.GetConfigArgs().count("-sec2.ccc")
437-
&& test_args.GetConfigArgs().count("-sec2.iii")
440+
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")
441+
&& test_args.m_settings.ro_config["sec1"].count("h")
442+
&& test_args.m_settings.ro_config["sec2"].count("ccc")
443+
&& test_args.m_settings.ro_config["sec2"].count("iii")
438444
);
439445

440446
BOOST_CHECK(test_args.IsArgSet("-a")
@@ -573,24 +579,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
573579
BOOST_AUTO_TEST_CASE(util_GetArg)
574580
{
575581
TestArgsManager testArgs;
576-
testArgs.GetOverrideArgs().clear();
577-
testArgs.GetOverrideArgs()["strtest1"] = {"string..."};
582+
LOCK(testArgs.cs_args);
583+
testArgs.m_settings.command_line_options.clear();
584+
testArgs.m_settings.command_line_options["strtest1"] = {"string..."};
578585
// strtest2 undefined on purpose
579-
testArgs.GetOverrideArgs()["inttest1"] = {"12345"};
580-
testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"};
586+
testArgs.m_settings.command_line_options["inttest1"] = {"12345"};
587+
testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"};
581588
// inttest3 undefined on purpose
582-
testArgs.GetOverrideArgs()["booltest1"] = {""};
589+
testArgs.m_settings.command_line_options["booltest1"] = {""};
583590
// booltest2 undefined on purpose
584-
testArgs.GetOverrideArgs()["booltest3"] = {"0"};
585-
testArgs.GetOverrideArgs()["booltest4"] = {"1"};
591+
testArgs.m_settings.command_line_options["booltest3"] = {"0"};
592+
testArgs.m_settings.command_line_options["booltest4"] = {"1"};
586593

587594
// priorities
588-
testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"};
589-
testArgs.GetConfigArgs()["pritest2"] = {"a", "b"};
590-
testArgs.GetOverrideArgs()["pritest3"] = {"a"};
591-
testArgs.GetConfigArgs()["pritest3"] = {"b"};
592-
testArgs.GetOverrideArgs()["pritest4"] = {"a","b"};
593-
testArgs.GetConfigArgs()["pritest4"] = {"c","d"};
595+
testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"};
596+
testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"};
597+
testArgs.m_settings.command_line_options["pritest3"] = {"a"};
598+
testArgs.m_settings.ro_config[""]["pritest3"] = {"b"};
599+
testArgs.m_settings.command_line_options["pritest4"] = {"a","b"};
600+
testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"};
594601

595602
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
596603
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");

0 commit comments

Comments
 (0)