Merge #9308: [test] Add CCoinsViewCache Access/Modify/Write tests

07df40b [test] Add CCoinsViewCache Access/Modify/Write tests (Russell Yanofsky)
This commit is contained in:
Wladimir J. van der Laan 2016-12-21 12:16:18 +01:00
commit 0698639a38
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6

View File

@ -80,6 +80,8 @@ public:
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
}
CCoinsMap& map() { return cacheCoins; }
size_t& usage() { return cachedCoinsUsage; }
};
}
@ -415,4 +417,366 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
}
}
const static uint256 TXID;
const static CAmount PRUNED = -1;
const static CAmount ABSENT = -2;
const static CAmount VALUE1 = 100;
const static CAmount VALUE2 = 200;
const static CAmount VALUE3 = 300;
const static char DIRTY = CCoinsCacheEntry::DIRTY;
const static char FRESH = CCoinsCacheEntry::FRESH;
const static char NO_ENTRY = -1;
const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
const static auto CLEAN_FLAGS = {char(0), FRESH};
const static auto DIRTY_FLAGS = {DIRTY, char(DIRTY | FRESH)};
const static auto ABSENT_FLAGS = {NO_ENTRY};
void SetCoinsValue(CAmount value, CCoins& coins)
{
assert(value != ABSENT);
coins.Clear();
assert(coins.IsPruned());
if (value != PRUNED) {
coins.vout.emplace_back();
coins.vout.back().nValue = value;
assert(!coins.IsPruned());
}
}
size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
{
if (value == ABSENT) {
assert(flags == NO_ENTRY);
return 0;
}
assert(flags != NO_ENTRY);
CCoinsCacheEntry entry;
entry.flags = flags;
SetCoinsValue(value, entry.coins);
auto inserted = map.emplace(TXID, std::move(entry));
assert(inserted.second);
return inserted.first->second.coins.DynamicMemoryUsage();
}
void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
{
auto it = map.find(TXID);
if (it == map.end()) {
value = ABSENT;
flags = NO_ENTRY;
} else {
if (it->second.coins.IsPruned()) {
assert(it->second.coins.vout.size() == 0);
value = PRUNED;
} else {
assert(it->second.coins.vout.size() == 1);
value = it->second.coins.vout[0].nValue;
}
flags = it->second.flags;
assert(flags != NO_ENTRY);
}
}
void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{
CCoinsMap map;
InsertCoinsMapEntry(map, value, flags);
view.BatchWrite(map, {});
}
class SingleEntryCacheTest
{
public:
SingleEntryCacheTest(CAmount base_value, CAmount cache_value, char cache_flags)
{
WriteCoinsViewEntry(base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
cache.usage() += InsertCoinsMapEntry(cache.map(), cache_value, cache_flags);
}
CCoinsView root;
CCoinsViewCacheTest base{&root};
CCoinsViewCacheTest cache{&base};
};
void CheckAccessCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
test.cache.AccessCoins(TXID);
test.cache.SelfTest();
CAmount result_value;
char result_flags;
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
BOOST_CHECK_EQUAL(result_value, expected_value);
BOOST_CHECK_EQUAL(result_flags, expected_flags);
}
BOOST_AUTO_TEST_CASE(ccoins_access)
{
/* Check AccessCoin behavior, requesting a coin from a cache view layered on
* top of a base view, and checking the resulting entry in the cache after
* the access.
*
* Base Cache Result Cache Result
* Value Value Value Flags Flags
*/
CheckAccessCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
CheckAccessCoins(ABSENT, PRUNED, PRUNED, 0 , 0 );
CheckAccessCoins(ABSENT, PRUNED, PRUNED, FRESH , FRESH );
CheckAccessCoins(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
CheckAccessCoins(ABSENT, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
CheckAccessCoins(ABSENT, VALUE2, VALUE2, 0 , 0 );
CheckAccessCoins(ABSENT, VALUE2, VALUE2, FRESH , FRESH );
CheckAccessCoins(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY );
CheckAccessCoins(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
CheckAccessCoins(PRUNED, ABSENT, PRUNED, NO_ENTRY , FRESH );
CheckAccessCoins(PRUNED, PRUNED, PRUNED, 0 , 0 );
CheckAccessCoins(PRUNED, PRUNED, PRUNED, FRESH , FRESH );
CheckAccessCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
CheckAccessCoins(PRUNED, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
CheckAccessCoins(PRUNED, VALUE2, VALUE2, 0 , 0 );
CheckAccessCoins(PRUNED, VALUE2, VALUE2, FRESH , FRESH );
CheckAccessCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY );
CheckAccessCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
CheckAccessCoins(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 );
CheckAccessCoins(VALUE1, PRUNED, PRUNED, 0 , 0 );
CheckAccessCoins(VALUE1, PRUNED, PRUNED, FRESH , FRESH );
CheckAccessCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
CheckAccessCoins(VALUE1, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
CheckAccessCoins(VALUE1, VALUE2, VALUE2, 0 , 0 );
CheckAccessCoins(VALUE1, VALUE2, VALUE2, FRESH , FRESH );
CheckAccessCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY );
CheckAccessCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
}
void CheckModifyCoins(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
SetCoinsValue(modify_value, *test.cache.ModifyCoins(TXID));
test.cache.SelfTest();
CAmount result_value;
char result_flags;
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
BOOST_CHECK_EQUAL(result_value, expected_value);
BOOST_CHECK_EQUAL(result_flags, expected_flags);
};
BOOST_AUTO_TEST_CASE(ccoins_modify)
{
/* Check ModifyCoin behavior, requesting a coin from a cache view layered on
* top of a base view, writing a modification to the coin, and then checking
* the resulting entry in the cache after the modification.
*
* Base Cache Write Result Cache Result
* Value Value Value Value Flags Flags
*/
CheckModifyCoins(ABSENT, ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY );
CheckModifyCoins(ABSENT, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH);
CheckModifyCoins(ABSENT, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(ABSENT, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(ABSENT, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(ABSENT, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(ABSENT, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
CheckModifyCoins(ABSENT, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(ABSENT, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(ABSENT, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(ABSENT, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(ABSENT, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
CheckModifyCoins(PRUNED, ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY );
CheckModifyCoins(PRUNED, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH);
CheckModifyCoins(PRUNED, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(PRUNED, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(PRUNED, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(PRUNED, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(PRUNED, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
CheckModifyCoins(PRUNED, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(PRUNED, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(PRUNED, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(PRUNED, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(PRUNED, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
CheckModifyCoins(VALUE1, ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY );
CheckModifyCoins(VALUE1, ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY );
CheckModifyCoins(VALUE1, PRUNED, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(VALUE1, PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(VALUE1, PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(VALUE1, PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(VALUE1, PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
CheckModifyCoins(VALUE1, VALUE2, PRUNED, PRUNED, 0 , DIRTY );
CheckModifyCoins(VALUE1, VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY );
CheckModifyCoins(VALUE1, VALUE2, PRUNED, PRUNED, DIRTY , DIRTY );
CheckModifyCoins(VALUE1, VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, 0 , DIRTY );
CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH);
CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, DIRTY , DIRTY );
CheckModifyCoins(VALUE1, VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH);
}
void CheckModifyNewCoinsBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
SetCoinsValue(modify_value, *test.cache.ModifyNewCoins(TXID, coinbase));
CAmount result_value;
char result_flags;
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
BOOST_CHECK_EQUAL(result_value, expected_value);
BOOST_CHECK_EQUAL(result_flags, expected_flags);
}
// Simple wrapper for CheckModifyNewCoinsBase function above that loops through
// different possible base_values, making sure each one gives the same results.
// This wrapper lets the modify_new test below be shorter and less repetitive,
// while still verifying that the CoinsViewCache::ModifyNewCoins implementation
// ignores base values.
template <typename... Args>
void CheckModifyNewCoins(Args&&... args)
{
for (CAmount base_value : {ABSENT, PRUNED, VALUE1})
CheckModifyNewCoinsBase(base_value, std::forward<Args>(args)...);
}
BOOST_AUTO_TEST_CASE(ccoins_modify_new)
{
/* Check ModifyNewCoin behavior, requesting a new coin from a cache view,
* writing a modification to the coin, and then checking the resulting
* entry in the cache after the modification. Verify behavior with the
* with the ModifyNewCoin coinbase argument set to false, and to true.
*
* Cache Write Result Cache Result Coinbase
* Value Value Value Flags Flags
*/
CheckModifyNewCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , NO_ENTRY , false);
CheckModifyNewCoins(ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY , true );
CheckModifyNewCoins(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH, false);
CheckModifyNewCoins(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY , true );
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, 0 , NO_ENTRY , false);
CheckModifyNewCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY , true );
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY , false);
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY , true );
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, DIRTY , NO_ENTRY , false);
CheckModifyNewCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , true );
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , false);
CheckModifyNewCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , true );
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, 0 , DIRTY , true );
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY|FRESH, false);
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY , true );
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
CheckModifyNewCoins(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, 0 , NO_ENTRY , false);
CheckModifyNewCoins(VALUE2, PRUNED, PRUNED, 0 , DIRTY , true );
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY , false);
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, FRESH , NO_ENTRY , true );
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, DIRTY , NO_ENTRY , false);
CheckModifyNewCoins(VALUE2, PRUNED, PRUNED, DIRTY , DIRTY , true );
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , false);
CheckModifyNewCoins(VALUE2, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY , true );
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, 0 , DIRTY , true );
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY , DIRTY|FRESH, false);
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY , DIRTY , true );
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
CheckModifyNewCoins(VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
}
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
{
SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
WriteCoinsViewEntry(test.cache, child_value, child_flags);
test.cache.SelfTest();
CAmount result_value;
char result_flags;
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
BOOST_CHECK_EQUAL(result_value, expected_value);
BOOST_CHECK_EQUAL(result_flags, expected_flags);
}
BOOST_AUTO_TEST_CASE(ccoins_write)
{
/* Check BatchWrite behavior, flushing one entry from a child cache to a
* parent cache, and checking the resulting entry in the parent cache
* after the write.
*
* Parent Child Result Parent Child Result
* Value Value Value Flags Flags Flags
*/
CheckWriteCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY , NO_ENTRY );
CheckWriteCoins(ABSENT, PRUNED, PRUNED, NO_ENTRY , DIRTY , DIRTY );
CheckWriteCoins(ABSENT, PRUNED, ABSENT, NO_ENTRY , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY , DIRTY );
CheckWriteCoins(ABSENT, VALUE2, VALUE2, NO_ENTRY , DIRTY|FRESH, DIRTY|FRESH);
CheckWriteCoins(PRUNED, ABSENT, PRUNED, 0 , NO_ENTRY , 0 );
CheckWriteCoins(PRUNED, ABSENT, PRUNED, FRESH , NO_ENTRY , FRESH );
CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY , NO_ENTRY , DIRTY );
CheckWriteCoins(PRUNED, ABSENT, PRUNED, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
CheckWriteCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
CheckWriteCoins(PRUNED, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
CheckWriteCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
CheckWriteCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
CheckWriteCoins(PRUNED, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
CheckWriteCoins(PRUNED, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
CheckWriteCoins(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
CheckWriteCoins(VALUE1, ABSENT, VALUE1, 0 , NO_ENTRY , 0 );
CheckWriteCoins(VALUE1, ABSENT, VALUE1, FRESH , NO_ENTRY , FRESH );
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
// The checks above omit cases where the child flags are not DIRTY, since
// they would be too repetitive (the parent cache is never updated in these
// cases). The loop below covers these cases and makes sure the parent cache
// is always left unchanged.
for (CAmount parent_value : {ABSENT, PRUNED, VALUE1})
for (CAmount child_value : {ABSENT, PRUNED, VALUE2})
for (char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
for (char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
}
BOOST_AUTO_TEST_SUITE_END()