Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 7: [PATCH 07/17] feat(rpc): preserve Roots operator interfaces on v31

src/test/getarg_tests.cpp

Before: src/test/getarg_tests.cpp · After: src/test/getarg_tests.cpp

File metadata
index d349ceea44b6bae8d7ce76bcb2d866394112bfcc..f2c9df2701a2ce614b2c5b727cdd7f9a5537a1b9 100644

Download original patch · Release notes

Diff rows 1–227 of 227. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

src/test/getarg_tests.cpp CHANGED
@@ -10,7 +10,10 @@
10
10
  #include <univalue.h>
11
11
  #include <util/strencodings.h>
12
12
 
13
+ #include <fstream>
13
14
  #include <limits>
15
+ #include <sstream>
16
+ #include <streambuf>
14
17
  #include <string>
15
18
  #include <utility>
16
19
  #include <vector>
@@ -48,6 +51,43 @@ void SetupArgs(ArgsManager& local_args, const std::vector<std::pair<std::string,
48
51
  }
49
52
  }
50
53
 
54
+ namespace {
55
+ // Report a real I/O failure after a chosen number of bytes, optionally only
56
+ // after the formatter rewinds to copy an invalid file into comments.
57
+ class FailingConfigBuffer : public std::streambuf {
58
+ std::string m_text;
59
+ size_t m_position{0};
60
+ size_t m_fail_at;
61
+ bool m_fail_after_rewind;
62
+ bool m_rewound{false};
63
+
64
+ int_type underflow() override
65
+ {
66
+ if (m_position == m_fail_at && (!m_fail_after_rewind || m_rewound)) {
67
+ throw std::ios_base::failure("Injected config read failure");
68
+ }
69
+ if (m_position == m_text.size()) return traits_type::eof();
70
+ return traits_type::to_int_type(m_text[m_position]);
71
+ }
72
+ int_type uflow() override
73
+ {
74
+ const auto value{underflow()};
75
+ if (!traits_type::eq_int_type(value, traits_type::eof())) ++m_position;
76
+ return value;
77
+ }
78
+ pos_type seekoff(off_type offset, std::ios_base::seekdir direction, std::ios_base::openmode) override
79
+ {
80
+ if (direction != std::ios_base::beg || offset != 0) return pos_type{off_type{-1}};
81
+ m_position = 0;
82
+ m_rewound = true;
83
+ return pos_type{0};
84
+ }
85
+ public:
86
+ FailingConfigBuffer(std::string text, size_t fail_at, bool fail_after_rewind = false)
87
+ : m_text(std::move(text)), m_fail_at(fail_at), m_fail_after_rewind(fail_after_rewind) {}
88
+ };
89
+ } // namespace
90
+
51
91
  // Test behavior of GetArg functions when string, integer, and boolean types
52
92
  // are specified in the settings.json file. GetArg functions are convenience
53
93
  // functions. The GetSetting method can always be used instead of GetArg
@@ -105,22 +145,22 @@ BOOST_AUTO_TEST_CASE(setting_args)
105
145
  BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "99");
106
146
  BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "99");
107
147
  BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 99);
108
- BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
109
- BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
148
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
149
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
110
150
 
111
151
  set_foo(3.25);
112
152
  BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "3.25");
113
153
  BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "3.25");
114
154
  BOOST_CHECK_THROW(args.GetIntArg("foo", 100), std::runtime_error);
115
- BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
116
- BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
155
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
156
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
117
157
 
118
158
  set_foo(0);
119
159
  BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "0");
120
160
  BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "0");
121
161
  BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
122
- BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
123
- BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
162
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), false);
163
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), false);
124
164
 
125
165
  set_foo(true);
126
166
  BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "true");
@@ -458,6 +498,9 @@ BOOST_AUTO_TEST_CASE(logargs)
458
498
  str += s;
459
499
  });
460
500
 
501
+ local_args.LockSettings([](common::Settings& settings) {
502
+ settings.rw_config["dontlog"] = {"private-rw42"};
503
+ });
461
504
  // Log the arguments
462
505
  local_args.LogArgs();
463
506
 
@@ -468,6 +511,137 @@ BOOST_AUTO_TEST_CASE(logargs)
468
511
  BOOST_CHECK(str.find("Command-line arg: okaylog=\"public\"") != std::string::npos);
469
512
  BOOST_CHECK(str.find("dontlog=****") != std::string::npos);
470
513
  BOOST_CHECK(str.find("private42") == std::string::npos);
514
+ BOOST_CHECK(str.find("private-rw42") == std::string::npos);
515
+ }
516
+
517
+ BOOST_AUTO_TEST_CASE(rw_config_stream)
518
+ {
519
+ std::istringstream input{"# operator settings\nprune=5\n[regtest]\nprune=1\n"};
520
+ std::ostringstream output;
521
+ ModifyRWConfigStream(input, output, {{"listen", "0"}, {"prune", "550"}});
522
+ BOOST_CHECK_EQUAL(output.str(), "# operator settings\nprune=550\nlisten=0\n[regtest]\nprune=1\n");
523
+
524
+ std::istringstream empty;
525
+ std::ostringstream rejected;
526
+ BOOST_CHECK_THROW(ModifyRWConfigStream(empty, rejected, {{"listen", "0\nprune=0"}}), std::invalid_argument);
527
+ }
528
+
529
+ BOOST_AUTO_TEST_CASE(config_read_failures)
530
+ {
531
+ class ReadArgs : public ArgsManager {
532
+ public:
533
+ using ArgsManager::ReadConfigStream;
534
+ };
535
+ const std::string text{"foo=original\nbar=tail"};
536
+ for (const size_t fail_at : {size_t{0}, size_t{4}, text.size() - 1}) {
537
+ FailingConfigBuffer buffer{text, fail_at};
538
+ std::istream input{&buffer};
539
+ std::ostringstream output;
540
+ BOOST_CHECK_THROW(ModifyRWConfigStream(input, output, {{"foo", "new"}}), std::ios_base::failure);
541
+ BOOST_CHECK(input.bad());
542
+
543
+ FailingConfigBuffer parser_buffer{text, fail_at};
544
+ std::istream parser_input{&parser_buffer};
545
+ ReadArgs args;
546
+ SetupArgs(args, {{"-foo", ArgsManager::ALLOW_ANY}, {"-bar", ArgsManager::ALLOW_ANY}});
547
+ std::map<std::string, std::vector<common::SettingsValue>> target{{"foo", {"kept"}}};
548
+ std::string error;
549
+ BOOST_CHECK(!args.ReadConfigStream(parser_input, "fault.conf", error, false, &target));
550
+ BOOST_CHECK_EQUAL(error, "Failed to read config file \"fault.conf\".");
551
+ BOOST_CHECK_EQUAL(target.size(), 1U);
552
+ BOOST_CHECK_EQUAL(target.at("foo").front().get_str(), "kept");
553
+ BOOST_CHECK(!args.IsArgSet("-foo"));
554
+ }
555
+ FailingConfigBuffer copy_buffer{"invalid\nfoo=old", 12, /*fail_after_rewind=*/true};
556
+ std::istream copy_input{&copy_buffer};
557
+ std::ostringstream copy_output;
558
+ BOOST_CHECK_THROW(ModifyRWConfigStream(copy_input, copy_output, {{"foo", "new"}}), std::ios_base::failure);
559
+ BOOST_CHECK(copy_input.bad());
560
+
561
+ std::istringstream failed_input{"foo=old"};
562
+ failed_input.setstate(std::ios::failbit);
563
+ std::ostringstream failed_output;
564
+ BOOST_CHECK_THROW(ModifyRWConfigStream(failed_input, failed_output, {{"foo", "new"}}), std::ios_base::failure);
565
+
566
+ for (const std::string original : {"", "foo=old", "foo=old\n"}) {
567
+ std::istringstream input{original};
568
+ std::ostringstream output;
569
+ ModifyRWConfigStream(input, output, {{"foo", "new"}});
570
+ BOOST_CHECK_EQUAL(output.str(), original.empty() || original.ends_with('\n') ? "foo=new\n" : "foo=new");
571
+ }
572
+ std::istringstream invalid_final{"invalid"};
573
+ std::ostringstream repaired;
574
+ ModifyRWConfigStream(invalid_final, repaired, {{"foo", "new"}});
575
+ BOOST_CHECK_EQUAL(repaired.str(), "foo=new\n[INVALID]\n# Error parsing line 1: invalid\n#invalid");
576
+ }
577
+
578
+ BOOST_AUTO_TEST_CASE(rw_config_precedence)
579
+ {
580
+ ArgsManager args;
581
+ SetupArgs(args, {{"-foo", ArgsManager::ALLOW_ANY}});
582
+ args.SelectConfigNetwork("regtest");
583
+ args.LockSettings([](common::Settings& settings) {
584
+ settings.ro_config[""]["foo"] = {"default"};
585
+ settings.ro_config["regtest"]["foo"] = {"network"};
586
+ settings.rw_settings["foo"] = "json";
587
+ settings.rw_config["foo"] = {"rw-first", "rw-second"};
588
+ });
589
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "rw-first");
590
+ ResetArgs(args, "-foo=cli-first -foo=cli-last");
591
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "cli-last");
592
+ args.ForceSetArg("-foo", "forced");
593
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "forced");
594
+ args.LockSettings([](common::Settings& settings) {
595
+ settings.forced_settings.clear();
596
+ settings.command_line_options.clear();
597
+ settings.rw_config["foo"] = {false, "after-negation"};
598
+ });
599
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "after-negation");
600
+ args.LockSettings([](common::Settings& settings) { settings.rw_config.clear(); });
601
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "json");
602
+ args.LockSettings([](common::Settings& settings) { settings.rw_settings.clear(); });
603
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "network");
604
+ }
605
+
606
+ BOOST_AUTO_TEST_CASE(rw_config_file_failures)
607
+ {
608
+ class RWArgs : public ArgsManager {
609
+ public:
610
+ explicit RWArgs(const fs::path& path) { LOCK(cs_args); m_rwconf_path = path; }
611
+ };
612
+ const auto path{m_args.GetDataDirBase() / "rw_config_test.conf"};
613
+ RWArgs args{path};
614
+ SetupArgs(args, {{"-foo", ArgsManager::ALLOW_ANY}});
615
+ args.ModifyRWConfigFile("foo", "original", /*also_settings_json=*/false);
616
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "original");
617
+ BOOST_CHECK_THROW(args.ModifyRWConfigFile("foo", "bad\nvalue", false), std::invalid_argument);
618
+ BOOST_CHECK_EQUAL(args.GetArg("-foo", "unset"), "original");
619
+ std::ifstream input{path.std_path()};
620
+ std::string line;
621
+ std::getline(input, line);
622
+ BOOST_CHECK_EQUAL(line, "foo=original");
623
+ input.close();
624
+ args.EraseRWConfigFile();
625
+ BOOST_CHECK(!fs::exists(path));
626
+ auto reset_path{path};
627
+ reset_path += ".reset";
628
+ BOOST_CHECK(fs::exists(reset_path));
629
+
630
+ const auto directory{m_args.GetDataDirBase() / "rw_config_unreadable"};
631
+ fs::create_directory(directory);
632
+ RWArgs unreadable{directory};
633
+ SetupArgs(unreadable, {{"-foo", ArgsManager::ALLOW_ANY}});
634
+ unreadable.LockSettings([](common::Settings& settings) { settings.rw_config["foo"] = {"kept"}; });
635
+ BOOST_CHECK_THROW(unreadable.ModifyRWConfigFile("foo", "new", false), std::ios_base::failure);
636
+ BOOST_CHECK(fs::is_directory(directory));
637
+ BOOST_CHECK_EQUAL(unreadable.GetArg("-foo", "unset"), "kept");
638
+ auto temporary_path{directory};
639
+ temporary_path += ".new";
640
+ BOOST_CHECK(!fs::exists(temporary_path));
641
+
642
+ RWArgs missing{path / "missing" / "config"};
643
+ BOOST_CHECK_THROW(missing.ModifyRWConfigFile("foo", "new", false), std::ios_base::failure);
644
+ BOOST_CHECK(!fs::exists(missing.GetRWConfigFilePath()));
471
645
  }
472
646
 
473
647
  BOOST_AUTO_TEST_SUITE_END()