#include #include #include #include #include #include #include namespace xrpl { namespace PeerFinder { bool operator==(Endpoint const& a, Endpoint const& b) { return (a.hops == b.hops && a.address == b.address); } class Livecache_test : public beast::unit_test::suite { TestStopwatch clock_; test::SuiteJournal journal_; public: Livecache_test() : journal_("Livecache_test", *this) { } // Add the address as an endpoint template inline void add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) { Endpoint cep{ep, hops}; c.insert(cep); } void testBasicInsert() { testcase("Basic Insert"); Livecache<> c(clock_, journal_); BEAST_EXPECT(c.empty()); for (auto i = 0; i < 10; ++i) add(beast::IP::randomEP(true), c); BEAST_EXPECT(!c.empty()); BEAST_EXPECT(c.size() == 10); for (auto i = 0; i < 10; ++i) add(beast::IP::randomEP(false), c); BEAST_EXPECT(!c.empty()); BEAST_EXPECT(c.size() == 20); } void testInsertUpdate() { testcase("Insert/Update"); Livecache<> c(clock_, journal_); auto ep1 = Endpoint{beast::IP::randomEP(), 2}; c.insert(ep1); BEAST_EXPECT(c.size() == 1); // third position list will contain the entry BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2); auto ep2 = Endpoint{ep1.address, 4}; // this will not change the entry has higher hops c.insert(ep2); BEAST_EXPECT(c.size() == 1); // still in third position list BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2); auto ep3 = Endpoint{ep1.address, 2}; // this will not change the entry has the same hops as existing c.insert(ep3); BEAST_EXPECT(c.size() == 1); // still in third position list BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2); auto ep4 = Endpoint{ep1.address, 1}; c.insert(ep4); BEAST_EXPECT(c.size() == 1); // now at second position list BEAST_EXPECT((c.hops.begin() + 1)->begin()->hops == 1); } void testExpire() { testcase("Expire"); using namespace std::chrono_literals; Livecache<> c(clock_, journal_); auto ep1 = Endpoint{beast::IP::randomEP(), 1}; c.insert(ep1); BEAST_EXPECT(c.size() == 1); c.expire(); BEAST_EXPECT(c.size() == 1); // verify that advancing to 1 sec before expiration // leaves our entry intact clock_.advance(Tuning::liveCacheSecondsToLive - 1s); c.expire(); BEAST_EXPECT(c.size() == 1); // now advance to the point of expiration clock_.advance(1s); c.expire(); BEAST_EXPECT(c.empty()); } void testHistogram() { testcase("Histogram"); constexpr auto num_eps = 40; Livecache<> c(clock_, journal_); for (auto i = 0; i < num_eps; ++i) add(beast::IP::randomEP(true), c, xrpl::rand_int()); auto h = c.hops.histogram(); if (!BEAST_EXPECT(!h.empty())) return; std::vector v; boost::split(v, h, boost::algorithm::is_any_of(",")); auto sum = 0; for (auto const& n : v) { auto val = boost::lexical_cast(boost::trim_copy(n)); sum += val; BEAST_EXPECT(val >= 0); } BEAST_EXPECT(sum == num_eps); } void testShuffle() { testcase("Shuffle"); Livecache<> c(clock_, journal_); for (auto i = 0; i < 100; ++i) add(beast::IP::randomEP(true), c, xrpl::rand_int(Tuning::maxHops + 1)); using at_hop = std::vector; using all_hops = std::array; auto cmp_EP = [](Endpoint const& a, Endpoint const& b) { return ( b.hops < a.hops || (b.hops == a.hops && b.address < a.address)); }; all_hops before; all_hops before_sorted; for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); ++i.first, ++i.second) { std::copy( (*i.second).begin(), (*i.second).end(), std::back_inserter(before[i.first])); std::copy( (*i.second).begin(), (*i.second).end(), std::back_inserter(before_sorted[i.first])); std::sort( before_sorted[i.first].begin(), before_sorted[i.first].end(), cmp_EP); } c.hops.shuffle(); all_hops after; all_hops after_sorted; for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end(); ++i.first, ++i.second) { std::copy( (*i.second).begin(), (*i.second).end(), std::back_inserter(after[i.first])); std::copy( (*i.second).begin(), (*i.second).end(), std::back_inserter(after_sorted[i.first])); std::sort( after_sorted[i.first].begin(), after_sorted[i.first].end(), cmp_EP); } // each hop bucket should contain the same items // before and after sort, albeit in different order bool all_match = true; for (auto i = 0; i < before.size(); ++i) { BEAST_EXPECT(before[i].size() == after[i].size()); all_match = all_match && (before[i] == after[i]); BEAST_EXPECT(before_sorted[i] == after_sorted[i]); } BEAST_EXPECT(!all_match); } void run() override { testBasicInsert(); testInsertUpdate(); testExpire(); testHistogram(); testShuffle(); } }; BEAST_DEFINE_TESTSUITE(Livecache, peerfinder, xrpl); } // namespace PeerFinder } // namespace xrpl