Files
rippled/src/test/peerfinder/Livecache_test.cpp
Bart 1eb0fdac65 refactor: Rename ripple namespace to xrpl (#5982)
This change renames all occurrences of `namespace ripple` and `ripple::` to `namespace xrpl` and `xrpl::`, respectively, as well as the names of test suites. It also provides a script to allow developers to replicate the changes in their local branch or fork to avoid conflicts.
2025-12-11 16:51:49 +00:00

225 lines
6.3 KiB
C++

#include <test/beast/IPEndpointCommon.h>
#include <test/unit_test/SuiteJournal.h>
#include <xrpld/peerfinder/detail/Livecache.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/clock/manual_clock.h>
#include <xrpl/beast/unit_test.h>
#include <boost/algorithm/string.hpp>
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 <class C>
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<std::uint32_t>());
auto h = c.hops.histogram();
if (!BEAST_EXPECT(!h.empty()))
return;
std::vector<std::string> v;
boost::split(v, h, boost::algorithm::is_any_of(","));
auto sum = 0;
for (auto const& n : v)
{
auto val = boost::lexical_cast<int>(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<xrpl::PeerFinder::Endpoint>;
using all_hops = std::array<at_hop, 1 + Tuning::maxHops + 1>;
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