LCOV - code coverage report
Current view: top level - src/test - txrequest_tests.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 447 448 99.8 %
Date: 2022-04-21 14:51:19 Functions: 40 40 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 140 142 98.6 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2020-2021 The Bitcoin Core developers
#       2                 :            : // Distributed under the MIT software license, see the accompanying
#       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       4                 :            : 
#       5                 :            : 
#       6                 :            : #include <txrequest.h>
#       7                 :            : #include <uint256.h>
#       8                 :            : 
#       9                 :            : #include <test/util/setup_common.h>
#      10                 :            : 
#      11                 :            : #include <algorithm>
#      12                 :            : #include <functional>
#      13                 :            : #include <vector>
#      14                 :            : 
#      15                 :            : #include <boost/test/unit_test.hpp>
#      16                 :            : 
#      17                 :            : BOOST_FIXTURE_TEST_SUITE(txrequest_tests, BasicTestingSetup)
#      18                 :            : 
#      19                 :            : namespace {
#      20                 :            : 
#      21                 :            : constexpr std::chrono::microseconds MIN_TIME = std::chrono::microseconds::min();
#      22                 :            : constexpr std::chrono::microseconds MAX_TIME = std::chrono::microseconds::max();
#      23                 :            : constexpr std::chrono::microseconds MICROSECOND = std::chrono::microseconds{1};
#      24                 :            : constexpr std::chrono::microseconds NO_TIME = std::chrono::microseconds{0};
#      25                 :            : 
#      26                 :            : /** An Action is a function to call at a particular (simulated) timestamp. */
#      27                 :            : using Action = std::pair<std::chrono::microseconds, std::function<void()>>;
#      28                 :            : 
#      29                 :            : /** Object that stores actions from multiple interleaved scenarios, and data shared across them.
#      30                 :            :  *
#      31                 :            :  * The Scenario below is used to fill this.
#      32                 :            :  */
#      33                 :            : struct Runner
#      34                 :            : {
#      35                 :            :     /** The TxRequestTracker being tested. */
#      36                 :            :     TxRequestTracker txrequest;
#      37                 :            : 
#      38                 :            :     /** List of actions to be executed (in order of increasing timestamp). */
#      39                 :            :     std::vector<Action> actions;
#      40                 :            : 
#      41                 :            :     /** Which node ids have been assigned already (to prevent reuse). */
#      42                 :            :     std::set<NodeId> peerset;
#      43                 :            : 
#      44                 :            :     /** Which txhashes have been assigned already (to prevent reuse). */
#      45                 :            :     std::set<uint256> txhashset;
#      46                 :            : 
#      47                 :            :     /** Which (peer, gtxid) combinations are known to be expired. These need to be accumulated here instead of
#      48                 :            :      *  checked directly in the GetRequestable return value to avoid introducing a dependency between the various
#      49                 :            :      *  parallel tests. */
#      50                 :            :     std::multiset<std::pair<NodeId, GenTxid>> expired;
#      51                 :            : };
#      52                 :            : 
#      53                 :      17718 : std::chrono::microseconds RandomTime8s() { return std::chrono::microseconds{1 + InsecureRandBits(23)}; }
#      54                 :         10 : std::chrono::microseconds RandomTime1y() { return std::chrono::microseconds{1 + InsecureRandBits(45)}; }
#      55                 :            : 
#      56                 :            : /** A proxy for a Runner that helps build a sequence of consecutive test actions on a TxRequestTracker.
#      57                 :            :  *
#      58                 :            :  * Each Scenario is a proxy through which actions for the (sequential) execution of various tests are added to a
#      59                 :            :  * Runner. The actions from multiple scenarios are then run concurrently, resulting in these tests being performed
#      60                 :            :  * against a TxRequestTracker in parallel. Every test has its own unique txhashes and NodeIds which are not
#      61                 :            :  * reused in other tests, and thus they should be independent from each other. Running them in parallel however
#      62                 :            :  * means that we verify the behavior (w.r.t. one test's txhashes and NodeIds) even when the state of the data
#      63                 :            :  * structure is more complicated due to the presence of other tests.
#      64                 :            :  */
#      65                 :            : class Scenario
#      66                 :            : {
#      67                 :            :     Runner& m_runner;
#      68                 :            :     std::chrono::microseconds m_now;
#      69                 :            :     std::string m_testname;
#      70                 :            : 
#      71                 :            : public:
#      72                 :        450 :     Scenario(Runner& runner, std::chrono::microseconds starttime) : m_runner(runner), m_now(starttime) {}
#      73                 :            : 
#      74                 :            :     /** Set a name for the current test, to give more clear error messages. */
#      75                 :            :     void SetTestName(std::string testname)
#      76                 :       3200 :     {
#      77                 :       3200 :         m_testname = std::move(testname);
#      78                 :       3200 :     }
#      79                 :            : 
#      80                 :            :     /** Advance this Scenario's time; this affects the timestamps newly scheduled events get. */
#      81                 :            :     void AdvanceTime(std::chrono::microseconds amount)
#      82                 :      20102 :     {
#      83                 :      20102 :         assert(amount.count() >= 0);
#      84                 :          0 :         m_now += amount;
#      85                 :      20102 :     }
#      86                 :            : 
#      87                 :            :     /** Schedule a ForgetTxHash call at the Scheduler's current time. */
#      88                 :            :     void ForgetTxHash(const uint256& txhash)
#      89                 :        800 :     {
#      90                 :        800 :         auto& runner = m_runner;
#      91                 :        800 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#      92                 :        800 :             runner.txrequest.ForgetTxHash(txhash);
#      93                 :        800 :             runner.txrequest.SanityCheck();
#      94                 :        800 :         });
#      95                 :        800 :     }
#      96                 :            : 
#      97                 :            :     /** Schedule a ReceivedInv call at the Scheduler's current time. */
#      98                 :            :     void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool pref, std::chrono::microseconds reqtime)
#      99                 :      11200 :     {
#     100                 :      11200 :         auto& runner = m_runner;
#     101                 :      11200 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     102                 :      11200 :             runner.txrequest.ReceivedInv(peer, gtxid, pref, reqtime);
#     103                 :      11200 :             runner.txrequest.SanityCheck();
#     104                 :      11200 :         });
#     105                 :      11200 :     }
#     106                 :            : 
#     107                 :            :     /** Schedule a DisconnectedPeer call at the Scheduler's current time. */
#     108                 :            :     void DisconnectedPeer(NodeId peer)
#     109                 :       4502 :     {
#     110                 :       4502 :         auto& runner = m_runner;
#     111                 :       4502 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     112                 :       4502 :             runner.txrequest.DisconnectedPeer(peer);
#     113                 :       4502 :             runner.txrequest.SanityCheck();
#     114                 :       4502 :         });
#     115                 :       4502 :     }
#     116                 :            : 
#     117                 :            :     /** Schedule a RequestedTx call at the Scheduler's current time. */
#     118                 :            :     void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds exptime)
#     119                 :       5920 :     {
#     120                 :       5920 :         auto& runner = m_runner;
#     121                 :       5920 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     122                 :       5920 :             runner.txrequest.RequestedTx(peer, txhash, exptime);
#     123                 :       5920 :             runner.txrequest.SanityCheck();
#     124                 :       5920 :         });
#     125                 :       5920 :     }
#     126                 :            : 
#     127                 :            :     /** Schedule a ReceivedResponse call at the Scheduler's current time. */
#     128                 :            :     void ReceivedResponse(NodeId peer, const uint256& txhash)
#     129                 :       1898 :     {
#     130                 :       1898 :         auto& runner = m_runner;
#     131                 :       1898 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     132                 :       1898 :             runner.txrequest.ReceivedResponse(peer, txhash);
#     133                 :       1898 :             runner.txrequest.SanityCheck();
#     134                 :       1898 :         });
#     135                 :       1898 :     }
#     136                 :            : 
#     137                 :            :     /** Schedule calls to verify the TxRequestTracker's state at the Scheduler's current time.
#     138                 :            :      *
#     139                 :            :      * @param peer       The peer whose state will be inspected.
#     140                 :            :      * @param expected   The expected return value for GetRequestable(peer)
#     141                 :            :      * @param candidates The expected return value CountCandidates(peer)
#     142                 :            :      * @param inflight   The expected return value CountInFlight(peer)
#     143                 :            :      * @param completed  The expected return value of Count(peer), minus candidates and inflight.
#     144                 :            :      * @param checkname  An arbitrary string to include in error messages, for test identificatrion.
#     145                 :            :      * @param offset     Offset with the current time to use (must be <= 0). This allows simulations of time going
#     146                 :            :      *                   backwards (but note that the ordering of this event only follows the scenario's m_now.
#     147                 :            :      */
#     148                 :            :     void Check(NodeId peer, const std::vector<GenTxid>& expected, size_t candidates, size_t inflight,
#     149                 :            :         size_t completed, const std::string& checkname,
#     150                 :            :         std::chrono::microseconds offset = std::chrono::microseconds{0})
#     151                 :      58718 :     {
#     152                 :      58718 :         const auto comment = m_testname + " " + checkname;
#     153                 :      58718 :         auto& runner = m_runner;
#     154                 :      58718 :         const auto now = m_now;
#     155                 :      58718 :         assert(offset.count() <= 0);
#     156                 :      58718 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     157                 :      58718 :             std::vector<std::pair<NodeId, GenTxid>> expired_now;
#     158                 :      58718 :             auto ret = runner.txrequest.GetRequestable(peer, now + offset, &expired_now);
#     159         [ +  + ]:      58718 :             for (const auto& entry : expired_now) runner.expired.insert(entry);
#     160                 :      58718 :             runner.txrequest.SanityCheck();
#     161                 :      58718 :             runner.txrequest.PostGetRequestableSanityCheck(now + offset);
#     162                 :      58718 :             size_t total = candidates + inflight + completed;
#     163                 :      58718 :             size_t real_total = runner.txrequest.Count(peer);
#     164                 :      58718 :             size_t real_candidates = runner.txrequest.CountCandidates(peer);
#     165                 :      58718 :             size_t real_inflight = runner.txrequest.CountInFlight(peer);
#     166                 :      58718 :             BOOST_CHECK_MESSAGE(real_total == total, strprintf("[" + comment + "] total %i (%i expected)", real_total, total));
#     167                 :      58718 :             BOOST_CHECK_MESSAGE(real_inflight == inflight, strprintf("[" + comment + "] inflight %i (%i expected)", real_inflight, inflight));
#     168                 :      58718 :             BOOST_CHECK_MESSAGE(real_candidates == candidates, strprintf("[" + comment + "] candidates %i (%i expected)", real_candidates, candidates));
#     169                 :      58718 :             BOOST_CHECK_MESSAGE(ret == expected, "[" + comment + "] mismatching requestables");
#     170                 :      58718 :         });
#     171                 :      58718 :     }
#     172                 :            : 
#     173                 :            :     /** Verify that an announcement for gtxid by peer has expired some time before this check is scheduled.
#     174                 :            :      *
#     175                 :            :      * Every expected expiration should be accounted for through exactly one call to this function.
#     176                 :            :      */
#     177                 :            :     void CheckExpired(NodeId peer, GenTxid gtxid)
#     178                 :       2080 :     {
#     179                 :       2080 :         const auto& testname = m_testname;
#     180                 :       2080 :         auto& runner = m_runner;
#     181                 :       2080 :         runner.actions.emplace_back(m_now, [=,&runner]() {
#     182                 :       2080 :             auto it = runner.expired.find(std::pair<NodeId, GenTxid>{peer, gtxid});
#     183                 :       2080 :             BOOST_CHECK_MESSAGE(it != runner.expired.end(), "[" + testname + "] missing expiration");
#     184         [ +  - ]:       2080 :             if (it != runner.expired.end()) runner.expired.erase(it);
#     185                 :       2080 :         });
#     186                 :       2080 :     }
#     187                 :            : 
#     188                 :            :     /** Generate a random txhash, whose priorities for certain peers are constrained.
#     189                 :            :      *
#     190                 :            :      * For example, NewTxHash({{p1,p2,p3},{p2,p4,p5}}) will generate a txhash T such that both:
#     191                 :            :      *  - priority(p1,T) > priority(p2,T) > priority(p3,T)
#     192                 :            :      *  - priority(p2,T) > priority(p4,T) > priority(p5,T)
#     193                 :            :      * where priority is the predicted internal TxRequestTracker's priority, assuming all announcements
#     194                 :            :      * are within the same preferredness class.
#     195                 :            :      */
#     196                 :            :     uint256 NewTxHash(const std::vector<std::vector<NodeId>>& orders = {})
#     197                 :       5760 :     {
#     198                 :       5760 :         uint256 ret;
#     199                 :       5760 :         bool ok;
#     200                 :     370792 :         do {
#     201                 :     370792 :             ret = InsecureRand256();
#     202                 :     370792 :             ok = true;
#     203         [ +  + ]:     588930 :             for (const auto& order : orders) {
#     204         [ +  + ]:     856354 :                 for (size_t pos = 1; pos < order.size(); ++pos) {
#     205                 :     632456 :                     uint64_t prio_prev = m_runner.txrequest.ComputePriority(ret, order[pos - 1], true);
#     206                 :     632456 :                     uint64_t prio_cur = m_runner.txrequest.ComputePriority(ret, order[pos], true);
#     207         [ +  + ]:     632456 :                     if (prio_prev <= prio_cur) {
#     208                 :     365032 :                         ok = false;
#     209                 :     365032 :                         break;
#     210                 :     365032 :                     }
#     211                 :     632456 :                 }
#     212         [ +  + ]:     588930 :                 if (!ok) break;
#     213                 :     588930 :             }
#     214         [ +  + ]:     370792 :             if (ok) {
#     215                 :       5760 :                 ok = m_runner.txhashset.insert(ret).second;
#     216                 :       5760 :             }
#     217         [ +  + ]:     370792 :         } while(!ok);
#     218                 :       5760 :         return ret;
#     219                 :       5760 :     }
#     220                 :            : 
#     221                 :            :     /** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
#     222                 :            :     GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
#     223                 :       5120 :     {
#     224         [ +  + ]:       5120 :         return InsecureRandBool() ? GenTxid::Wtxid(NewTxHash(orders)) : GenTxid::Txid(NewTxHash(orders));
#     225                 :       5120 :     }
#     226                 :            : 
#     227                 :            :     /** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
#     228                 :            :      *  (across all Scenarios combined). */
#     229                 :            :     NodeId NewPeer()
#     230                 :       9280 :     {
#     231                 :       9280 :         bool ok;
#     232                 :       9280 :         NodeId ret;
#     233                 :       9280 :         do {
#     234                 :       9280 :             ret = InsecureRandBits(63);
#     235                 :       9280 :             ok = m_runner.peerset.insert(ret).second;
#     236         [ -  + ]:       9280 :         } while(!ok);
#     237                 :       9280 :         return ret;
#     238                 :       9280 :     }
#     239                 :            : 
#     240                 :      12414 :     std::chrono::microseconds Now() const { return m_now; }
#     241                 :            : };
#     242                 :            : 
#     243                 :            : /** Add to scenario a test with a single tx announced by a single peer.
#     244                 :            :  *
#     245                 :            :  * config is an integer in [0, 32), which controls which variant of the test is used.
#     246                 :            :  */
#     247                 :            : void BuildSingleTest(Scenario& scenario, int config)
#     248                 :        640 : {
#     249                 :        640 :     auto peer = scenario.NewPeer();
#     250                 :        640 :     auto gtxid = scenario.NewGTxid();
#     251                 :        640 :     bool immediate = config & 1;
#     252                 :        640 :     bool preferred = config & 2;
#     253         [ +  + ]:        640 :     auto delay = immediate ? NO_TIME : RandomTime8s();
#     254                 :            : 
#     255                 :        640 :     scenario.SetTestName(strprintf("Single(config=%i)", config));
#     256                 :            : 
#     257                 :            :     // Receive an announcement, either immediately requestable or delayed.
#     258         [ +  + ]:        640 :     scenario.ReceivedInv(peer, gtxid, preferred, immediate ? MIN_TIME : scenario.Now() + delay);
#     259         [ +  + ]:        640 :     if (immediate) {
#     260                 :        320 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s1");
#     261                 :        320 :     } else {
#     262                 :        320 :         scenario.Check(peer, {}, 1, 0, 0, "s2");
#     263                 :        320 :         scenario.AdvanceTime(delay - MICROSECOND);
#     264                 :        320 :         scenario.Check(peer, {}, 1, 0, 0, "s3");
#     265                 :        320 :         scenario.AdvanceTime(MICROSECOND);
#     266                 :        320 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s4");
#     267                 :        320 :     }
#     268                 :            : 
#     269         [ +  + ]:        640 :     if (config >> 3) { // We'll request the transaction
#     270                 :        480 :         scenario.AdvanceTime(RandomTime8s());
#     271                 :        480 :         auto expiry = RandomTime8s();
#     272                 :        480 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s5");
#     273                 :        480 :         scenario.RequestedTx(peer, gtxid.GetHash(), scenario.Now() + expiry);
#     274                 :        480 :         scenario.Check(peer, {}, 0, 1, 0, "s6");
#     275                 :            : 
#     276         [ +  + ]:        480 :         if ((config >> 3) == 1) { // The request will time out
#     277                 :        160 :             scenario.AdvanceTime(expiry - MICROSECOND);
#     278                 :        160 :             scenario.Check(peer, {}, 0, 1, 0, "s7");
#     279                 :        160 :             scenario.AdvanceTime(MICROSECOND);
#     280                 :        160 :             scenario.Check(peer, {}, 0, 0, 0, "s8");
#     281                 :        160 :             scenario.CheckExpired(peer, gtxid);
#     282                 :        160 :             return;
#     283                 :        320 :         } else {
#     284                 :        320 :             scenario.AdvanceTime(std::chrono::microseconds{InsecureRandRange(expiry.count())});
#     285                 :        320 :             scenario.Check(peer, {}, 0, 1, 0, "s9");
#     286         [ +  + ]:        320 :             if ((config >> 3) == 3) { // A response will arrive for the transaction
#     287                 :        160 :                 scenario.ReceivedResponse(peer, gtxid.GetHash());
#     288                 :        160 :                 scenario.Check(peer, {}, 0, 0, 0, "s10");
#     289                 :        160 :                 return;
#     290                 :        160 :             }
#     291                 :        320 :         }
#     292                 :        480 :     }
#     293                 :            : 
#     294         [ +  + ]:        320 :     if (config & 4) { // The peer will go offline
#     295                 :        160 :         scenario.DisconnectedPeer(peer);
#     296                 :        160 :     } else { // The transaction is no longer needed
#     297                 :        160 :         scenario.ForgetTxHash(gtxid.GetHash());
#     298                 :        160 :     }
#     299                 :        320 :     scenario.Check(peer, {}, 0, 0, 0, "s11");
#     300                 :        320 : }
#     301                 :            : 
#     302                 :            : /** Add to scenario a test with a single tx announced by two peers, to verify the
#     303                 :            :  *  right peer is selected for requests.
#     304                 :            :  *
#     305                 :            :  * config is an integer in [0, 32), which controls which variant of the test is used.
#     306                 :            :  */
#     307                 :            : void BuildPriorityTest(Scenario& scenario, int config)
#     308                 :        640 : {
#     309                 :        640 :     scenario.SetTestName(strprintf("Priority(config=%i)", config));
#     310                 :            : 
#     311                 :            :     // Two peers. They will announce in order {peer1, peer2}.
#     312                 :        640 :     auto peer1 = scenario.NewPeer(), peer2 = scenario.NewPeer();
#     313                 :            :     // Construct a transaction that under random rules would be preferred by peer2 or peer1,
#     314                 :            :     // depending on configuration.
#     315                 :        640 :     bool prio1 = config & 1;
#     316         [ +  + ]:        640 :     auto gtxid = prio1 ? scenario.NewGTxid({{peer1, peer2}}) : scenario.NewGTxid({{peer2, peer1}});
#     317                 :        640 :     bool pref1 = config & 2, pref2 = config & 4;
#     318                 :            : 
#     319                 :        640 :     scenario.ReceivedInv(peer1, gtxid, pref1, MIN_TIME);
#     320                 :        640 :     scenario.Check(peer1, {gtxid}, 1, 0, 0, "p1");
#     321         [ +  + ]:        640 :     if (InsecureRandBool()) {
#     322                 :        318 :         scenario.AdvanceTime(RandomTime8s());
#     323                 :        318 :         scenario.Check(peer1, {gtxid}, 1, 0, 0, "p2");
#     324                 :        318 :     }
#     325                 :            : 
#     326                 :        640 :     scenario.ReceivedInv(peer2, gtxid, pref2, MIN_TIME);
#     327                 :        640 :     bool stage2_prio =
#     328                 :            :         // At this point, peer2 will be given priority if:
#     329                 :            :         // - It is preferred and peer1 is not
#     330 [ +  + ][ +  + ]:        640 :         (pref2 && !pref1) ||
#     331                 :            :         // - They're in the same preference class,
#     332                 :            :         //   and the randomized priority favors peer2 over peer1.
#     333 [ +  + ][ +  + ]:        640 :         (pref1 == pref2 && !prio1);
#     334 [ +  + ][ +  + ]:        640 :     NodeId priopeer = stage2_prio ? peer2 : peer1, otherpeer = stage2_prio ? peer1 : peer2;
#     335                 :        640 :     scenario.Check(otherpeer, {}, 1, 0, 0, "p3");
#     336                 :        640 :     scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p4");
#     337         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     338                 :        640 :     scenario.Check(otherpeer, {}, 1, 0, 0, "p5");
#     339                 :        640 :     scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p6");
#     340                 :            : 
#     341                 :            :     // We possibly request from the selected peer.
#     342         [ +  + ]:        640 :     if (config & 8) {
#     343                 :        320 :         scenario.RequestedTx(priopeer, gtxid.GetHash(), MAX_TIME);
#     344                 :        320 :         scenario.Check(priopeer, {}, 0, 1, 0, "p7");
#     345                 :        320 :         scenario.Check(otherpeer, {}, 1, 0, 0, "p8");
#     346         [ +  + ]:        320 :         if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     347                 :        320 :     }
#     348                 :            : 
#     349                 :            :     // The peer which was selected (or requested from) now goes offline, or a NOTFOUND is received from them.
#     350         [ +  + ]:        640 :     if (config & 16) {
#     351                 :        320 :         scenario.DisconnectedPeer(priopeer);
#     352                 :        320 :     } else {
#     353                 :        320 :         scenario.ReceivedResponse(priopeer, gtxid.GetHash());
#     354                 :        320 :     }
#     355         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     356                 :        640 :     scenario.Check(priopeer, {}, 0, 0, !(config & 16), "p8");
#     357                 :        640 :     scenario.Check(otherpeer, {gtxid}, 1, 0, 0, "p9");
#     358         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     359                 :            : 
#     360                 :            :     // Now the other peer goes offline.
#     361                 :        640 :     scenario.DisconnectedPeer(otherpeer);
#     362         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     363                 :        640 :     scenario.Check(peer1, {}, 0, 0, 0, "p10");
#     364                 :        640 :     scenario.Check(peer2, {}, 0, 0, 0, "p11");
#     365                 :        640 : }
#     366                 :            : 
#     367                 :            : /** Add to scenario a randomized test in which N peers announce the same transaction, to verify
#     368                 :            :  *  the order in which they are requested. */
#     369                 :            : void BuildBigPriorityTest(Scenario& scenario, int peers)
#     370                 :        640 : {
#     371                 :        640 :     scenario.SetTestName(strprintf("BigPriority(peers=%i)", peers));
#     372                 :            : 
#     373                 :            :     // We will have N peers announce the same transaction.
#     374                 :        640 :     std::map<NodeId, bool> preferred;
#     375                 :        640 :     std::vector<NodeId> pref_peers, npref_peers;
#     376                 :        640 :     int num_pref = InsecureRandRange(peers + 1) ; // Some preferred, ...
#     377                 :        640 :     int num_npref = peers - num_pref; // some not preferred.
#     378         [ +  + ]:       2106 :     for (int i = 0; i < num_pref; ++i) {
#     379                 :       1466 :         pref_peers.push_back(scenario.NewPeer());
#     380                 :       1466 :         preferred[pref_peers.back()] = true;
#     381                 :       1466 :     }
#     382         [ +  + ]:       2054 :     for (int i = 0; i < num_npref; ++i) {
#     383                 :       1414 :         npref_peers.push_back(scenario.NewPeer());
#     384                 :       1414 :         preferred[npref_peers.back()] = false;
#     385                 :       1414 :     }
#     386                 :            :     // Make a list of all peers, in order of intended request order (concatenation of pref_peers and npref_peers).
#     387                 :        640 :     std::vector<NodeId> request_order;
#     388         [ +  + ]:       2106 :     for (int i = 0; i < num_pref; ++i) request_order.push_back(pref_peers[i]);
#     389         [ +  + ]:       2054 :     for (int i = 0; i < num_npref; ++i) request_order.push_back(npref_peers[i]);
#     390                 :            : 
#     391                 :            :     // Determine the announcement order randomly.
#     392                 :        640 :     std::vector<NodeId> announce_order = request_order;
#     393                 :        640 :     Shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
#     394                 :            : 
#     395                 :            :     // Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
#     396                 :            :     // within npref_peers.
#     397                 :        640 :     auto gtxid = scenario.NewGTxid({pref_peers, npref_peers});
#     398                 :            : 
#     399                 :            :     // Decide reqtimes in opposite order of the expected request order. This means that as time passes we expect the
#     400                 :            :     // to-be-requested-from-peer will change every time a subsequent reqtime is passed.
#     401                 :        640 :     std::map<NodeId, std::chrono::microseconds> reqtimes;
#     402                 :        640 :     auto reqtime = scenario.Now();
#     403         [ +  + ]:       3520 :     for (int i = peers - 1; i >= 0; --i) {
#     404                 :       2880 :         reqtime += RandomTime8s();
#     405                 :       2880 :         reqtimes[request_order[i]] = reqtime;
#     406                 :       2880 :     }
#     407                 :            : 
#     408                 :            :     // Actually announce from all peers simultaneously (but in announce_order).
#     409         [ +  + ]:       2880 :     for (const auto peer : announce_order) {
#     410                 :       2880 :         scenario.ReceivedInv(peer, gtxid, preferred[peer], reqtimes[peer]);
#     411                 :       2880 :     }
#     412         [ +  + ]:       2880 :     for (const auto peer : announce_order) {
#     413                 :       2880 :         scenario.Check(peer, {}, 1, 0, 0, "b1");
#     414                 :       2880 :     }
#     415                 :            : 
#     416                 :            :     // Let time pass and observe the to-be-requested-from peer change, from nonpreferred to preferred, and from
#     417                 :            :     // high priority to low priority within each class.
#     418         [ +  + ]:       3520 :     for (int i = peers - 1; i >= 0; --i) {
#     419                 :       2880 :         scenario.AdvanceTime(reqtimes[request_order[i]] - scenario.Now() - MICROSECOND);
#     420                 :       2880 :         scenario.Check(request_order[i], {}, 1, 0, 0, "b2");
#     421                 :       2880 :         scenario.AdvanceTime(MICROSECOND);
#     422                 :       2880 :         scenario.Check(request_order[i], {gtxid}, 1, 0, 0, "b3");
#     423                 :       2880 :     }
#     424                 :            : 
#     425                 :            :     // Peers now in random order go offline, or send NOTFOUNDs. At every point in time the new to-be-requested-from
#     426                 :            :     // peer should be the best remaining one, so verify this after every response.
#     427         [ +  + ]:       3520 :     for (int i = 0; i < peers; ++i) {
#     428         [ +  + ]:       2880 :         if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     429                 :       2880 :         const int pos = InsecureRandRange(request_order.size());
#     430                 :       2880 :         const auto peer = request_order[pos];
#     431                 :       2880 :         request_order.erase(request_order.begin() + pos);
#     432         [ +  + ]:       2880 :         if (InsecureRandBool()) {
#     433                 :       1462 :             scenario.DisconnectedPeer(peer);
#     434                 :       1462 :             scenario.Check(peer, {}, 0, 0, 0, "b4");
#     435                 :       1462 :         } else {
#     436                 :       1418 :             scenario.ReceivedResponse(peer, gtxid.GetHash());
#     437                 :       1418 :             scenario.Check(peer, {}, 0, 0, request_order.size() > 0, "b5");
#     438                 :       1418 :         }
#     439         [ +  + ]:       2880 :         if (request_order.size()) {
#     440                 :       2240 :             scenario.Check(request_order[0], {gtxid}, 1, 0, 0, "b6");
#     441                 :       2240 :         }
#     442                 :       2880 :     }
#     443                 :            : 
#     444                 :            :     // Everything is gone in the end.
#     445         [ +  + ]:       2880 :     for (const auto peer : announce_order) {
#     446                 :       2880 :         scenario.Check(peer, {}, 0, 0, 0, "b7");
#     447                 :       2880 :     }
#     448                 :        640 : }
#     449                 :            : 
#     450                 :            : /** Add to scenario a test with one peer announcing two transactions, to verify they are
#     451                 :            :  *  fetched in announcement order.
#     452                 :            :  *
#     453                 :            :  *  config is an integer in [0, 4) inclusive, and selects the variant of the test.
#     454                 :            :  */
#     455                 :            : void BuildRequestOrderTest(Scenario& scenario, int config)
#     456                 :        640 : {
#     457                 :        640 :     scenario.SetTestName(strprintf("RequestOrder(config=%i)", config));
#     458                 :            : 
#     459                 :        640 :     auto peer = scenario.NewPeer();
#     460                 :        640 :     auto gtxid1 = scenario.NewGTxid();
#     461                 :        640 :     auto gtxid2 = scenario.NewGTxid();
#     462                 :            : 
#     463                 :        640 :     auto reqtime2 = scenario.Now() + RandomTime8s();
#     464                 :        640 :     auto reqtime1 = reqtime2 + RandomTime8s();
#     465                 :            : 
#     466                 :        640 :     scenario.ReceivedInv(peer, gtxid1, config & 1, reqtime1);
#     467                 :            :     // Simulate time going backwards by giving the second announcement an earlier reqtime.
#     468                 :        640 :     scenario.ReceivedInv(peer, gtxid2, config & 2, reqtime2);
#     469                 :            : 
#     470                 :        640 :     scenario.AdvanceTime(reqtime2 - MICROSECOND - scenario.Now());
#     471                 :        640 :     scenario.Check(peer, {}, 2, 0, 0, "o1");
#     472                 :        640 :     scenario.AdvanceTime(MICROSECOND);
#     473                 :        640 :     scenario.Check(peer, {gtxid2}, 2, 0, 0, "o2");
#     474                 :        640 :     scenario.AdvanceTime(reqtime1 - MICROSECOND - scenario.Now());
#     475                 :        640 :     scenario.Check(peer, {gtxid2}, 2, 0, 0, "o3");
#     476                 :        640 :     scenario.AdvanceTime(MICROSECOND);
#     477                 :            :     // Even with time going backwards in between announcements, the return value of GetRequestable is in
#     478                 :            :     // announcement order.
#     479                 :        640 :     scenario.Check(peer, {gtxid1, gtxid2}, 2, 0, 0, "o4");
#     480                 :            : 
#     481                 :        640 :     scenario.DisconnectedPeer(peer);
#     482                 :        640 :     scenario.Check(peer, {}, 0, 0, 0, "o5");
#     483                 :        640 : }
#     484                 :            : 
#     485                 :            : /** Add to scenario a test that verifies behavior related to both txid and wtxid with the same
#     486                 :            :  *  hash being announced.
#     487                 :            :  *
#     488                 :            :  *  config is an integer in [0, 4) inclusive, and selects the variant of the test used.
#     489                 :            : */
#     490                 :            : void BuildWtxidTest(Scenario& scenario, int config)
#     491                 :        640 : {
#     492                 :        640 :     scenario.SetTestName(strprintf("Wtxid(config=%i)", config));
#     493                 :            : 
#     494                 :        640 :     auto peerT = scenario.NewPeer();
#     495                 :        640 :     auto peerW = scenario.NewPeer();
#     496                 :        640 :     auto txhash = scenario.NewTxHash();
#     497                 :        640 :     auto txid{GenTxid::Txid(txhash)};
#     498                 :        640 :     auto wtxid{GenTxid::Wtxid(txhash)};
#     499                 :            : 
#     500         [ +  + ]:        640 :     auto reqtimeT = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
#     501         [ +  + ]:        640 :     auto reqtimeW = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
#     502                 :            : 
#     503                 :            :     // Announce txid first or wtxid first.
#     504         [ +  + ]:        640 :     if (config & 1) {
#     505                 :        320 :         scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
#     506         [ +  + ]:        320 :         if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     507                 :        320 :         scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
#     508                 :        320 :     } else {
#     509                 :        320 :         scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
#     510         [ +  + ]:        320 :         if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     511                 :        320 :         scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
#     512                 :        320 :     }
#     513                 :            : 
#     514                 :            :     // Let time pass if needed, and check that the preferred announcement (txid or wtxid)
#     515                 :            :     // is correctly to-be-requested (and with the correct wtxidness).
#     516                 :        640 :     auto max_reqtime = std::max(reqtimeT, reqtimeW);
#     517         [ +  + ]:        640 :     if (max_reqtime > scenario.Now()) scenario.AdvanceTime(max_reqtime - scenario.Now());
#     518         [ +  + ]:        640 :     if (config & 2) {
#     519                 :        320 :         scenario.Check(peerT, {txid}, 1, 0, 0, "w1");
#     520                 :        320 :         scenario.Check(peerW, {}, 1, 0, 0, "w2");
#     521                 :        320 :     } else {
#     522                 :        320 :         scenario.Check(peerT, {}, 1, 0, 0, "w3");
#     523                 :        320 :         scenario.Check(peerW, {wtxid}, 1, 0, 0, "w4");
#     524                 :        320 :     }
#     525                 :            : 
#     526                 :            :     // Let the preferred announcement be requested. It's not going to be delivered.
#     527                 :        640 :     auto expiry = RandomTime8s();
#     528         [ +  + ]:        640 :     if (config & 2) {
#     529                 :        320 :         scenario.RequestedTx(peerT, txid.GetHash(), scenario.Now() + expiry);
#     530                 :        320 :         scenario.Check(peerT, {}, 0, 1, 0, "w5");
#     531                 :        320 :         scenario.Check(peerW, {}, 1, 0, 0, "w6");
#     532                 :        320 :     } else {
#     533                 :        320 :         scenario.RequestedTx(peerW, wtxid.GetHash(), scenario.Now() + expiry);
#     534                 :        320 :         scenario.Check(peerT, {}, 1, 0, 0, "w7");
#     535                 :        320 :         scenario.Check(peerW, {}, 0, 1, 0, "w8");
#     536                 :        320 :     }
#     537                 :            : 
#     538                 :            :     // After reaching expiration time of the preferred announcement, verify that the
#     539                 :            :     // remaining one is requestable
#     540                 :        640 :     scenario.AdvanceTime(expiry);
#     541         [ +  + ]:        640 :     if (config & 2) {
#     542                 :        320 :         scenario.Check(peerT, {}, 0, 0, 1, "w9");
#     543                 :        320 :         scenario.Check(peerW, {wtxid}, 1, 0, 0, "w10");
#     544                 :        320 :         scenario.CheckExpired(peerT, txid);
#     545                 :        320 :     } else {
#     546                 :        320 :         scenario.Check(peerT, {txid}, 1, 0, 0, "w11");
#     547                 :        320 :         scenario.Check(peerW, {}, 0, 0, 1, "w12");
#     548                 :        320 :         scenario.CheckExpired(peerW, wtxid);
#     549                 :        320 :     }
#     550                 :            : 
#     551                 :            :     // If a good transaction with either that hash as wtxid or txid arrives, both
#     552                 :            :     // announcements are gone.
#     553         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     554                 :        640 :     scenario.ForgetTxHash(txhash);
#     555                 :        640 :     scenario.Check(peerT, {}, 0, 0, 0, "w13");
#     556                 :        640 :     scenario.Check(peerW, {}, 0, 0, 0, "w14");
#     557                 :        640 : }
#     558                 :            : 
#     559                 :            : /** Add to scenario a test that exercises clocks that go backwards. */
#     560                 :            : void BuildTimeBackwardsTest(Scenario& scenario)
#     561                 :        640 : {
#     562                 :        640 :     auto peer1 = scenario.NewPeer();
#     563                 :        640 :     auto peer2 = scenario.NewPeer();
#     564                 :        640 :     auto gtxid = scenario.NewGTxid({{peer1, peer2}});
#     565                 :            : 
#     566                 :            :     // Announce from peer2.
#     567                 :        640 :     auto reqtime = scenario.Now() + RandomTime8s();
#     568                 :        640 :     scenario.ReceivedInv(peer2, gtxid, true, reqtime);
#     569                 :        640 :     scenario.Check(peer2, {}, 1, 0, 0, "r1");
#     570                 :        640 :     scenario.AdvanceTime(reqtime - scenario.Now());
#     571                 :        640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r2");
#     572                 :            :     // Check that if the clock goes backwards by 1us, the transaction would stop being requested.
#     573                 :        640 :     scenario.Check(peer2, {}, 1, 0, 0, "r3", -MICROSECOND);
#     574                 :            :     // But it reverts to being requested if time goes forward again.
#     575                 :        640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r4");
#     576                 :            : 
#     577                 :            :     // Announce from peer1.
#     578         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     579                 :        640 :     scenario.ReceivedInv(peer1, gtxid, true, MAX_TIME);
#     580                 :        640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r5");
#     581                 :        640 :     scenario.Check(peer1, {}, 1, 0, 0, "r6");
#     582                 :            : 
#     583                 :            :     // Request from peer1.
#     584         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     585                 :        640 :     auto expiry = scenario.Now() + RandomTime8s();
#     586                 :        640 :     scenario.RequestedTx(peer1, gtxid.GetHash(), expiry);
#     587                 :        640 :     scenario.Check(peer1, {}, 0, 1, 0, "r7");
#     588                 :        640 :     scenario.Check(peer2, {}, 1, 0, 0, "r8");
#     589                 :            : 
#     590                 :            :     // Expiration passes.
#     591                 :        640 :     scenario.AdvanceTime(expiry - scenario.Now());
#     592                 :        640 :     scenario.Check(peer1, {}, 0, 0, 1, "r9");
#     593                 :        640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r10"); // Request goes back to peer2.
#     594                 :        640 :     scenario.CheckExpired(peer1, gtxid);
#     595                 :        640 :     scenario.Check(peer1, {}, 0, 0, 1, "r11", -MICROSECOND); // Going back does not unexpire.
#     596                 :        640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r12", -MICROSECOND);
#     597                 :            : 
#     598                 :            :     // Peer2 goes offline, meaning no viable announcements remain.
#     599         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     600                 :        640 :     scenario.DisconnectedPeer(peer2);
#     601                 :        640 :     scenario.Check(peer1, {}, 0, 0, 0, "r13");
#     602                 :        640 :     scenario.Check(peer2, {}, 0, 0, 0, "r14");
#     603                 :        640 : }
#     604                 :            : 
#     605                 :            : /** Add to scenario a test that involves RequestedTx() calls for txhashes not returned by GetRequestable. */
#     606                 :            : void BuildWeirdRequestsTest(Scenario& scenario)
#     607                 :        640 : {
#     608                 :        640 :     auto peer1 = scenario.NewPeer();
#     609                 :        640 :     auto peer2 = scenario.NewPeer();
#     610                 :        640 :     auto gtxid1 = scenario.NewGTxid({{peer1, peer2}});
#     611                 :        640 :     auto gtxid2 = scenario.NewGTxid({{peer2, peer1}});
#     612                 :            : 
#     613                 :            :     // Announce gtxid1 by peer1.
#     614                 :        640 :     scenario.ReceivedInv(peer1, gtxid1, true, MIN_TIME);
#     615                 :        640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q1");
#     616                 :            : 
#     617                 :            :     // Announce gtxid2 by peer2.
#     618         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     619                 :        640 :     scenario.ReceivedInv(peer2, gtxid2, true, MIN_TIME);
#     620                 :        640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q2");
#     621                 :        640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q3");
#     622                 :            : 
#     623                 :            :     // We request gtxid2 from *peer1* - no effect.
#     624         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     625                 :        640 :     scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
#     626                 :        640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q4");
#     627                 :        640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q5");
#     628                 :            : 
#     629                 :            :     // Now request gtxid1 from peer1 - marks it as REQUESTED.
#     630         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     631                 :        640 :     auto expiryA = scenario.Now() + RandomTime8s();
#     632                 :        640 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryA);
#     633                 :        640 :     scenario.Check(peer1, {}, 0, 1, 0, "q6");
#     634                 :        640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q7");
#     635                 :            : 
#     636                 :            :     // Request it a second time - nothing happens, as it's already REQUESTED.
#     637                 :        640 :     auto expiryB = expiryA + RandomTime8s();
#     638                 :        640 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryB);
#     639                 :        640 :     scenario.Check(peer1, {}, 0, 1, 0, "q8");
#     640                 :        640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q9");
#     641                 :            : 
#     642                 :            :     // Also announce gtxid1 from peer2 now, so that the txhash isn't forgotten when the peer1 request expires.
#     643                 :        640 :     scenario.ReceivedInv(peer2, gtxid1, true, MIN_TIME);
#     644                 :        640 :     scenario.Check(peer1, {}, 0, 1, 0, "q10");
#     645                 :        640 :     scenario.Check(peer2, {gtxid2}, 2, 0, 0, "q11");
#     646                 :            : 
#     647                 :            :     // When reaching expiryA, it expires (not expiryB, which is later).
#     648                 :        640 :     scenario.AdvanceTime(expiryA - scenario.Now());
#     649                 :        640 :     scenario.Check(peer1, {}, 0, 0, 1, "q12");
#     650                 :        640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q13");
#     651                 :        640 :     scenario.CheckExpired(peer1, gtxid1);
#     652                 :            : 
#     653                 :            :     // Requesting it yet again from peer1 doesn't do anything, as it's already COMPLETED.
#     654         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     655                 :        640 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), MAX_TIME);
#     656                 :        640 :     scenario.Check(peer1, {}, 0, 0, 1, "q14");
#     657                 :        640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q15");
#     658                 :            : 
#     659                 :            :     // Now announce gtxid2 from peer1.
#     660         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     661                 :        640 :     scenario.ReceivedInv(peer1, gtxid2, true, MIN_TIME);
#     662                 :        640 :     scenario.Check(peer1, {}, 1, 0, 1, "q16");
#     663                 :        640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q17");
#     664                 :            : 
#     665                 :            :     // And request it from peer1 (weird as peer2 has the preference).
#     666         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     667                 :        640 :     scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
#     668                 :        640 :     scenario.Check(peer1, {}, 0, 1, 1, "q18");
#     669                 :        640 :     scenario.Check(peer2, {gtxid1}, 2, 0, 0, "q19");
#     670                 :            : 
#     671                 :            :     // If peer2 now (normally) requests gtxid2, the existing request by peer1 becomes COMPLETED.
#     672         [ +  + ]:        640 :     if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
#     673                 :        640 :     scenario.RequestedTx(peer2, gtxid2.GetHash(), MAX_TIME);
#     674                 :        640 :     scenario.Check(peer1, {}, 0, 0, 2, "q20");
#     675                 :        640 :     scenario.Check(peer2, {gtxid1}, 1, 1, 0, "q21");
#     676                 :            : 
#     677                 :            :     // If peer2 goes offline, no viable announcements remain.
#     678                 :        640 :     scenario.DisconnectedPeer(peer2);
#     679                 :        640 :     scenario.Check(peer1, {}, 0, 0, 0, "q22");
#     680                 :        640 :     scenario.Check(peer2, {}, 0, 0, 0, "q23");
#     681                 :        640 : }
#     682                 :            : 
#     683                 :            : void TestInterleavedScenarios()
#     684                 :         10 : {
#     685                 :            :     // Create a list of functions which add tests to scenarios.
#     686                 :         10 :     std::vector<std::function<void(Scenario&)>> builders;
#     687                 :            :     // Add instances of every test, for every configuration.
#     688         [ +  + ]:        650 :     for (int n = 0; n < 64; ++n) {
#     689                 :        640 :         builders.emplace_back([n](Scenario& scenario){ BuildWtxidTest(scenario, n); });
#     690                 :        640 :         builders.emplace_back([n](Scenario& scenario){ BuildRequestOrderTest(scenario, n & 3); });
#     691                 :        640 :         builders.emplace_back([n](Scenario& scenario){ BuildSingleTest(scenario, n & 31); });
#     692                 :        640 :         builders.emplace_back([n](Scenario& scenario){ BuildPriorityTest(scenario, n & 31); });
#     693                 :        640 :         builders.emplace_back([n](Scenario& scenario){ BuildBigPriorityTest(scenario, (n & 7) + 1); });
#     694                 :        640 :         builders.emplace_back([](Scenario& scenario){ BuildTimeBackwardsTest(scenario); });
#     695                 :        640 :         builders.emplace_back([](Scenario& scenario){ BuildWeirdRequestsTest(scenario); });
#     696                 :        640 :     }
#     697                 :            :     // Randomly shuffle all those functions.
#     698                 :         10 :     Shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
#     699                 :            : 
#     700                 :         10 :     Runner runner;
#     701                 :         10 :     auto starttime = RandomTime1y();
#     702                 :            :     // Construct many scenarios, and run (up to) 10 randomly-chosen tests consecutively in each.
#     703         [ +  + ]:        460 :     while (builders.size()) {
#     704                 :            :         // Introduce some variation in the start time of each scenario, so they don't all start off
#     705                 :            :         // concurrently, but get a more random interleaving.
#     706                 :        450 :         auto scenario_start = starttime + RandomTime8s() + RandomTime8s() + RandomTime8s();
#     707                 :        450 :         Scenario scenario(runner, scenario_start);
#     708 [ +  + ][ +  + ]:       4930 :         for (int j = 0; builders.size() && j < 10; ++j) {
#     709                 :       4480 :             builders.back()(scenario);
#     710                 :       4480 :             builders.pop_back();
#     711                 :       4480 :         }
#     712                 :        450 :     }
#     713                 :            :     // Sort all the actions from all those scenarios chronologically, resulting in the actions from
#     714                 :            :     // distinct scenarios to become interleaved. Use stable_sort so that actions from one scenario
#     715                 :            :     // aren't reordered w.r.t. each other.
#     716                 :     784290 :     std::stable_sort(runner.actions.begin(), runner.actions.end(), [](const Action& a1, const Action& a2) {
#     717                 :     784290 :         return a1.first < a2.first;
#     718                 :     784290 :     });
#     719                 :            : 
#     720                 :            :     // Run all actions from all scenarios, in order.
#     721         [ +  + ]:      85118 :     for (auto& action : runner.actions) {
#     722                 :      85118 :         action.second();
#     723                 :      85118 :     }
#     724                 :            : 
#     725                 :         10 :     BOOST_CHECK_EQUAL(runner.txrequest.Size(), 0U);
#     726                 :         10 :     BOOST_CHECK(runner.expired.empty());
#     727                 :         10 : }
#     728                 :            : 
#     729                 :            : }  // namespace
#     730                 :            : 
#     731                 :            : BOOST_AUTO_TEST_CASE(TxRequestTest)
#     732                 :          2 : {
#     733         [ +  + ]:         12 :     for (int i = 0; i < 5; ++i) {
#     734                 :         10 :         TestInterleavedScenarios();
#     735                 :         10 :     }
#     736                 :          2 : }
#     737                 :            : 
#     738                 :            : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a