This is a large randomized insert/remove simulation test on a variable-size stack of caches on top of CCoinsViewTest. It will randomly create/update/delete Coin entries to a tip of caches, with txids picked from a limited list of random 256-bit hashes. Occasionally, a new tip is added to the stack of caches, or the tip is flushed and removed. During the process, booleans are kept to make sure th
| 121 | // otherwise we'll hit an assertion in BatchWrite. |
| 122 | // |
| 123 | void SimulationTest(CCoinsView* base, bool fake_best_block) |
| 124 | { |
| 125 | // Various coverage trackers. |
| 126 | bool removed_all_caches = false; |
| 127 | bool reached_4_caches = false; |
| 128 | bool added_an_entry = false; |
| 129 | bool added_an_unspendable_entry = false; |
| 130 | bool removed_an_entry = false; |
| 131 | bool updated_an_entry = false; |
| 132 | bool found_an_entry = false; |
| 133 | bool missed_an_entry = false; |
| 134 | bool uncached_an_entry = false; |
| 135 | bool flushed_without_erase = false; |
| 136 | |
| 137 | // A simple map to track what we expect the cache stack to represent. |
| 138 | std::map<COutPoint, Coin> result; |
| 139 | |
| 140 | // The cache stack. |
| 141 | std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top. |
| 142 | stack.push_back(std::make_unique<CCoinsViewCacheTest>(base)); // Start with one cache. |
| 143 | |
| 144 | // Use a limited set of random transaction ids, so we do test overwriting entries. |
| 145 | std::vector<Txid> txids; |
| 146 | txids.resize(NUM_SIMULATION_ITERATIONS / 8); |
| 147 | for (unsigned int i = 0; i < txids.size(); i++) { |
| 148 | txids[i] = Txid::FromUint256(m_rng.rand256()); |
| 149 | } |
| 150 | |
| 151 | for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { |
| 152 | // Do a random modification. |
| 153 | { |
| 154 | auto txid = txids[m_rng.randrange(txids.size())]; // txid we're going to modify in this iteration. |
| 155 | Coin& coin = result[COutPoint(txid, 0)]; |
| 156 | |
| 157 | // Determine whether to test HaveCoin before or after Access* (or both). As these functions |
| 158 | // can influence each other's behaviour by pulling things into the cache, all combinations |
| 159 | // are tested. |
| 160 | bool test_havecoin_before = m_rng.randbits(2) == 0; |
| 161 | bool test_havecoin_after = m_rng.randbits(2) == 0; |
| 162 | |
| 163 | bool result_havecoin = test_havecoin_before ? stack.back()->HaveCoin(COutPoint(txid, 0)) : false; |
| 164 | |
| 165 | // Infrequently, test usage of AccessByTxid instead of AccessCoin - the |
| 166 | // former just delegates to the latter and returns the first unspent in a txn. |
| 167 | const Coin& entry = (m_rng.randrange(500) == 0) ? |
| 168 | AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0)); |
| 169 | BOOST_CHECK(coin == entry); |
| 170 | |
| 171 | if (test_havecoin_before) { |
| 172 | BOOST_CHECK(result_havecoin == !entry.IsSpent()); |
| 173 | } |
| 174 | |
| 175 | if (test_havecoin_after) { |
| 176 | bool ret = stack.back()->HaveCoin(COutPoint(txid, 0)); |
| 177 | BOOST_CHECK(ret == !entry.IsSpent()); |
| 178 | } |
| 179 | |
| 180 | if (m_rng.randrange(5) == 0 || coin.IsSpent()) { |
nothing calls this directly
no test coverage detected