Skip to content

Commit 6f4ba64

Browse files
author
MarcoFalke
committed
Merge #15988: Add test for ArgsManager::GetChainName
f6bb11f Add test for ArgsManager::GetChainName (Russell Yanofsky) 4b33115 Add unit test NextString, ForEachNoDup functions (Russell Yanofsky) 05bfee3 util_SettingsMerge test cleanup (Russell Yanofsky) Pull request description: There was some test coverage previously, but it was limited and didn't test conflicting and negated arguments. ACKs for commit f6bb11: MarcoFalke: re-utACK f6bb11f Tree-SHA512: d03596614dc48584c7a9440117b107c6abb23fd4c7fa15fb4015351ec3de08b2656bc956ce05310663675672343d7a6aff35421657f29172080c7005045680b0
2 parents 40c66bb + f6bb11f commit 6f4ba64

File tree

2 files changed

+149
-42
lines changed

2 files changed

+149
-42
lines changed

src/test/util.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,37 @@ std::string getnewaddress(CWallet& w);
3434
/** Returns the generated coin */
3535
CTxIn generatetoaddress(const std::string& address);
3636

37+
/**
38+
* Increment a string. Useful to enumerate all fixed length strings with
39+
* characters in [min_char, max_char].
40+
*/
41+
template <typename CharType, size_t StringLength>
42+
bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char)
43+
{
44+
for (CharType& elem : string) {
45+
bool has_next = elem != max_char;
46+
elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1);
47+
if (has_next) return true;
48+
}
49+
return false;
50+
}
51+
52+
/**
53+
* Iterate over string values and call function for each string without
54+
* successive duplicate characters.
55+
*/
56+
template <typename CharType, size_t StringLength, typename Fn>
57+
void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn&& fn) {
58+
for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) {
59+
int prev = -1;
60+
bool skip_string = false;
61+
for (CharType c : string) {
62+
if (c == prev) skip_string = true;
63+
if (skip_string || c < min_char || c > max_char) break;
64+
prev = c;
65+
}
66+
if (!skip_string) fn();
67+
}
68+
}
3769

3870
#endif // BITCOIN_TEST_UTIL_H

src/test/util_tests.cpp

Lines changed: 117 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <clientversion.h>
88
#include <primitives/transaction.h>
99
#include <sync.h>
10+
#include <test/util.h>
1011
#include <util/strencodings.h>
1112
#include <util/moneystr.h>
1213
#include <test/setup_common.h>
@@ -580,7 +581,7 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
580581

581582
// Test different ways settings can be merged, and verify results. This test can
582583
// be used to confirm that updates to settings code don't change behavior
583-
// unintentially.
584+
// unintentionally.
584585
//
585586
// The test covers:
586587
//
@@ -600,20 +601,22 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
600601
// outside a network section, and non-network specific settings like "-server"
601602
// that aren't sensitive to the network.
602603
//
603-
struct SettingsMergeTestingSetup : public BasicTestingSetup {
604+
struct ArgsMergeTestingSetup : public BasicTestingSetup {
604605
//! Max number of actions to sequence together. Can decrease this when
605606
//! debugging to make test results easier to understand.
606607
static constexpr int MAX_ACTIONS = 3;
607608

608-
enum Action { SET = 0, NEGATE, SECTION_SET, SECTION_NEGATE, END };
609+
enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
609610
using ActionList = Action[MAX_ACTIONS];
610611

611612
//! Enumerate all possible test configurations.
612613
template <typename Fn>
613614
void ForEachMergeSetup(Fn&& fn)
614615
{
615-
ForEachActionList([&](const ActionList& arg_actions) {
616-
ForEachActionList([&](const ActionList& conf_actions) {
616+
ActionList arg_actions = {};
617+
ForEachNoDup(arg_actions, SET, SECTION_NEGATE, [&] {
618+
ActionList conf_actions = {};
619+
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
617620
for (bool soft_set : {false, true}) {
618621
for (bool force_set : {false, true}) {
619622
for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
@@ -629,36 +632,6 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
629632
});
630633
}
631634

632-
//! Enumerate interesting combinations of actions.
633-
template <typename Fn>
634-
void ForEachActionList(Fn&& fn)
635-
{
636-
ActionList actions = {SET};
637-
for (bool done = false; !done;) {
638-
int prev_action = -1;
639-
bool skip_actions = false;
640-
for (Action action : actions) {
641-
if ((prev_action == END && action != END) || (prev_action != END && action == prev_action)) {
642-
// To cut down list of enumerated settings, skip enumerating
643-
// settings with ignored actions after an END, and settings that
644-
// repeat the same action twice in a row.
645-
skip_actions = true;
646-
break;
647-
}
648-
prev_action = action;
649-
}
650-
if (!skip_actions) fn(actions);
651-
done = true;
652-
for (Action& action : actions) {
653-
action = Action(action < END ? action + 1 : 0);
654-
if (action) {
655-
done = false;
656-
break;
657-
}
658-
}
659-
}
660-
}
661-
662635
//! Translate actions into a list of <key>=<value> setting strings.
663636
std::vector<std::string> GetValues(const ActionList& actions,
664637
const std::string& section,
@@ -668,7 +641,7 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
668641
std::vector<std::string> values;
669642
int suffix = 0;
670643
for (Action action : actions) {
671-
if (action == END) break;
644+
if (action == NONE) break;
672645
std::string prefix;
673646
if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + ".";
674647
if (action == SET || action == SECTION_SET) {
@@ -687,12 +660,12 @@ struct SettingsMergeTestingSetup : public BasicTestingSetup {
687660
// Regression test covering different ways config settings can be merged. The
688661
// test parses and merges settings, representing the results as strings that get
689662
// compared against an expected hash. To debug, the result strings can be dumped
690-
// to a file (see below).
691-
BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
663+
// to a file (see comments below).
664+
BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
692665
{
693666
CHash256 out_sha;
694667
FILE* out_file = nullptr;
695-
if (const char* out_path = getenv("SETTINGS_MERGE_TEST_OUT")) {
668+
if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) {
696669
out_file = fsbridge::fopen(out_path, "w");
697670
if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
698671
}
@@ -706,7 +679,7 @@ BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
706679
desc += network;
707680
parser.m_network = network;
708681

709-
const std::string& name = net_specific ? "server" : "wallet";
682+
const std::string& name = net_specific ? "wallet" : "server";
710683
const std::string key = "-" + name;
711684
parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
712685
if (net_specific) parser.SetNetworkOnlyArg(key);
@@ -794,14 +767,116 @@ BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
794767

795768
// If check below fails, should manually dump the results with:
796769
//
797-
// SETTINGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_SettingsMerge
770+
// ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge
798771
//
799772
// And verify diff against previous results to make sure the changes are expected.
800773
//
801774
// Results file is formatted like:
802775
//
803776
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
804-
BOOST_CHECK_EQUAL(out_sha_hex, "80964e17fbd3c5569d3c824d032e28e2d319ef57494735b0e76eb7aad9957f2c");
777+
BOOST_CHECK_EQUAL(out_sha_hex, "b835eef5977d69114eb039a976201f8c7121f34fe2b7ea2b73cafb516e5c9dc8");
778+
}
779+
780+
// Similar test as above, but for ArgsManager::GetChainName function.
781+
struct ChainMergeTestingSetup : public BasicTestingSetup {
782+
static constexpr int MAX_ACTIONS = 2;
783+
784+
enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG };
785+
using ActionList = Action[MAX_ACTIONS];
786+
787+
//! Enumerate all possible test configurations.
788+
template <typename Fn>
789+
void ForEachMergeSetup(Fn&& fn)
790+
{
791+
ActionList arg_actions = {};
792+
ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] {
793+
ActionList conf_actions = {};
794+
ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); });
795+
});
796+
}
797+
};
798+
799+
BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
800+
{
801+
CHash256 out_sha;
802+
FILE* out_file = nullptr;
803+
if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) {
804+
out_file = fsbridge::fopen(out_path, "w");
805+
if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
806+
}
807+
808+
ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
809+
TestArgsManager parser;
810+
LOCK(parser.cs_args);
811+
parser.AddArg("-regtest", "regtest", false, OptionsCategory::OPTIONS);
812+
parser.AddArg("-testnet", "testnet", false, OptionsCategory::OPTIONS);
813+
814+
auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
815+
action == DISABLE_TEST ? "-testnet=0" :
816+
action == NEGATE_TEST ? "-notestnet=1" :
817+
action == ENABLE_REG ? "-regtest=1" :
818+
action == DISABLE_REG ? "-regtest=0" :
819+
action == NEGATE_REG ? "-noregtest=1" : nullptr; };
820+
821+
std::string desc;
822+
std::vector<const char*> argv = {"ignored"};
823+
for (Action action : arg_actions) {
824+
const char* argstr = arg(action);
825+
if (!argstr) break;
826+
argv.push_back(argstr);
827+
desc += " ";
828+
desc += argv.back();
829+
}
830+
std::string error;
831+
BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
832+
BOOST_CHECK_EQUAL(error, "");
833+
834+
std::string conf;
835+
for (Action action : conf_actions) {
836+
const char* argstr = arg(action);
837+
if (!argstr) break;
838+
desc += " ";
839+
desc += argstr + 1;
840+
conf += argstr + 1;
841+
}
842+
std::istringstream conf_stream(conf);
843+
BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
844+
BOOST_CHECK_EQUAL(error, "");
845+
846+
desc += " || ";
847+
try {
848+
desc += parser.GetChainName();
849+
} catch (const std::runtime_error& e) {
850+
desc += "error: ";
851+
desc += e.what();
852+
}
853+
desc += "\n";
854+
855+
out_sha.Write((const unsigned char*)desc.data(), desc.size());
856+
if (out_file) {
857+
BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
858+
}
859+
});
860+
861+
if (out_file) {
862+
if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
863+
out_file = nullptr;
864+
}
865+
866+
unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
867+
out_sha.Finalize(out_sha_bytes);
868+
std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
869+
870+
// If check below fails, should manually dump the results with:
871+
//
872+
// CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge
873+
//
874+
// And verify diff against previous results to make sure the changes are expected.
875+
//
876+
// Results file is formatted like:
877+
//
878+
// <input> || <output>
879+
BOOST_CHECK_EQUAL(out_sha_hex, "b284f4b4a15dd6bf8c06213a69a004b1960388e1d9917173927db52ac220927f");
805880
}
806881

807882
BOOST_AUTO_TEST_CASE(util_FormatMoney)

0 commit comments

Comments
 (0)