diff --git a/src/test/radix_tests.cpp b/src/test/radix_tests.cpp index e021ba9a5..75aa5971a 100644 --- a/src/test/radix_tests.cpp +++ b/src/test/radix_tests.cpp @@ -1,647 +1,647 @@ // Copyright (c) 2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include BOOST_FIXTURE_TEST_SUITE(radix_tests, BasicTestingSetup) template struct TestElement { K key; TestElement(K keyIn) : key(keyIn) {} const K &getId() const { return key; } IMPLEMENT_RCU_REFCOUNT(uint32_t); }; template struct TestElementInt : TestElement { TestElementInt(K keyIn) : TestElement(std::move(keyIn)) {} static auto SignedMin() { return std::numeric_limits::type>::min(); } static auto SignedMax() { return std::numeric_limits::type>::max(); } static auto MinusOne() { return std::numeric_limits::type>::max(); } static auto MinusTwo() { return std::numeric_limits< typename std::make_unsigned::type>::max() - 1; } }; struct TestElementUint256 : TestElement { TestElementUint256(const uint256 &keyIn) : TestElement(Uint256RadixKey(keyIn)) {} TestElementUint256(uint64_t keyIn) : TestElement(Uint256RadixKey(keyIn)) {} static inline arith_uint256 signedMin = arith_uint256(1) << 255; static inline arith_uint256 signedMax = ~signedMin; static inline arith_uint256 minusOne = arith_uint256(0) - 1; static inline arith_uint256 minusTwo = minusOne - 1; static uint256 SignedMin() { return ArithToUint256(signedMin); } static uint256 SignedMax() { return ArithToUint256(signedMax); } static uint256 MinusOne() { return ArithToUint256(minusOne); } static uint256 MinusTwo() { return ArithToUint256(minusTwo); } }; template void testInsert() { RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); // Inserting a new element into the tree returns true. BOOST_CHECK(mytree.insert(one)); // Inserting an element already in the tree returns false. BOOST_CHECK(!mytree.insert(one)); // Let's insert more elements and check behavior stays consistent. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(!mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(!mytree.insert(two)); BOOST_CHECK(!mytree.insert(three)); // Check extreme values. auto maxsigned = RCUPtr::make(E::SignedMax()); auto minsigned = RCUPtr::make(E::SignedMin()); auto minusone = RCUPtr::make(E::MinusOne()); auto minustwo = RCUPtr::make(E::MinusTwo()); // Insert them into the tree. BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // All elements are now in the tree. BOOST_CHECK(!mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(!mytree.insert(two)); BOOST_CHECK(!mytree.insert(three)); BOOST_CHECK(!mytree.insert(maxsigned)); BOOST_CHECK(!mytree.insert(minsigned)); BOOST_CHECK(!mytree.insert(minusone)); BOOST_CHECK(!mytree.insert(minustwo)); } BOOST_AUTO_TEST_CASE(insert_test) { testInsert>(); testInsert>(); testInsert>(); testInsert>(); testInsert(); } template void testGet() { RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); auto keyZero = zero->getId(); auto keyOne = one->getId(); auto keyTwo = two->getId(); auto keyThree = three->getId(); // There are no elements in the tree so far. - BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), nullptr); // Insert an element into the tree and check it is there. BOOST_CHECK(mytree.insert(one)); BOOST_CHECK_EQUAL(mytree.get(keyOne), one); // Let's insert more elements and check they are recovered properly. - BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyZero), nullptr); BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); BOOST_CHECK_EQUAL(mytree.get(keyOne), one); // More elements. - BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyThree), nullptr); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); BOOST_CHECK_EQUAL(mytree.get(keyOne), one); BOOST_CHECK_EQUAL(mytree.get(keyTwo), two); BOOST_CHECK_EQUAL(mytree.get(keyThree), three); auto maxsigned = RCUPtr::make(E::SignedMax()); auto minsigned = RCUPtr::make(E::SignedMin()); auto minusone = RCUPtr::make(E::MinusOne()); auto minustwo = RCUPtr::make(E::MinusTwo()); // Check that they are not in the tree. - BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), nullptr); // Insert into the tree. BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // And now they all are in the tree. BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); BOOST_CHECK_EQUAL(mytree.get(keyOne), one); BOOST_CHECK_EQUAL(mytree.get(keyTwo), two); BOOST_CHECK_EQUAL(mytree.get(keyThree), three); BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), maxsigned); BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), minsigned); BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), minusone); BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), minustwo); } BOOST_AUTO_TEST_CASE(get_test) { testGet>(); testGet>(); testGet>(); testGet>(); testGet(); } template void testRemove() { RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); auto keyZero = zero->getId(); auto keyOne = one->getId(); auto keyTwo = two->getId(); auto keyThree = three->getId(); // Removing an element that isn't in the tree returns false. BOOST_CHECK(!mytree.remove(keyOne)); // Insert an element into the tree and check you can remove it. BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.remove(keyOne)); - BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), nullptr); // Insert several elements and remove them. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(mytree.remove(keyZero)); BOOST_CHECK(mytree.remove(keyOne)); BOOST_CHECK(mytree.remove(keyTwo)); BOOST_CHECK(mytree.remove(keyThree)); - BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyZero), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyOne), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyThree), nullptr); // Once the elements are removed, removing them again returns false. BOOST_CHECK(!mytree.remove(keyZero)); BOOST_CHECK(!mytree.remove(keyOne)); BOOST_CHECK(!mytree.remove(keyTwo)); BOOST_CHECK(!mytree.remove(keyThree)); // Check extreme values. auto maxsigned = RCUPtr::make(E::SignedMax()); auto minsigned = RCUPtr::make(E::SignedMin()); auto minusone = RCUPtr::make(E::MinusOne()); auto minustwo = RCUPtr::make(E::MinusTwo()); // Insert them all. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // Delete them all BOOST_CHECK(mytree.remove(keyZero)); BOOST_CHECK(mytree.remove(keyOne)); BOOST_CHECK(mytree.remove(keyTwo)); BOOST_CHECK(mytree.remove(keyThree)); BOOST_CHECK(mytree.remove(E::SignedMax())); BOOST_CHECK(mytree.remove(E::SignedMin())); BOOST_CHECK(mytree.remove(E::MinusOne())); BOOST_CHECK(mytree.remove(E::MinusTwo())); - BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyZero), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyOne), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), nullptr); + BOOST_CHECK_EQUAL(mytree.get(keyThree), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), nullptr); + BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), nullptr); } BOOST_AUTO_TEST_CASE(remove_test) { testRemove>(); testRemove>(); testRemove>(); testRemove>(); testRemove(); } BOOST_AUTO_TEST_CASE(const_element_test) { using C = const TestElementInt; RadixTree mytree; BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); BOOST_CHECK(!mytree.insert(RCUPtr::make(0))); BOOST_CHECK(!mytree.insert(RCUPtr::make(1))); BOOST_CHECK(!mytree.insert(RCUPtr::make(2))); BOOST_CHECK(!mytree.insert(RCUPtr::make(3))); BOOST_CHECK(mytree.get(0)); BOOST_CHECK(mytree.get(1)); BOOST_CHECK(mytree.get(2)); BOOST_CHECK(mytree.get(3)); BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); BOOST_CHECK(!mytree.get(0)); BOOST_CHECK(!mytree.get(1)); BOOST_CHECK(!mytree.get(2)); BOOST_CHECK(!mytree.get(3)); BOOST_CHECK(!mytree.remove(0)); BOOST_CHECK(!mytree.remove(1)); BOOST_CHECK(!mytree.remove(2)); BOOST_CHECK(!mytree.remove(3)); } void CheckConstTree(const RadixTree> &mytree, bool expected) { BOOST_CHECK_EQUAL(!!mytree.get(0), expected); BOOST_CHECK_EQUAL(!!mytree.get(1), expected); BOOST_CHECK_EQUAL(!!mytree.get(2), expected); BOOST_CHECK_EQUAL(!!mytree.get(3), expected); } BOOST_AUTO_TEST_CASE(const_tree_test) { using E = TestElementInt; RadixTree mytree; CheckConstTree(mytree, false); BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); CheckConstTree(mytree, true); BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); CheckConstTree(mytree, false); } BOOST_AUTO_TEST_CASE(test_cow) { using E = TestElementInt; RadixTree mytree; BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); RadixTree copyTree = mytree; // The copy tree is identical. BOOST_CHECK(copyTree.get(0)); BOOST_CHECK(copyTree.get(1)); BOOST_CHECK(copyTree.get(2)); BOOST_CHECK(copyTree.get(3)); BOOST_CHECK(mytree.insert(RCUPtr::make(90))); BOOST_CHECK(mytree.insert(RCUPtr::make(91))); BOOST_CHECK(mytree.insert(RCUPtr::make(92))); BOOST_CHECK(mytree.insert(RCUPtr::make(93))); // The copy was unaffected. BOOST_CHECK(copyTree.get(0)); BOOST_CHECK(copyTree.get(1)); BOOST_CHECK(copyTree.get(2)); BOOST_CHECK(copyTree.get(3)); BOOST_CHECK(!copyTree.get(90)); BOOST_CHECK(!copyTree.get(91)); BOOST_CHECK(!copyTree.get(92)); BOOST_CHECK(!copyTree.get(93)); copyTree = mytree; BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); // The copy was unaffected by the removals. BOOST_CHECK(copyTree.get(0)); BOOST_CHECK(copyTree.get(1)); BOOST_CHECK(copyTree.get(2)); BOOST_CHECK(copyTree.get(3)); BOOST_CHECK(copyTree.get(90)); BOOST_CHECK(copyTree.get(91)); BOOST_CHECK(copyTree.get(92)); BOOST_CHECK(copyTree.get(93)); } BOOST_AUTO_TEST_CASE(test_move) { using E = TestElementInt; RadixTree mytree; BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); RadixTree movedTree = std::move(mytree); BOOST_CHECK(!mytree.remove(0)); BOOST_CHECK(!mytree.remove(1)); BOOST_CHECK(!mytree.remove(2)); BOOST_CHECK(!mytree.remove(3)); BOOST_CHECK(movedTree.remove(0)); BOOST_CHECK(movedTree.remove(1)); BOOST_CHECK(movedTree.remove(2)); BOOST_CHECK(movedTree.remove(3)); } #define THREADS 128 #define ELEMENTS 65536 BOOST_AUTO_TEST_CASE(insert_stress_test) { using E = TestElementInt; RadixTree mytree; std::atomic success{0}; std::vector threads; for (int i = 0; i < THREADS; i++) { threads.push_back(std::thread([&] { MMIXLinearCongruentialGenerator lcg; for (int j = 0; j < ELEMENTS; j++) { uint32_t v = lcg.next(); if (mytree.remove(v)) { success--; std::this_thread::yield(); } auto ptr = RCUPtr::make(v); if (mytree.insert(ptr)) { success++; std::this_thread::yield(); } if (mytree.remove(v)) { success--; std::this_thread::yield(); } if (mytree.insert(ptr)) { success++; std::this_thread::yield(); } } })); } // Wait for all the threads to finish. for (std::thread &t : threads) { t.join(); } BOOST_CHECK_EQUAL(success.load(), ELEMENTS); // All the elements have been inserted into the tree. MMIXLinearCongruentialGenerator lcg; for (int i = 0; i < ELEMENTS; i++) { uint32_t v = lcg.next(); BOOST_CHECK_EQUAL(mytree.get(v)->getId(), v); auto ptr = RCUPtr::make(v); BOOST_CHECK(!mytree.insert(ptr)); BOOST_CHECK(mytree.get(v) != ptr); } // Cleanup after ourselves. RCULock::synchronize(); } BOOST_AUTO_TEST_CASE(tree_traversal) { using E = TestElement; RadixTree mytree; // Build a vector of elements in ascending key order std::vector> elements; elements.reserve(ELEMENTS); for (uint32_t i = 0; i < ELEMENTS; i++) { auto ptr = RCUPtr::make(i); elements.push_back(std::move(ptr)); } // Insert in random order auto randomizedElements = elements; Shuffle(randomizedElements.begin(), randomizedElements.end(), FastRandomContext()); for (auto &element : randomizedElements) { BOOST_CHECK(mytree.insert(element)); } // Check the tree is traversed in ascending key order size_t count = 0; bool ret = mytree.forEachLeaf([&](RCUPtr ptr) { // This test assumes the key is equal to the value BOOST_CHECK_EQUAL(ptr->getId(), count); BOOST_CHECK_EQUAL(ptr, elements[count++]); return true; }); // All the elements are parsed BOOST_CHECK_EQUAL(count, ELEMENTS); BOOST_CHECK(ret); // Check we can stop the traversal when needed const size_t stopCount = ELEMENTS / 2; count = 0; ret = mytree.forEachLeaf([&](RCUPtr ptr) { // This test assumes the key is equal to the value BOOST_CHECK_EQUAL(ptr->getId(), count); BOOST_CHECK_EQUAL(ptr, elements[count++]); return count < stopCount; }); BOOST_CHECK_EQUAL(count, stopCount); BOOST_CHECK(!ret); } BOOST_AUTO_TEST_CASE(uint256_key_wrapper) { Uint256RadixKey key = uint256S( "AA00000000000000000000000000000000000000000000000000000000000000"); auto checkEqual = [&](const Uint256RadixKey &val, const uint256 &expected) { BOOST_CHECK_EQUAL(ArithToUint256(val.base), expected); }; auto checkOperands = [&](const Uint256RadixKey &val, const uint256 &expected_uint256, const size_t expected_size_t) { checkEqual(val, expected_uint256); checkEqual(val & Uint256RadixKey(uint256::ZERO), uint256::ZERO); checkEqual(val & val, expected_uint256); checkEqual(val & TestElementUint256::MinusOne(), expected_uint256); BOOST_CHECK_EQUAL(size_t(val), expected_size_t); }; // clang-format off checkOperands(key >> 0u, uint256S("AA00000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 1u, uint256S("5500000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 2u, uint256S("2A80000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 3u, uint256S("1540000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 4u, uint256S("0AA0000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 8u, uint256S("00AA000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 16u, uint256S("0000AA0000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 24u, uint256S("000000AA00000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 32u, uint256S("00000000AA000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 40u, uint256S("0000000000AA0000000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 48u, uint256S("000000000000AA00000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 56u, uint256S("00000000000000AA000000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 64u, uint256S("0000000000000000AA0000000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 72u, uint256S("000000000000000000AA00000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 80u, uint256S("00000000000000000000AA000000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 88u, uint256S("0000000000000000000000AA0000000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 96u, uint256S("000000000000000000000000AA00000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 104u, uint256S("00000000000000000000000000AA000000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 112u, uint256S("0000000000000000000000000000AA0000000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 120u, uint256S("000000000000000000000000000000AA00000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 128u, uint256S("00000000000000000000000000000000AA000000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 136u, uint256S("0000000000000000000000000000000000AA0000000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 144u, uint256S("000000000000000000000000000000000000AA00000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 152u, uint256S("00000000000000000000000000000000000000AA000000000000000000000000"), 0x0000000000000000); checkOperands(key >> 160u, uint256S("0000000000000000000000000000000000000000AA0000000000000000000000"), 0x0000000000000000); checkOperands(key >> 168u, uint256S("000000000000000000000000000000000000000000AA00000000000000000000"), 0x0000000000000000); checkOperands(key >> 176u, uint256S("00000000000000000000000000000000000000000000AA000000000000000000"), 0x0000000000000000); checkOperands(key >> 184u, uint256S("0000000000000000000000000000000000000000000000AA0000000000000000"), 0x0000000000000000); checkOperands(key >> 185u, uint256S("0000000000000000000000000000000000000000000000550000000000000000"), 0x0000000000000000); checkOperands(key >> 186u, uint256S("00000000000000000000000000000000000000000000002A8000000000000000"), 0x8000000000000000); checkOperands(key >> 192u, uint256S("000000000000000000000000000000000000000000000000AA00000000000000"), 0xAA00000000000000); checkOperands(key >> 200u, uint256S("00000000000000000000000000000000000000000000000000AA000000000000"), 0x00AA000000000000); checkOperands(key >> 208u, uint256S("0000000000000000000000000000000000000000000000000000AA0000000000"), 0x0000AA0000000000); checkOperands(key >> 216u, uint256S("000000000000000000000000000000000000000000000000000000AA00000000"), 0x000000AA00000000); checkOperands(key >> 224u, uint256S("00000000000000000000000000000000000000000000000000000000AA000000"), 0x00000000AA000000); checkOperands(key >> 232u, uint256S("0000000000000000000000000000000000000000000000000000000000AA0000"), 0x0000000000AA0000); checkOperands(key >> 240u, uint256S("000000000000000000000000000000000000000000000000000000000000AA00"), 0x000000000000AA00); checkOperands(key >> 248u, uint256S("00000000000000000000000000000000000000000000000000000000000000AA"), 0x00000000000000AA); checkOperands(key >> 252u, uint256S("000000000000000000000000000000000000000000000000000000000000000A"), 0x000000000000000A); checkOperands(key >> 253u, uint256S("0000000000000000000000000000000000000000000000000000000000000005"), 0x0000000000000005); checkOperands(key >> 254u, uint256S("0000000000000000000000000000000000000000000000000000000000000002"), 0x0000000000000002); checkOperands(key >> 255u, uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0x0000000000000001); checkOperands(key >> 256u, uint256S("0000000000000000000000000000000000000000000000000000000000000000"), 0x0000000000000000); // clang-format on } BOOST_AUTO_TEST_CASE(radix_adapter) { using E = TestElement; struct StringKeyAdapter { uint64_t getId(const E &e) const { uint64_t key; BOOST_REQUIRE(ParseUInt64(e.getId(), &key)); return key; } }; RadixTree mytree; auto zero = RCUPtr::make("0"); auto one = RCUPtr::make("1"); auto two = RCUPtr::make("2"); auto three = RCUPtr::make("3"); // Let's insert some elements BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(!mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(!mytree.insert(two)); BOOST_CHECK(!mytree.insert(three)); // Retrieval is done using the converted key BOOST_CHECK_EQUAL(mytree.get(0), zero); BOOST_CHECK_EQUAL(mytree.get(1), one); BOOST_CHECK_EQUAL(mytree.get(2), two); BOOST_CHECK_EQUAL(mytree.get(3), three); // And so does removal BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); BOOST_CHECK(!mytree.get(0)); BOOST_CHECK(!mytree.get(1)); BOOST_CHECK(!mytree.get(2)); BOOST_CHECK(!mytree.get(3)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rcu_tests.cpp b/src/test/rcu_tests.cpp index 5601d5870..f781b5135 100644 --- a/src/test/rcu_tests.cpp +++ b/src/test/rcu_tests.cpp @@ -1,475 +1,475 @@ // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include struct RCUTest { static uint64_t getRevision() { return RCUInfos::revision.load(); } static uint64_t hasSyncedTo(uint64_t syncRev) { return RCUInfos::infos.hasSyncedTo(syncRev); } static std::map> &getCleanups() { return RCUInfos::infos.cleanups; } }; BOOST_AUTO_TEST_SUITE(rcu_tests) enum RCUTestStep { Init, Locked, LockAck, RCULocked, Synchronizing, Synchronized, }; #define WAIT_FOR_STEP(step) \ do { \ cond.notify_all(); \ } while (!cond.wait_for(lock, std::chrono::milliseconds(1), \ [&] { return otherstep == step; })) void synchronize(std::atomic &step, const std::atomic &otherstep, Mutex &cs, std::condition_variable &cond, std::atomic &syncRev) { assert(step == RCUTestStep::Init); { WAIT_LOCK(cs, lock); step = RCUTestStep::Locked; // Wait for our lock to be acknowledged. WAIT_FOR_STEP(RCUTestStep::LockAck); RCULock rculock; // Update step. step = RCUTestStep::RCULocked; // Wait for master. WAIT_FOR_STEP(RCUTestStep::RCULocked); } // Update step. syncRev = RCUTest::getRevision() + 1; step = RCUTestStep::Synchronizing; assert(!RCUTest::hasSyncedTo(syncRev)); // We wait for readers. RCULock::synchronize(); // Update step. step = RCUTestStep::Synchronized; } void lockAndWaitForSynchronize(std::atomic &step, const std::atomic &otherstep, Mutex &cs, std::condition_variable &cond, std::atomic &syncRev) { assert(step == RCUTestStep::Init); WAIT_LOCK(cs, lock); // Wait for th eother thread to be locked. WAIT_FOR_STEP(RCUTestStep::Locked); step = RCUTestStep::LockAck; // Wait for the synchronizing tread to take its RCU lock. WAIT_FOR_STEP(RCUTestStep::RCULocked); assert(!RCUTest::hasSyncedTo(syncRev)); { RCULock rculock; // Update master step. step = RCUTestStep::RCULocked; while (RCUTest::getRevision() < syncRev) { WAIT_FOR_STEP(RCUTestStep::Synchronizing); } assert(RCUTest::getRevision() >= syncRev); assert(otherstep.load() == RCUTestStep::Synchronizing); } assert(RCUTest::hasSyncedTo(syncRev) >= syncRev); WAIT_FOR_STEP(RCUTestStep::Synchronized); } static const int COUNT = 128; BOOST_AUTO_TEST_CASE(synchronize_test) { Mutex cs; std::condition_variable cond; std::atomic parentstep; std::atomic childstep; std::atomic syncRev; for (int i = 0; i < COUNT; i++) { parentstep = RCUTestStep::Init; childstep = RCUTestStep::Init; syncRev = RCUTest::getRevision() + 1; std::thread tlock([&] { lockAndWaitForSynchronize(parentstep, childstep, cs, cond, syncRev); }); std::thread tsync( [&] { synchronize(childstep, parentstep, cs, cond, syncRev); }); tlock.join(); tsync.join(); } // Needed to suppress "Test case [...] did not check any assertions" BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE(cleanup_simple) { RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); bool isClean1 = false; RCULock::registerCleanup([&] { isClean1 = true; }); BOOST_CHECK(!isClean1); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 1); auto revision = RCUTest::getCleanups().begin()->first; BOOST_CHECK_EQUAL(RCUTest::getRevision(), revision); // Synchronize runs the cleanups. RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); } BOOST_AUTO_TEST_CASE(cleanup_multiple) { // Check multiple callbacks. bool isClean1 = false; bool isClean2 = false; bool isClean3 = false; RCULock::registerCleanup([&] { isClean1 = true; }); RCULock::registerCleanup([&] { isClean2 = true; }); RCULock::registerCleanup([&] { isClean3 = true; }); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 3); RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); BOOST_CHECK(isClean2); BOOST_CHECK(isClean3); } BOOST_AUTO_TEST_CASE(cleanup_test_nested) { // Check callbacks adding each others. bool isClean1 = false; bool isClean2 = false; bool isClean3 = false; RCULock::registerCleanup([&] { isClean1 = true; RCULock::registerCleanup([&] { isClean2 = true; RCULock::registerCleanup([&] { isClean3 = true; }); }); }); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 1); RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); BOOST_CHECK(isClean2); BOOST_CHECK(isClean3); } BOOST_AUTO_TEST_CASE(cleanup_on_unlock) { // Check callbacks adding each others. bool isClean1 = false; bool isClean2 = false; bool isClean3 = false; RCULock::registerCleanup([&] { isClean1 = true; RCULock::registerCleanup([&] { isClean2 = true; RCULock::registerCleanup([&] { isClean3 = true; }); }); }); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 1); { // There is no contention, so this will cleanup. RCULock lock; } BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); BOOST_CHECK(isClean2); BOOST_CHECK(isClean3); } class RCURefTestItem { IMPLEMENT_RCU_REFCOUNT(uint32_t); const std::function cleanupfun; public: explicit RCURefTestItem(const std::function &fun) : cleanupfun(fun) {} ~RCURefTestItem() { cleanupfun(); } uint32_t getRefCount() const { return refcount.load(); } }; BOOST_AUTO_TEST_CASE(rcuptr_test) { // Make sure it works for null. { RCURefTestItem *ptr = nullptr; RCUPtr::copy(ptr); RCUPtr::acquire(ptr); } // Check the destruction mechanism. bool isDestroyed = false; { auto rcuptr = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 0); } // rcuptr waits for synchronization to destroy. BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Check that copy behaves properly. isDestroyed = false; RCUPtr gptr; { auto rcuptr = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 0); gptr = rcuptr; BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 1); BOOST_CHECK_EQUAL(gptr->getRefCount(), 1); auto rcuptrcopy = rcuptr; BOOST_CHECK_EQUAL(rcuptrcopy->getRefCount(), 2); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 2); BOOST_CHECK_EQUAL(gptr->getRefCount(), 2); } BOOST_CHECK_EQUAL(gptr->getRefCount(), 0); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); gptr = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_CASE(rcuptr_operator_test) { auto gptr = RCUPtr(); auto ptr = new RCURefTestItem([] {}); auto oldPtr = ptr; auto altptr = RCUPtr::make([] {}); // Check various operators. - BOOST_CHECK_EQUAL(gptr.get(), NULLPTR(RCURefTestItem)); - BOOST_CHECK_EQUAL(gptr, NULLPTR(RCURefTestItem)); + BOOST_CHECK_EQUAL(gptr.get(), nullptr); + BOOST_CHECK_EQUAL(gptr, nullptr); BOOST_CHECK(!gptr); auto copyptr = gptr; BOOST_CHECK(gptr == nullptr); BOOST_CHECK(gptr != oldPtr); BOOST_CHECK(gptr == copyptr); BOOST_CHECK(gptr != altptr); gptr = RCUPtr::acquire(ptr); - BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefTestItem)); + BOOST_CHECK_EQUAL(ptr, nullptr); BOOST_CHECK_EQUAL(gptr.get(), oldPtr); BOOST_CHECK_EQUAL(&*gptr, oldPtr); BOOST_CHECK_EQUAL(gptr, oldPtr); BOOST_CHECK(gptr); copyptr = gptr; BOOST_CHECK(gptr != nullptr); BOOST_CHECK(gptr == oldPtr); BOOST_CHECK(gptr == copyptr); BOOST_CHECK(gptr != altptr); } BOOST_AUTO_TEST_CASE(const_rcuptr_test) { bool isDestroyed = false; auto ptr = RCUPtr::make([&] { isDestroyed = true; }); // Now let's destroy it. ptr = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } class RCURefMoveTestItem { const std::function cleanupfun; public: explicit RCURefMoveTestItem(const std::function &fun) : cleanupfun(fun) {} ~RCURefMoveTestItem() { cleanupfun(); } void incrementRefCount() { throw std::runtime_error("RCUPtr incremented the refcount"); } void decrementRefCount() { RCULock::registerCleanup([this] { delete this; }); } }; BOOST_AUTO_TEST_CASE(move_rcuptr_test) { bool isDestroyed = false; // Check tat copy is failing. auto rcuptr1 = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_THROW(rcuptr1->incrementRefCount(), std::runtime_error); BOOST_CHECK_THROW(auto rcuptrcopy = rcuptr1;, std::runtime_error); // Try to move. auto rcuptr2 = std::move(rcuptr1); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Move to a local and check proper destruction. { auto rcuptr3 = std::move(rcuptr2); } BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Let's try to swap. isDestroyed = false; rcuptr1 = RCUPtr::make([&] { isDestroyed = true; }); std::swap(rcuptr1, rcuptr2); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Chain moves to make sure there are no double free. { auto rcuptr3 = std::move(rcuptr2); auto rcuptr4 = std::move(rcuptr3); std::swap(rcuptr1, rcuptr4); } RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Check we can return from a function. { auto r = ([&] { auto moved = std::move(rcuptr1); return moved; })(); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); } BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Acquire/release workflow. isDestroyed = false; auto ptr = new RCURefMoveTestItem([&] { isDestroyed = true; }); auto ptrCopy = ptr; BOOST_CHECK_THROW(RCUPtr::copy(ptr), std::runtime_error); rcuptr1 = RCUPtr::acquire(ptr); BOOST_CHECK_EQUAL(rcuptr1, ptrCopy); - BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefMoveTestItem)); + BOOST_CHECK_EQUAL(ptr, nullptr); ptr = rcuptr1.release(); - BOOST_CHECK_EQUAL(rcuptr1, NULLPTR(RCURefMoveTestItem)); + BOOST_CHECK_EQUAL(rcuptr1, nullptr); BOOST_CHECK_EQUAL(ptr, ptrCopy); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); RCUPtr::acquire(ptr); - BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefMoveTestItem)); + BOOST_CHECK_EQUAL(ptr, nullptr); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_CASE(rcu_converting_constructor) { bool isDestroyed = false; auto rcuNonConst = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuNonConst->getRefCount(), 0); RCUPtr rcuConst(rcuNonConst); BOOST_CHECK_EQUAL(rcuConst->getRefCount(), 1); BOOST_CHECK_EQUAL(rcuNonConst->getRefCount(), 1); rcuNonConst = RCUPtr(); // We still have a copy BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Destroy the copy rcuConst = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_CASE(rcu_converting_assignment) { bool isDestroyed = false; auto rcuConst = RCUPtr(); { auto rcuNonConst = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuNonConst->getRefCount(), 0); rcuConst = rcuNonConst; BOOST_CHECK_EQUAL(rcuConst->getRefCount(), 1); BOOST_CHECK_EQUAL(rcuNonConst->getRefCount(), 1); } // We still have a copy BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Destroy the copy rcuConst = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 875ec7d4b..ca4ce4495 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -1,266 +1,257 @@ // Copyright (c) 2015-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_TEST_UTIL_SETUP_COMMON_H #define BITCOIN_TEST_UTIL_SETUP_COMMON_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -/** - * Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t. - * In order to work around this, we ensure that the null pointers are typed in a - * way that Boost will like better. - * - * TODO: Use nullptr directly once the minimum version of boost is 1.64 or more. - */ -#define NULLPTR(T) static_cast(nullptr) - // Enable BOOST_CHECK_EQUAL for enum class types template std::ostream &operator<<( typename std::enable_if::value, std::ostream>::type &stream, const T &e) { return stream << static_cast::type>(e); } /** * This global and the helpers that use it are not thread-safe. * * If thread-safety is needed, the global could be made thread_local (given * that thread_local is supported on all architectures we support) or a * per-thread instance could be used in the multi-threaded test. */ extern FastRandomContext g_insecure_rand_ctx; /** * Flag to make GetRand in random.h return the same number */ extern bool g_mock_deterministic_tests; enum class SeedRand { ZEROS, //!< Seed with a compile time constant of zeros SEED, //!< Call the Seed() helper }; /** * Seed the given random ctx or use the seed passed in via an * environment var */ void Seed(FastRandomContext &ctx); static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED) { if (seed == SeedRand::ZEROS) { g_insecure_rand_ctx = FastRandomContext(/* deterministic */ true); } else { Seed(g_insecure_rand_ctx); } } static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); } static inline uint160 InsecureRand160() { return g_insecure_rand_ctx.rand160(); } static inline uint256 InsecureRand256() { return g_insecure_rand_ctx.rand256(); } static inline uint64_t InsecureRandBits(int bits) { return g_insecure_rand_ctx.randbits(bits); } static inline uint64_t InsecureRandRange(uint64_t range) { return g_insecure_rand_ctx.randrange(range); } static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); } static constexpr Amount CENT(COIN / 100); extern std::vector fixture_extra_args; /** * Basic testing setup. * This just configures logging, data dir and chain parameters. */ struct BasicTestingSetup { ECCVerifyHandle globalVerifyHandle; node::NodeContext m_node; explicit BasicTestingSetup( const std::string &chainName = CBaseChainParams::MAIN, const std::vector &extra_args = {}); ~BasicTestingSetup(); const fs::path m_path_root; ArgsManager m_args; }; /** * Testing setup that performs all steps up until right before * ChainstateManager gets initialized. Meant for testing ChainstateManager * initialization behaviour. */ struct ChainTestingSetup : public BasicTestingSetup { node::CacheSizes m_cache_sizes{}; explicit ChainTestingSetup( const std::string &chainName = CBaseChainParams::MAIN, const std::vector &extra_args = {}); ~ChainTestingSetup(); }; /** * Testing setup that configures a complete environment. */ struct TestingSetup : public ChainTestingSetup { explicit TestingSetup(const std::string &chainName = CBaseChainParams::MAIN, const std::vector &extra_args = {}); }; /** Identical to TestingSetup, but chain set to regtest */ struct RegTestingSetup : public TestingSetup { RegTestingSetup() : TestingSetup{CBaseChainParams::REGTEST} {} }; class CBlock; class Chainstate; class CMutableTransaction; class CScript; /** * Testing fixture that pre-creates a 100-block REGTEST-mode block chain */ struct TestChain100Setup : public RegTestingSetup { TestChain100Setup(); /** * Create a new block with just given transactions, coinbase paying to * scriptPubKey, and try to add it to the current chain. * If no chainstate is specified, default to the active. */ CBlock CreateAndProcessBlock(const std::vector &txns, const CScript &scriptPubKey, Chainstate *chainstate = nullptr); /** * Create a new block with just given transactions, coinbase paying to * scriptPubKey. */ CBlock CreateBlock(const std::vector &txns, const CScript &scriptPubKey, Chainstate &chainstate); //! Mine a series of new blocks on the active chain. void mineBlocks(int num_blocks); /** * Create a transaction and submit to the mempool. * * @param input_transaction The transaction to spend * @param input_vout The vout to spend from the input_transaction * @param input_height The height of the block that included the * input_transaction * @param input_signing_key The key to spend the input_transaction * @param output_destination Where to send the output * @param output_amount How much to send * @param submit Whether or not to submit to mempool */ CMutableTransaction CreateValidMempoolTransaction( CTransactionRef input_transaction, int input_vout, int input_height, CKey input_signing_key, CScript output_destination, Amount output_amount = COIN, bool submit = true); ~TestChain100Setup(); // For convenience, coinbase transactions. std::vector m_coinbase_txns; // private/public key needed to spend coinbase transactions. CKey coinbaseKey; }; class CTxMemPoolEntry; struct TestMemPoolEntryHelper { // Default values Amount nFee; int64_t nTime; unsigned int nHeight; bool spendsCoinbase; unsigned int nSigChecks; uint64_t entryId = 0; TestMemPoolEntryHelper() : nFee(), nTime(0), nHeight(1), spendsCoinbase(false), nSigChecks(1) {} CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const; CTxMemPoolEntry FromTx(const CTransactionRef &tx) const; // Change the default value TestMemPoolEntryHelper &Fee(Amount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigChecks(unsigned int _nSigChecks) { nSigChecks = _nSigChecks; return *this; } TestMemPoolEntryHelper &EntryId(uint64_t _entryId) { entryId = _entryId; return *this; } }; enum class ScriptError; // define implicit conversions here so that these types may be used in // BOOST_*_EQUAL std::ostream &operator<<(std::ostream &os, const uint256 &num); std::ostream &operator<<(std::ostream &os, const ScriptError &err); CBlock getBlock13b8a(); /** * BOOST_CHECK_EXCEPTION predicates to check the specific validation error. * Use as * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo")); */ class HasReason { public: explicit HasReason(const std::string &reason) : m_reason(reason) {} bool operator()(const std::exception &e) const { return std::string(e.what()).find(m_reason) != std::string::npos; }; private: const std::string m_reason; }; #endif // BITCOIN_TEST_UTIL_SETUP_COMMON_H