Line data Source code
1 : // Copyright (c) 2011-2022 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/system.h>
6 : #include <fs.h>
7 : #include <sync.h>
8 : #include <test/util/logging.h>
9 : #include <test/util/setup_common.h>
10 : #include <test/util/str.h>
11 : #include <util/strencodings.h>
12 : #include <univalue.h>
13 :
14 : #include <array>
15 : #include <optional>
16 : #include <cstdint>
17 : #include <cstring>
18 : #include <vector>
19 :
20 : #include <boost/test/unit_test.hpp>
21 :
22 146 : BOOST_FIXTURE_TEST_SUITE(argsman_tests, BasicTestingSetup)
23 :
24 149 : BOOST_AUTO_TEST_CASE(util_datadir)
25 : {
26 : // Use local args variable instead of m_args to avoid making assumptions about test setup
27 1 : ArgsManager args;
28 1 : args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
29 :
30 1 : const fs::path dd_norm = args.GetDataDirBase();
31 :
32 1 : args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/");
33 1 : args.ClearPathCache();
34 1 : BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
35 :
36 1 : args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.");
37 1 : args.ClearPathCache();
38 1 : BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
39 :
40 1 : args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./");
41 1 : args.ClearPathCache();
42 1 : BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
43 :
44 1 : args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//");
45 1 : args.ClearPathCache();
46 1 : BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
47 1 : }
48 :
49 : struct TestArgsManager : public ArgsManager
50 : {
51 56274 : TestArgsManager() { m_network_only_args.clear(); }
52 13 : void ReadConfigString(const std::string str_config)
53 : {
54 13 : std::istringstream streamConfig(str_config);
55 : {
56 13 : LOCK(cs_args);
57 13 : m_settings.ro_config.clear();
58 13 : m_config_sections.clear();
59 13 : }
60 13 : std::string error;
61 13 : BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error));
62 13 : }
63 13359 : void SetNetworkOnlyArg(const std::string arg)
64 : {
65 13359 : LOCK(cs_args);
66 13359 : m_network_only_args.insert(arg);
67 13359 : }
68 53 : void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
69 : {
70 125 : for (const auto& arg : args) {
71 72 : AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
72 : }
73 53 : }
74 : using ArgsManager::GetSetting;
75 : using ArgsManager::GetSettingsList;
76 : using ArgsManager::ReadConfigStream;
77 : using ArgsManager::cs_args;
78 : using ArgsManager::m_network;
79 : using ArgsManager::m_settings;
80 : };
81 :
82 : //! Test GetSetting and GetArg type coercion, negation, and default value handling.
83 1 : class CheckValueTest : public TestChain100Setup
84 : {
85 : public:
86 : struct Expect {
87 : util::SettingsValue setting;
88 13 : bool default_string = false;
89 13 : bool default_int = false;
90 13 : bool default_bool = false;
91 13 : const char* string_value = nullptr;
92 : std::optional<int64_t> int_value;
93 : std::optional<bool> bool_value;
94 : std::optional<std::vector<std::string>> list_value;
95 13 : const char* error = nullptr;
96 :
97 52 : explicit Expect(util::SettingsValue s) : setting(std::move(s)) {}
98 1 : Expect& DefaultString() { default_string = true; return *this; }
99 1 : Expect& DefaultInt() { default_int = true; return *this; }
100 1 : Expect& DefaultBool() { default_bool = true; return *this; }
101 12 : Expect& String(const char* s) { string_value = s; return *this; }
102 12 : Expect& Int(int64_t i) { int_value = i; return *this; }
103 12 : Expect& Bool(bool b) { bool_value = b; return *this; }
104 13 : Expect& List(std::vector<std::string> m) { list_value = std::move(m); return *this; }
105 : Expect& Error(const char* e) { error = e; return *this; }
106 : };
107 :
108 13 : void CheckValue(unsigned int flags, const char* arg, const Expect& expect)
109 : {
110 13 : TestArgsManager test;
111 13 : test.SetupArgs({{"-value", flags}});
112 13 : const char* argv[] = {"ignored", arg};
113 13 : std::string error;
114 13 : bool success = test.ParseParameters(arg ? 2 : 1, argv, error);
115 :
116 13 : BOOST_CHECK_EQUAL(test.GetSetting("-value").write(), expect.setting.write());
117 13 : auto settings_list = test.GetSettingsList("-value");
118 13 : if (expect.setting.isNull() || expect.setting.isFalse()) {
119 5 : BOOST_CHECK_EQUAL(settings_list.size(), 0U);
120 5 : } else {
121 8 : BOOST_CHECK_EQUAL(settings_list.size(), 1U);
122 8 : BOOST_CHECK_EQUAL(settings_list[0].write(), expect.setting.write());
123 : }
124 :
125 13 : if (expect.error) {
126 0 : BOOST_CHECK(!success);
127 0 : BOOST_CHECK_NE(error.find(expect.error), std::string::npos);
128 0 : } else {
129 13 : BOOST_CHECK(success);
130 13 : BOOST_CHECK_EQUAL(error, "");
131 : }
132 :
133 13 : if (expect.default_string) {
134 1 : BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), "zzzzz");
135 13 : } else if (expect.string_value) {
136 12 : BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), expect.string_value);
137 12 : } else {
138 0 : BOOST_CHECK(!success);
139 : }
140 :
141 13 : if (expect.default_int) {
142 1 : BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), 99999);
143 13 : } else if (expect.int_value) {
144 12 : BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), *expect.int_value);
145 12 : } else {
146 0 : BOOST_CHECK(!success);
147 : }
148 :
149 13 : if (expect.default_bool) {
150 1 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), false);
151 1 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), true);
152 13 : } else if (expect.bool_value) {
153 12 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), *expect.bool_value);
154 12 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), *expect.bool_value);
155 12 : } else {
156 0 : BOOST_CHECK(!success);
157 : }
158 :
159 13 : if (expect.list_value) {
160 13 : auto l = test.GetArgs("-value");
161 13 : BOOST_CHECK_EQUAL_COLLECTIONS(l.begin(), l.end(), expect.list_value->begin(), expect.list_value->end());
162 13 : } else {
163 0 : BOOST_CHECK(!success);
164 : }
165 13 : }
166 : };
167 :
168 148 : BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest)
169 : {
170 : using M = ArgsManager;
171 :
172 1 : CheckValue(M::ALLOW_ANY, nullptr, Expect{{}}.DefaultString().DefaultInt().DefaultBool().List({}));
173 1 : CheckValue(M::ALLOW_ANY, "-novalue", Expect{false}.String("0").Int(0).Bool(false).List({}));
174 1 : CheckValue(M::ALLOW_ANY, "-novalue=", Expect{false}.String("0").Int(0).Bool(false).List({}));
175 1 : CheckValue(M::ALLOW_ANY, "-novalue=0", Expect{true}.String("1").Int(1).Bool(true).List({"1"}));
176 1 : CheckValue(M::ALLOW_ANY, "-novalue=1", Expect{false}.String("0").Int(0).Bool(false).List({}));
177 1 : CheckValue(M::ALLOW_ANY, "-novalue=2", Expect{false}.String("0").Int(0).Bool(false).List({}));
178 1 : CheckValue(M::ALLOW_ANY, "-novalue=abc", Expect{true}.String("1").Int(1).Bool(true).List({"1"}));
179 1 : CheckValue(M::ALLOW_ANY, "-value", Expect{""}.String("").Int(0).Bool(true).List({""}));
180 1 : CheckValue(M::ALLOW_ANY, "-value=", Expect{""}.String("").Int(0).Bool(true).List({""}));
181 1 : CheckValue(M::ALLOW_ANY, "-value=0", Expect{"0"}.String("0").Int(0).Bool(false).List({"0"}));
182 1 : CheckValue(M::ALLOW_ANY, "-value=1", Expect{"1"}.String("1").Int(1).Bool(true).List({"1"}));
183 1 : CheckValue(M::ALLOW_ANY, "-value=2", Expect{"2"}.String("2").Int(2).Bool(true).List({"2"}));
184 1 : CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
185 1 : }
186 :
187 : struct NoIncludeConfTest {
188 3 : std::string Parse(const char* arg)
189 : {
190 3 : TestArgsManager test;
191 3 : test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}});
192 3 : std::array argv{"ignored", arg};
193 3 : std::string error;
194 3 : (void)test.ParseParameters(argv.size(), argv.data(), error);
195 3 : return error;
196 3 : }
197 : };
198 :
199 148 : BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest)
200 : {
201 1 : BOOST_CHECK_EQUAL(Parse("-noincludeconf"), "");
202 1 : BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\"");
203 1 : BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\"");
204 1 : }
205 :
206 149 : BOOST_AUTO_TEST_CASE(util_ParseParameters)
207 : {
208 1 : TestArgsManager testArgs;
209 1 : const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
210 1 : const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
211 1 : const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
212 1 : const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
213 :
214 1 : const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
215 :
216 1 : std::string error;
217 1 : LOCK(testArgs.cs_args);
218 1 : testArgs.SetupArgs({a, b, ccc, d});
219 1 : BOOST_CHECK(testArgs.ParseParameters(0, argv_test, error));
220 1 : BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
221 :
222 1 : BOOST_CHECK(testArgs.ParseParameters(1, argv_test, error));
223 1 : BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
224 :
225 1 : BOOST_CHECK(testArgs.ParseParameters(7, argv_test, error));
226 : // expectation: -ignored is ignored (program name argument),
227 : // -a, -b and -ccc end up in map, -d ignored because it is after
228 : // a non-option argument (non-GNU option parsing)
229 1 : BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty());
230 1 : BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
231 : && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
232 1 : 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")
233 : && !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d"));
234 :
235 1 : BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
236 1 : BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == "");
237 1 : BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2);
238 1 : BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument");
239 1 : BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple");
240 1 : BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
241 1 : }
242 :
243 149 : BOOST_AUTO_TEST_CASE(util_ParseInvalidParameters)
244 : {
245 1 : TestArgsManager test;
246 1 : test.SetupArgs({{"-registered", ArgsManager::ALLOW_ANY}});
247 :
248 1 : const char* argv[] = {"ignored", "-registered"};
249 1 : std::string error;
250 1 : BOOST_CHECK(test.ParseParameters(2, argv, error));
251 1 : BOOST_CHECK_EQUAL(error, "");
252 :
253 1 : argv[1] = "-unregistered";
254 1 : BOOST_CHECK(!test.ParseParameters(2, argv, error));
255 1 : BOOST_CHECK_EQUAL(error, "Invalid parameter -unregistered");
256 :
257 : // Make sure registered parameters prefixed with a chain name trigger errors.
258 : // (Previously, they were accepted and ignored.)
259 1 : argv[1] = "-test.registered";
260 1 : BOOST_CHECK(!test.ParseParameters(2, argv, error));
261 1 : BOOST_CHECK_EQUAL(error, "Invalid parameter -test.registered");
262 1 : }
263 :
264 31 : static void TestParse(const std::string& str, bool expected_bool, int64_t expected_int)
265 : {
266 31 : TestArgsManager test;
267 31 : test.SetupArgs({{"-value", ArgsManager::ALLOW_ANY}});
268 31 : std::string arg = "-value=" + str;
269 31 : const char* argv[] = {"ignored", arg.c_str()};
270 31 : std::string error;
271 31 : BOOST_CHECK(test.ParseParameters(2, argv, error));
272 31 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool);
273 31 : BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool);
274 31 : BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99998), expected_int);
275 31 : BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), expected_int);
276 31 : }
277 :
278 : // Test bool and int parsing.
279 149 : BOOST_AUTO_TEST_CASE(util_ArgParsing)
280 : {
281 : // Some of these cases could be ambiguous or surprising to users, and might
282 : // be worth triggering errors or warnings in the future. But for now basic
283 : // test coverage is useful to avoid breaking backwards compatibility
284 : // unintentionally.
285 1 : TestParse("", true, 0);
286 1 : TestParse(" ", false, 0);
287 1 : TestParse("0", false, 0);
288 1 : TestParse("0 ", false, 0);
289 1 : TestParse(" 0", false, 0);
290 1 : TestParse("+0", false, 0);
291 1 : TestParse("-0", false, 0);
292 1 : TestParse("5", true, 5);
293 1 : TestParse("5 ", true, 5);
294 1 : TestParse(" 5", true, 5);
295 1 : TestParse("+5", true, 5);
296 1 : TestParse("-5", true, -5);
297 1 : TestParse("0 5", false, 0);
298 1 : TestParse("5 0", true, 5);
299 1 : TestParse("050", true, 50);
300 1 : TestParse("0.", false, 0);
301 1 : TestParse("5.", true, 5);
302 1 : TestParse("0.0", false, 0);
303 1 : TestParse("0.5", false, 0);
304 1 : TestParse("5.0", true, 5);
305 1 : TestParse("5.5", true, 5);
306 1 : TestParse("x", false, 0);
307 1 : TestParse("x0", false, 0);
308 1 : TestParse("x5", false, 0);
309 1 : TestParse("0x", false, 0);
310 1 : TestParse("5x", true, 5);
311 1 : TestParse("0x5", false, 0);
312 1 : TestParse("false", false, 0);
313 1 : TestParse("true", false, 0);
314 1 : TestParse("yes", false, 0);
315 1 : TestParse("no", false, 0);
316 1 : }
317 :
318 149 : BOOST_AUTO_TEST_CASE(util_GetBoolArg)
319 : {
320 1 : TestArgsManager testArgs;
321 1 : const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
322 1 : const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
323 1 : const auto c = std::make_pair("-c", ArgsManager::ALLOW_ANY);
324 1 : const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
325 1 : const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
326 1 : const auto f = std::make_pair("-f", ArgsManager::ALLOW_ANY);
327 :
328 1 : const char *argv_test[] = {
329 : "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
330 1 : std::string error;
331 1 : LOCK(testArgs.cs_args);
332 1 : testArgs.SetupArgs({a, b, c, d, e, f});
333 1 : BOOST_CHECK(testArgs.ParseParameters(7, argv_test, error));
334 :
335 : // Each letter should be set.
336 8 : for (const char opt : "abcdef")
337 7 : BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
338 :
339 : // Nothing else should be in the map
340 1 : BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 &&
341 : testArgs.m_settings.ro_config.empty());
342 :
343 : // The -no prefix should get stripped on the way in.
344 1 : BOOST_CHECK(!testArgs.IsArgSet("-nob"));
345 :
346 : // The -b option is flagged as negated, and nothing else is
347 1 : BOOST_CHECK(testArgs.IsArgNegated("-b"));
348 1 : BOOST_CHECK(!testArgs.IsArgNegated("-a"));
349 :
350 : // Check expected values.
351 1 : BOOST_CHECK(testArgs.GetBoolArg("-a", false) == true);
352 1 : BOOST_CHECK(testArgs.GetBoolArg("-b", true) == false);
353 1 : BOOST_CHECK(testArgs.GetBoolArg("-c", true) == false);
354 1 : BOOST_CHECK(testArgs.GetBoolArg("-d", false) == true);
355 1 : BOOST_CHECK(testArgs.GetBoolArg("-e", true) == false);
356 1 : BOOST_CHECK(testArgs.GetBoolArg("-f", true) == false);
357 1 : }
358 :
359 149 : BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
360 : {
361 : // Test some awful edge cases that hopefully no user will ever exercise.
362 1 : TestArgsManager testArgs;
363 :
364 : // Params test
365 1 : const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
366 1 : const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
367 1 : const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
368 1 : testArgs.SetupArgs({foo, bar});
369 1 : std::string error;
370 1 : BOOST_CHECK(testArgs.ParseParameters(4, argv_test, error));
371 :
372 : // This was passed twice, second one overrides the negative setting.
373 1 : BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
374 1 : BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "");
375 :
376 : // A double negative is a positive, and not marked as negated.
377 1 : BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
378 1 : BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1");
379 :
380 : // Config test
381 1 : const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n";
382 1 : BOOST_CHECK(testArgs.ParseParameters(1, argv_test, error));
383 1 : testArgs.ReadConfigString(conf_test);
384 :
385 : // This was passed twice, second one overrides the negative setting,
386 : // and the value.
387 1 : BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
388 1 : BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1");
389 :
390 : // A double negative is a positive, and does not count as negated.
391 1 : BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
392 1 : BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1");
393 :
394 : // Combined test
395 1 : const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"};
396 1 : const char *combo_test_conf = "foo=1\nnobar=1\n";
397 1 : BOOST_CHECK(testArgs.ParseParameters(3, combo_test_args, error));
398 1 : testArgs.ReadConfigString(combo_test_conf);
399 :
400 : // Command line overrides, but doesn't erase old setting
401 1 : BOOST_CHECK(testArgs.IsArgNegated("-foo"));
402 1 : BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0");
403 1 : BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0);
404 :
405 : // Command line overrides, but doesn't erase old setting
406 1 : BOOST_CHECK(!testArgs.IsArgNegated("-bar"));
407 1 : BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "");
408 1 : BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1
409 : && testArgs.GetArgs("-bar").front() == "");
410 1 : }
411 :
412 149 : BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
413 : {
414 1 : const char *str_config =
415 : "a=\n"
416 : "b=1\n"
417 : "ccc=argument\n"
418 : "ccc=multiple\n"
419 : "d=e\n"
420 : "nofff=1\n"
421 : "noggg=0\n"
422 : "h=1\n"
423 : "noh=1\n"
424 : "noi=1\n"
425 : "i=1\n"
426 : "sec1.ccc=extend1\n"
427 : "\n"
428 : "[sec1]\n"
429 : "ccc=extend2\n"
430 : "d=eee\n"
431 : "h=1\n"
432 : "[sec2]\n"
433 : "ccc=extend3\n"
434 : "iii=2\n";
435 :
436 1 : TestArgsManager test_args;
437 1 : LOCK(test_args.cs_args);
438 1 : const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
439 1 : const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
440 1 : const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
441 1 : const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
442 1 : const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
443 1 : const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_ANY);
444 1 : const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_ANY);
445 1 : const auto h = std::make_pair("-h", ArgsManager::ALLOW_ANY);
446 1 : const auto i = std::make_pair("-i", ArgsManager::ALLOW_ANY);
447 1 : const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_ANY);
448 1 : test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii});
449 :
450 1 : test_args.ReadConfigString(str_config);
451 : // expectation: a, b, ccc, d, fff, ggg, h, i end up in map
452 : // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii
453 :
454 1 : BOOST_CHECK(test_args.m_settings.command_line_options.empty());
455 1 : BOOST_CHECK(test_args.m_settings.ro_config.size() == 3);
456 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8);
457 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
458 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
459 :
460 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("a"));
461 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("b"));
462 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("ccc"));
463 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("d"));
464 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("fff"));
465 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("ggg"));
466 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("h"));
467 1 : BOOST_CHECK(test_args.m_settings.ro_config[""].count("i"));
468 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc"));
469 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("h"));
470 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("ccc"));
471 1 : BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("iii"));
472 :
473 1 : BOOST_CHECK(test_args.IsArgSet("-a"));
474 1 : BOOST_CHECK(test_args.IsArgSet("-b"));
475 1 : BOOST_CHECK(test_args.IsArgSet("-ccc"));
476 1 : BOOST_CHECK(test_args.IsArgSet("-d"));
477 1 : BOOST_CHECK(test_args.IsArgSet("-fff"));
478 1 : BOOST_CHECK(test_args.IsArgSet("-ggg"));
479 1 : BOOST_CHECK(test_args.IsArgSet("-h"));
480 1 : BOOST_CHECK(test_args.IsArgSet("-i"));
481 1 : BOOST_CHECK(!test_args.IsArgSet("-zzz"));
482 1 : BOOST_CHECK(!test_args.IsArgSet("-iii"));
483 :
484 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), "");
485 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1");
486 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-ccc", "xxx"), "argument");
487 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-d", "xxx"), "e");
488 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0");
489 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1");
490 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-h", "xxx"), "0");
491 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-i", "xxx"), "1");
492 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx");
493 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx");
494 :
495 3 : for (const bool def : {false, true}) {
496 2 : BOOST_CHECK(test_args.GetBoolArg("-a", def));
497 2 : BOOST_CHECK(test_args.GetBoolArg("-b", def));
498 2 : BOOST_CHECK(!test_args.GetBoolArg("-ccc", def));
499 2 : BOOST_CHECK(!test_args.GetBoolArg("-d", def));
500 2 : BOOST_CHECK(!test_args.GetBoolArg("-fff", def));
501 2 : BOOST_CHECK(test_args.GetBoolArg("-ggg", def));
502 2 : BOOST_CHECK(!test_args.GetBoolArg("-h", def));
503 2 : BOOST_CHECK(test_args.GetBoolArg("-i", def));
504 2 : BOOST_CHECK(test_args.GetBoolArg("-zzz", def) == def);
505 2 : BOOST_CHECK(test_args.GetBoolArg("-iii", def) == def);
506 : }
507 :
508 1 : BOOST_CHECK(test_args.GetArgs("-a").size() == 1
509 : && test_args.GetArgs("-a").front() == "");
510 1 : BOOST_CHECK(test_args.GetArgs("-b").size() == 1
511 : && test_args.GetArgs("-b").front() == "1");
512 1 : BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2
513 : && test_args.GetArgs("-ccc").front() == "argument"
514 : && test_args.GetArgs("-ccc").back() == "multiple");
515 1 : BOOST_CHECK(test_args.GetArgs("-fff").size() == 0);
516 1 : BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0);
517 1 : BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1
518 : && test_args.GetArgs("-ggg").front() == "1");
519 1 : BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0);
520 1 : BOOST_CHECK(test_args.GetArgs("-h").size() == 0);
521 1 : BOOST_CHECK(test_args.GetArgs("-noh").size() == 0);
522 1 : BOOST_CHECK(test_args.GetArgs("-i").size() == 1
523 : && test_args.GetArgs("-i").front() == "1");
524 1 : BOOST_CHECK(test_args.GetArgs("-noi").size() == 0);
525 1 : BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0);
526 :
527 1 : BOOST_CHECK(!test_args.IsArgNegated("-a"));
528 1 : BOOST_CHECK(!test_args.IsArgNegated("-b"));
529 1 : BOOST_CHECK(!test_args.IsArgNegated("-ccc"));
530 1 : BOOST_CHECK(!test_args.IsArgNegated("-d"));
531 1 : BOOST_CHECK(test_args.IsArgNegated("-fff"));
532 1 : BOOST_CHECK(!test_args.IsArgNegated("-ggg"));
533 1 : BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence
534 1 : BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence
535 1 : BOOST_CHECK(!test_args.IsArgNegated("-zzz"));
536 :
537 : // Test sections work
538 1 : test_args.SelectConfigNetwork("sec1");
539 :
540 : // same as original
541 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), "");
542 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1");
543 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0");
544 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1");
545 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx");
546 1 : BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx");
547 : // d is overridden
548 1 : BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee");
549 : // section-specific setting
550 1 : BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1");
551 : // section takes priority for multiple values
552 1 : BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1");
553 : // check multiple values works
554 1 : const std::vector<std::string> sec1_ccc_expected = {"extend1","extend2","argument","multiple"};
555 1 : const auto& sec1_ccc_res = test_args.GetArgs("-ccc");
556 1 : BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end());
557 :
558 1 : test_args.SelectConfigNetwork("sec2");
559 :
560 : // same as original
561 1 : BOOST_CHECK(test_args.GetArg("-a", "xxx") == "");
562 1 : BOOST_CHECK(test_args.GetArg("-b", "xxx") == "1");
563 1 : BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e");
564 1 : BOOST_CHECK(test_args.GetArg("-fff", "xxx") == "0");
565 1 : BOOST_CHECK(test_args.GetArg("-ggg", "xxx") == "1");
566 1 : BOOST_CHECK(test_args.GetArg("-zzz", "xxx") == "xxx");
567 1 : BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
568 : // section-specific setting
569 1 : BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2");
570 : // section takes priority for multiple values
571 1 : BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3");
572 : // check multiple values works
573 1 : const std::vector<std::string> sec2_ccc_expected = {"extend3","argument","multiple"};
574 1 : const auto& sec2_ccc_res = test_args.GetArgs("-ccc");
575 1 : BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end());
576 :
577 : // Test section only options
578 :
579 1 : test_args.SetNetworkOnlyArg("-d");
580 1 : test_args.SetNetworkOnlyArg("-ccc");
581 1 : test_args.SetNetworkOnlyArg("-h");
582 :
583 1 : test_args.SelectConfigNetwork(CBaseChainParams::MAIN);
584 1 : BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e");
585 1 : BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2);
586 1 : BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
587 :
588 1 : test_args.SelectConfigNetwork("sec1");
589 1 : BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee");
590 1 : BOOST_CHECK(test_args.GetArgs("-d").size() == 1);
591 1 : BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2);
592 1 : BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1");
593 :
594 1 : test_args.SelectConfigNetwork("sec2");
595 1 : BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx");
596 1 : BOOST_CHECK(test_args.GetArgs("-d").size() == 0);
597 1 : BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1);
598 1 : BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
599 1 : }
600 :
601 149 : BOOST_AUTO_TEST_CASE(util_GetArg)
602 : {
603 1 : TestArgsManager testArgs;
604 1 : LOCK(testArgs.cs_args);
605 1 : testArgs.m_settings.command_line_options.clear();
606 1 : testArgs.m_settings.command_line_options["strtest1"] = {"string..."};
607 : // strtest2 undefined on purpose
608 1 : testArgs.m_settings.command_line_options["inttest1"] = {"12345"};
609 1 : testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"};
610 : // inttest3 undefined on purpose
611 1 : testArgs.m_settings.command_line_options["booltest1"] = {""};
612 : // booltest2 undefined on purpose
613 1 : testArgs.m_settings.command_line_options["booltest3"] = {"0"};
614 1 : testArgs.m_settings.command_line_options["booltest4"] = {"1"};
615 :
616 : // priorities
617 1 : testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"};
618 1 : testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"};
619 1 : testArgs.m_settings.command_line_options["pritest3"] = {"a"};
620 1 : testArgs.m_settings.ro_config[""]["pritest3"] = {"b"};
621 1 : testArgs.m_settings.command_line_options["pritest4"] = {"a","b"};
622 1 : testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"};
623 :
624 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
625 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
626 1 : BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest1", -1), 12345);
627 1 : BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest2", -1), 81985529216486895LL);
628 1 : BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest3", -1), -1);
629 1 : BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
630 1 : BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false);
631 1 : BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false);
632 1 : BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true);
633 :
634 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b");
635 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a");
636 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a");
637 1 : BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b");
638 1 : }
639 :
640 149 : BOOST_AUTO_TEST_CASE(util_GetChainName)
641 : {
642 1 : TestArgsManager test_args;
643 1 : const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY);
644 1 : const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY);
645 1 : test_args.SetupArgs({testnet, regtest});
646 :
647 1 : const char* argv_testnet[] = {"cmd", "-testnet"};
648 1 : const char* argv_regtest[] = {"cmd", "-regtest"};
649 1 : const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"};
650 1 : const char* argv_both[] = {"cmd", "-testnet", "-regtest"};
651 :
652 : // equivalent to "-testnet"
653 : // regtest in testnet section is ignored
654 1 : const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1";
655 1 : std::string error;
656 :
657 1 : BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error));
658 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "main");
659 :
660 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error));
661 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
662 :
663 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error));
664 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest");
665 :
666 1 : BOOST_CHECK(test_args.ParseParameters(3, argv_test_no_reg, error));
667 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
668 :
669 1 : BOOST_CHECK(test_args.ParseParameters(3, argv_both, error));
670 2 : BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
671 :
672 1 : BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error));
673 1 : test_args.ReadConfigString(testnetconf);
674 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
675 :
676 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error));
677 1 : test_args.ReadConfigString(testnetconf);
678 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
679 :
680 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error));
681 1 : test_args.ReadConfigString(testnetconf);
682 2 : BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
683 :
684 1 : BOOST_CHECK(test_args.ParseParameters(3, argv_test_no_reg, error));
685 1 : test_args.ReadConfigString(testnetconf);
686 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
687 :
688 1 : BOOST_CHECK(test_args.ParseParameters(3, argv_both, error));
689 1 : test_args.ReadConfigString(testnetconf);
690 2 : BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
691 :
692 : // check setting the network to test (and thus making
693 : // [test] regtest=1 potentially relevant) doesn't break things
694 1 : test_args.SelectConfigNetwork("test");
695 :
696 1 : BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error));
697 1 : test_args.ReadConfigString(testnetconf);
698 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
699 :
700 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error));
701 1 : test_args.ReadConfigString(testnetconf);
702 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
703 :
704 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error));
705 1 : test_args.ReadConfigString(testnetconf);
706 2 : BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
707 :
708 1 : BOOST_CHECK(test_args.ParseParameters(2, argv_test_no_reg, error));
709 1 : test_args.ReadConfigString(testnetconf);
710 1 : BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
711 :
712 1 : BOOST_CHECK(test_args.ParseParameters(3, argv_both, error));
713 1 : test_args.ReadConfigString(testnetconf);
714 2 : BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
715 6 : }
716 :
717 : // Test different ways settings can be merged, and verify results. This test can
718 : // be used to confirm that updates to settings code don't change behavior
719 : // unintentionally.
720 : //
721 : // The test covers:
722 : //
723 : // - Combining different setting actions. Possible actions are: configuring a
724 : // setting, negating a setting (adding "-no" prefix), and configuring/negating
725 : // settings in a network section (adding "main." or "test." prefixes).
726 : //
727 : // - Combining settings from command line arguments and a config file.
728 : //
729 : // - Combining SoftSet and ForceSet calls.
730 : //
731 : // - Testing "main" and "test" network values to make sure settings from network
732 : // sections are applied and to check for mainnet-specific behaviors like
733 : // inheriting settings from the default section.
734 : //
735 : // - Testing network-specific settings like "-wallet", that may be ignored
736 : // outside a network section, and non-network specific settings like "-server"
737 : // that aren't sensitive to the network.
738 : //
739 1 : struct ArgsMergeTestingSetup : public BasicTestingSetup {
740 : //! Max number of actions to sequence together. Can decrease this when
741 : //! debugging to make test results easier to understand.
742 : static constexpr int MAX_ACTIONS = 3;
743 :
744 : enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
745 : using ActionList = Action[MAX_ACTIONS];
746 :
747 : //! Enumerate all possible test configurations.
748 : template <typename Fn>
749 1 : void ForEachMergeSetup(Fn&& fn)
750 : {
751 1 : ActionList arg_actions = {};
752 : // command_line_options do not have sections. Only iterate over SET and NEGATE
753 8 : ForEachNoDup(arg_actions, SET, NEGATE, [&] {
754 7 : ActionList conf_actions = {};
755 378 : ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
756 1113 : for (bool soft_set : {false, true}) {
757 2226 : for (bool force_set : {false, true}) {
758 5936 : for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::DEVNET}) {
759 17808 : for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::DEVNET}) {
760 40068 : for (bool net_specific : {false, true}) {
761 26712 : fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
762 : }
763 : }
764 : }
765 : }
766 : }
767 371 : });
768 7 : });
769 1 : }
770 :
771 : //! Translate actions into a list of <key>=<value> setting strings.
772 53424 : std::vector<std::string> GetValues(const ActionList& actions,
773 : const std::string& section,
774 : const std::string& name,
775 : const std::string& value_prefix)
776 : {
777 53424 : std::vector<std::string> values;
778 53424 : int suffix = 0;
779 167760 : for (Action action : actions) {
780 141984 : if (action == NONE) break;
781 114336 : std::string prefix;
782 114336 : if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + ".";
783 91440 : if (action == SET || action == SECTION_SET) {
784 205776 : for (int i = 0; i < 2; ++i) {
785 114336 : values.push_back(prefix + name + "=" + value_prefix + ToString(++suffix));
786 114336 : }
787 57168 : }
788 57168 : if (action == NEGATE || action == SECTION_NEGATE) {
789 34272 : values.push_back(prefix + "no" + name + "=1");
790 57168 : }
791 114336 : }
792 53424 : return values;
793 99216 : }
794 : };
795 :
796 : // Regression test covering different ways config settings can be merged. The
797 : // test parses and merges settings, representing the results as strings that get
798 : // compared against an expected hash. To debug, the result strings can be dumped
799 : // to a file (see comments below).
800 148 : BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
801 : {
802 1 : CHash256 out_sha;
803 1 : FILE* out_file = nullptr;
804 1 : if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) {
805 0 : out_file = fsbridge::fopen(out_path, "w");
806 0 : if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
807 0 : }
808 :
809 26713 : ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool soft_set, bool force_set,
810 : const std::string& section, const std::string& network, bool net_specific) {
811 26712 : TestArgsManager parser;
812 26712 : LOCK(parser.cs_args);
813 :
814 26712 : std::string desc = "net=";
815 26712 : desc += network;
816 26712 : parser.m_network = network;
817 :
818 26712 : const std::string& name = net_specific ? "wallet" : "server";
819 26712 : const std::string key = "-" + name;
820 26712 : parser.AddArg(key, name, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
821 26712 : if (net_specific) parser.SetNetworkOnlyArg(key);
822 :
823 26712 : auto args = GetValues(arg_actions, section, name, "a");
824 26712 : std::vector<const char*> argv = {"ignored"};
825 95400 : for (auto& arg : args) {
826 68688 : arg.insert(0, "-");
827 68688 : desc += " ";
828 68688 : desc += arg;
829 68688 : argv.push_back(arg.c_str());
830 : }
831 26712 : std::string error;
832 26712 : BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
833 26712 : BOOST_CHECK_EQUAL(error, "");
834 :
835 26712 : std::string conf;
836 129528 : for (auto& conf_val : GetValues(conf_actions, section, name, "c")) {
837 102816 : desc += " ";
838 102816 : desc += conf_val;
839 102816 : conf += conf_val;
840 102816 : conf += "\n";
841 : }
842 26712 : std::istringstream conf_stream(conf);
843 26712 : BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
844 26712 : BOOST_CHECK_EQUAL(error, "");
845 :
846 26712 : if (soft_set) {
847 13356 : desc += " soft";
848 13356 : parser.SoftSetArg(key, "soft1");
849 13356 : parser.SoftSetArg(key, "soft2");
850 13356 : }
851 :
852 26712 : if (force_set) {
853 13356 : desc += " force";
854 13356 : parser.ForceSetArg(key, "force1");
855 13356 : parser.ForceSetArg(key, "force2");
856 13356 : }
857 :
858 26712 : desc += " || ";
859 :
860 26712 : if (!parser.IsArgSet(key)) {
861 188 : desc += "unset";
862 188 : BOOST_CHECK(!parser.IsArgNegated(key));
863 188 : BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default");
864 188 : BOOST_CHECK(parser.GetArgs(key).empty());
865 26712 : } else if (parser.IsArgNegated(key)) {
866 6588 : desc += "negated";
867 6588 : BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0");
868 6588 : BOOST_CHECK(parser.GetArgs(key).empty());
869 6588 : } else {
870 19936 : desc += parser.GetArg(key, "default");
871 19936 : desc += " |";
872 64552 : for (const auto& arg : parser.GetArgs(key)) {
873 44616 : desc += " ";
874 44616 : desc += arg;
875 : }
876 : }
877 :
878 26712 : std::set<std::string> ignored = parser.GetUnsuitableSectionOnlyArgs();
879 26712 : if (!ignored.empty()) {
880 846 : desc += " | ignored";
881 1692 : for (const auto& arg : ignored) {
882 846 : desc += " ";
883 846 : desc += arg;
884 : }
885 846 : }
886 :
887 26712 : desc += "\n";
888 :
889 26712 : out_sha.Write(MakeUCharSpan(desc));
890 26712 : if (out_file) {
891 0 : BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
892 0 : }
893 26712 : });
894 :
895 1 : if (out_file) {
896 0 : if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
897 0 : out_file = nullptr;
898 0 : }
899 :
900 : unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
901 1 : out_sha.Finalize(out_sha_bytes);
902 1 : std::string out_sha_hex = HexStr(out_sha_bytes);
903 :
904 : // If check below fails, should manually dump the results with:
905 : //
906 : // ARGS_MERGE_TEST_OUT=results.txt ./test_dash --run_test=argsman_tests/util_ArgsMerge
907 : //
908 : // And verify diff against previous results to make sure the changes are expected.
909 : //
910 : // Results file is formatted like:
911 : //
912 : // <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
913 1 : BOOST_CHECK_EQUAL(out_sha_hex, "c78db4195fcbe4cf3f535f9a72b963bba77fd36099da29dfb8845291a2cf760a");
914 1 : }
915 :
916 : // Similar test as above, but for ArgsManager::GetChainName function.
917 1 : struct ChainMergeTestingSetup : public BasicTestingSetup {
918 : static constexpr int MAX_ACTIONS = 2;
919 :
920 : enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG };
921 : using ActionList = Action[MAX_ACTIONS];
922 :
923 : //! Enumerate all possible test configurations.
924 : template <typename Fn>
925 1 : void ForEachMergeSetup(Fn&& fn)
926 : {
927 1 : ActionList arg_actions = {};
928 38 : ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] {
929 37 : ActionList conf_actions = {};
930 1406 : ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); });
931 37 : });
932 1 : }
933 : };
934 :
935 148 : BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
936 : {
937 1 : CHash256 out_sha;
938 1 : FILE* out_file = nullptr;
939 1 : if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) {
940 0 : out_file = fsbridge::fopen(out_path, "w");
941 0 : if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
942 0 : }
943 :
944 1370 : ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
945 1369 : TestArgsManager parser;
946 1369 : LOCK(parser.cs_args);
947 1369 : parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
948 1369 : parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
949 :
950 11359 : auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
951 8362 : action == DISABLE_TEST ? "-testnet=0" :
952 6734 : action == NEGATE_TEST ? "-notestnet=1" :
953 5106 : action == ENABLE_REG ? "-regtest=1" :
954 2146 : action == DISABLE_REG ? "-regtest=0" :
955 1332 : action == NEGATE_REG ? "-noregtest=1" : nullptr; };
956 :
957 1369 : std::string desc;
958 1369 : std::vector<const char*> argv = {"ignored"};
959 3811 : for (Action action : arg_actions) {
960 2701 : const char* argstr = arg(action);
961 2701 : if (!argstr) break;
962 2442 : argv.push_back(argstr);
963 2442 : desc += " ";
964 2442 : desc += argv.back();
965 : }
966 1369 : std::string error;
967 1369 : BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
968 1369 : BOOST_CHECK_EQUAL(error, "");
969 :
970 1369 : std::string conf;
971 3811 : for (Action action : conf_actions) {
972 2701 : const char* argstr = arg(action);
973 2701 : if (!argstr) break;
974 2442 : desc += " ";
975 2442 : desc += argstr + 1;
976 2442 : conf += argstr + 1;
977 2442 : conf += "\n";
978 : }
979 1369 : std::istringstream conf_stream(conf);
980 1369 : BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
981 1369 : BOOST_CHECK_EQUAL(error, "");
982 :
983 1369 : desc += " || ";
984 : try {
985 1369 : desc += parser.GetChainName();
986 1369 : } catch (const std::runtime_error& e) {
987 182 : desc += "error: ";
988 182 : desc += e.what();
989 182 : }
990 1369 : desc += "\n";
991 :
992 1369 : out_sha.Write(MakeUCharSpan(desc));
993 1369 : if (out_file) {
994 0 : BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
995 0 : }
996 1551 : });
997 :
998 1 : if (out_file) {
999 0 : if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
1000 0 : out_file = nullptr;
1001 0 : }
1002 :
1003 : unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
1004 1 : out_sha.Finalize(out_sha_bytes);
1005 1 : std::string out_sha_hex = HexStr(out_sha_bytes);
1006 :
1007 : // If check below fails, should manually dump the results with:
1008 : //
1009 : // CHAIN_MERGE_TEST_OUT=results.txt ./test_dash --run_test=argsman_tests/util_ChainMerge
1010 : //
1011 : // And verify diff against previous results to make sure the changes are expected.
1012 : //
1013 : // Results file is formatted like:
1014 : //
1015 : // <input> || <output>
1016 1 : BOOST_CHECK_EQUAL(out_sha_hex, "1cd2697d1665d086f15ed18b3333bc356c3d39ba490589dc58afd8fc2f457731");
1017 1 : }
1018 :
1019 149 : BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)
1020 : {
1021 : // Test writing setting.
1022 1 : TestArgsManager args1;
1023 1 : args1.ForceSetArg("-datadir", fs::PathToString(m_path_root));
1024 2 : args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; });
1025 1 : args1.WriteSettingsFile();
1026 :
1027 : // Test reading setting.
1028 1 : TestArgsManager args2;
1029 1 : args2.ForceSetArg("-datadir", fs::PathToString(m_path_root));
1030 1 : args2.ReadSettingsFile();
1031 2 : args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); });
1032 :
1033 : // Test error logging, and remove previously written setting.
1034 : {
1035 1 : ASSERT_DEBUG_LOG("Failed renaming settings file");
1036 1 : fs::remove(args1.GetDataDirBase() / "settings.json");
1037 1 : fs::create_directory(args1.GetDataDirBase() / "settings.json");
1038 1 : args2.WriteSettingsFile();
1039 1 : fs::remove(args1.GetDataDirBase() / "settings.json");
1040 1 : }
1041 1 : }
1042 :
1043 146 : BOOST_AUTO_TEST_SUITE_END()
|