hmmm, we're repeating the same steps but with different data?
Can we separate the data from the algorithm and do the iteration in something like:
for (size_t max_mempool_size_bytes : {size_t{0}, MAX_MEMPOOL_CACHE_BYTES}) {
// The steps from OK to CRITICAL
}
<details>
<summary>For simplicity, here's the full `getcoinscachesizestate` test</summary>
// Copyright (c) 2019-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
#include <test/util/coins.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
//! Verify that Chainstate::GetCoinsCacheSizeState() switches from OK→LARGE→CRITICAL
//! at the expected utilization thresholds, first with *no* mempool head-room,
//! then with additional mempool head-room.
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
Chainstate& chainstate{m_node.chainman->ActiveChainstate()};
LOCK(::cs_main);
CCoinsViewCache& view = chainstate.CoinsTip();
BOOST_CHECK_LT(view.DynamicMemoryUsage() / (256 * 1024.0), 1.1);
constexpr size_t MAX_COINS_CACHE_BYTES{8'000'000}; // ~8 MB cache size for the test
constexpr size_t MAX_MEMPOOL_CACHE_BYTES{4'000'000}; // ~4 MB extra head-room
constexpr size_t MAX_ATTEMPTS{50'000}; // runaway-loop safety cap
// Run the same growth-path twice: first with 0 head-room, then with extra head-room
for (size_t max_mempool_size_bytes : {size_t{0}, MAX_MEMPOOL_CACHE_BYTES}) {
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, max_mempool_size_bytes), CoinsCacheSizeState::OK);
const size_t full_cap = MAX_COINS_CACHE_BYTES + max_mempool_size_bytes;
// OK → LARGE
for (size_t i{1}; i <= MAX_ATTEMPTS; ++i) {
AddTestCoin(m_rng, view);
auto state{chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, max_mempool_size_bytes)};
if (i == MAX_ATTEMPTS || view.DynamicMemoryUsage() >= full_cap * 90 / 100) {
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::LARGE);
break;
}
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::OK);
}
// LARGE → CRITICAL
for (size_t i{1}; i <= MAX_ATTEMPTS; ++i) {
AddTestCoin(m_rng, view);
auto state{chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, max_mempool_size_bytes)};
if (i == MAX_ATTEMPTS || view.DynamicMemoryUsage() > full_cap) {
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::CRITICAL);
break;
}
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::LARGE);
}
}
for (int i{0}; i < 1'000; ++i) {
AddTestCoin(m_rng, view);
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(), CoinsCacheSizeState::OK);
}
// CRITICAL → OK via Flush
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*mempool=*/0), CoinsCacheSizeState::CRITICAL);
view.SetBestBlock(m_rng.rand256());
BOOST_CHECK(view.Flush());
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*mempool=*/0), CoinsCacheSizeState::OK);
}
BOOST_AUTO_TEST_SUITE_END()
</details>