Skip to content

Commit 81137c6

Browse files
committed
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite instantiated its own duplicate ChainstateManager on which tests were performed. This wasn't a problem for the specific actions performed in that suite. However, the existence of this duplicate ChainstateManager and the fact that many of our validation static functions reach for g_chainman, ::Chain(state|)Active means we may end up acting on two different CChainStates should we write more extensive tests in the future. This change adds a new ChainTestingSetup which performs all initialization previously done by TestingSetup except: 1. RPC command registration 2. ChainState initialization 3. Genesis Activation 4. {Ban,Conn,Peer}Man initialization Means that we will no longer need to initialize a duplicate ChainstateManger in order to test the initialization codepaths of CChainState and ChainstateManager. Lastly, this change has the additional benefit of allowing for review-only assertions meant to show correctness to work in future work de-globalizing g_chainman. In the test chainstatemanager_rebalance_caches, an additional LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile], which is out of bounds when LoadGenesisBlock hasn't been called yet. ----- Note for the future: The class con/destructor inheritance structure we have for these TestingSetup classes is probably not the most suitable abstraction. In particular, for both TestingSetup and ChainTestingSetup, we need to stop the scheduler first before anything else. Otherwise classes depending on the scheduler may be referenced by the scheduler after said classes are freed. This means that there's no clear parallel between our teardown code and C++'s destructuring order for class hierarchies. Future work should strive to coalesce (as much as possible) test and non-test init codepaths and perhaps structure it in a more fail-proof way.
1 parent 7e9e7fe commit 81137c6

File tree

3 files changed

+56
-41
lines changed

3 files changed

+56
-41
lines changed

src/test/util/setup_common.cpp

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,12 @@ BasicTestingSetup::~BasicTestingSetup()
125125
ECC_Stop();
126126
}
127127

128-
TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
128+
ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
129129
: BasicTestingSetup(chainName, extra_args)
130130
{
131-
const CChainParams& chainparams = Params();
132-
// Ideally we'd move all the RPC tests to the functional testing framework
133-
// instead of unit tests, but for now we need these here.
134-
RegisterAllCoreRPCCommands(tableRPC);
135-
136-
m_node.scheduler = MakeUnique<CScheduler>();
137-
138131
// We have to run a scheduler thread to prevent ActivateBestChain
139132
// from blocking due to queue overrun.
133+
m_node.scheduler = MakeUnique<CScheduler>();
140134
threadGroup.create_thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); });
141135
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
142136

@@ -146,39 +140,16 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
146140
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
147141

148142
m_node.chainman = &::g_chainman;
149-
m_node.chainman->InitializeChainstate(*m_node.mempool);
150-
::ChainstateActive().InitCoinsDB(
151-
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
152-
assert(!::ChainstateActive().CanFlushToDisk());
153-
::ChainstateActive().InitCoinsCache(1 << 23);
154-
assert(::ChainstateActive().CanFlushToDisk());
155-
if (!LoadGenesisBlock(chainparams)) {
156-
throw std::runtime_error("LoadGenesisBlock failed.");
157-
}
158-
159-
BlockValidationState state;
160-
if (!ActivateBestChain(state, chainparams)) {
161-
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
162-
}
163143

164144
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
165145
constexpr int script_check_threads = 2;
166146
for (int i = 0; i < script_check_threads; ++i) {
167147
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
168148
}
169149
g_parallel_script_checks = true;
170-
171-
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
172-
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
173-
m_node.peerman = MakeUnique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
174-
{
175-
CConnman::Options options;
176-
options.m_msgproc = m_node.peerman.get();
177-
m_node.connman->Init(options);
178-
}
179150
}
180151

181-
TestingSetup::~TestingSetup()
152+
ChainTestingSetup::~ChainTestingSetup()
182153
{
183154
if (m_node.scheduler) m_node.scheduler->stop();
184155
threadGroup.interrupt_all();
@@ -196,6 +167,39 @@ TestingSetup::~TestingSetup()
196167
pblocktree.reset();
197168
}
198169

170+
TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
171+
: ChainTestingSetup(chainName, extra_args)
172+
{
173+
const CChainParams& chainparams = Params();
174+
// Ideally we'd move all the RPC tests to the functional testing framework
175+
// instead of unit tests, but for now we need these here.
176+
RegisterAllCoreRPCCommands(tableRPC);
177+
178+
m_node.chainman->InitializeChainstate(*m_node.mempool);
179+
::ChainstateActive().InitCoinsDB(
180+
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
181+
assert(!::ChainstateActive().CanFlushToDisk());
182+
::ChainstateActive().InitCoinsCache(1 << 23);
183+
assert(::ChainstateActive().CanFlushToDisk());
184+
if (!LoadGenesisBlock(chainparams)) {
185+
throw std::runtime_error("LoadGenesisBlock failed.");
186+
}
187+
188+
BlockValidationState state;
189+
if (!ActivateBestChain(state, chainparams)) {
190+
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
191+
}
192+
193+
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
194+
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
195+
m_node.peerman = MakeUnique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
196+
{
197+
CConnman::Options options;
198+
options.m_msgproc = m_node.peerman.get();
199+
m_node.connman->Init(options);
200+
}
201+
}
202+
199203
TestChain100Setup::TestChain100Setup()
200204
{
201205
// Generate a 100-block chain:

src/test/util/setup_common.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,21 @@ struct BasicTestingSetup {
8383
const fs::path m_path_root;
8484
};
8585

86-
/** Testing setup that configures a complete environment.
87-
* Included are coins database, script check threads setup.
86+
/** Testing setup that performs all steps up until right before
87+
* ChainstateManager gets initialized. Meant for testing ChainstateManager
88+
* initialization behaviour.
8889
*/
89-
struct TestingSetup : public BasicTestingSetup {
90+
struct ChainTestingSetup : public BasicTestingSetup {
9091
boost::thread_group threadGroup;
9192

93+
explicit ChainTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
94+
~ChainTestingSetup();
95+
};
96+
97+
/** Testing setup that configures a complete environment.
98+
*/
99+
struct TestingSetup : public ChainTestingSetup {
92100
explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
93-
~TestingSetup();
94101
};
95102

96103
/** Identical to TestingSetup, but chain set to regtest */

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@
1515

1616
#include <boost/test/unit_test.hpp>
1717

18-
BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
18+
BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup)
1919

2020
//! Basic tests for ChainstateManager.
2121
//!
2222
//! First create a legacy (IBD) chainstate, then create a snapshot chainstate.
2323
BOOST_AUTO_TEST_CASE(chainstatemanager)
2424
{
25-
ChainstateManager manager;
26-
CTxMemPool mempool;
25+
ChainstateManager& manager = *m_node.chainman;
26+
CTxMemPool& mempool = *m_node.mempool;
27+
2728
std::vector<CChainState*> chainstates;
2829
const CChainParams& chainparams = Params();
2930

@@ -104,8 +105,9 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
104105
//! Test rebalancing the caches associated with each chainstate.
105106
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
106107
{
107-
ChainstateManager manager;
108-
CTxMemPool mempool;
108+
ChainstateManager& manager = *m_node.chainman;
109+
CTxMemPool& mempool = *m_node.mempool;
110+
109111
size_t max_cache = 10000;
110112
manager.m_total_coinsdb_cache = max_cache;
111113
manager.m_total_coinstip_cache = max_cache;
@@ -122,6 +124,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
122124
{
123125
LOCK(::cs_main);
124126
c1.InitCoinsCache(1 << 23);
127+
BOOST_REQUIRE(c1.LoadGenesisBlock(Params()));
125128
c1.CoinsTip().SetBestBlock(InsecureRand256());
126129
manager.MaybeRebalanceCaches();
127130
}
@@ -139,6 +142,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
139142
{
140143
LOCK(::cs_main);
141144
c2.InitCoinsCache(1 << 23);
145+
BOOST_REQUIRE(c2.LoadGenesisBlock(Params()));
142146
c2.CoinsTip().SetBestBlock(InsecureRand256());
143147
manager.MaybeRebalanceCaches();
144148
}

0 commit comments

Comments
 (0)