From 8a7dd0fc85d3f3d53a6c842163e587a12583f38f Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Tue, 5 May 2026 15:28:38 +0200 Subject: [PATCH 1/7] test: Copy peerfinder Beast tests for migration to GTest --- .../libxrpl/peerfinder/Livecache_test.cpp | 214 +++++++ .../libxrpl/peerfinder/PeerFinder_test.cpp | 532 ++++++++++++++++++ 2 files changed, 746 insertions(+) create mode 100644 src/tests/libxrpl/peerfinder/Livecache_test.cpp create mode 100644 src/tests/libxrpl/peerfinder/PeerFinder_test.cpp diff --git a/src/tests/libxrpl/peerfinder/Livecache_test.cpp b/src/tests/libxrpl/peerfinder/Livecache_test.cpp new file mode 100644 index 00000000000..28d5beef887 --- /dev/null +++ b/src/tests/libxrpl/peerfinder/Livecache_test.cpp @@ -0,0 +1,214 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace xrpl::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 + void + add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) + { + Endpoint const 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::kLIVE_CACHE_SECONDS_TO_LIVE - 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 kNUM_EPS = 40; + Livecache<> c(clock_, journal_); + for (auto i = 0; i < kNUM_EPS; ++i) + add(beast::IP::randomEP(true), c, xrpl::randInt()); + 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 == kNUM_EPS); + } + + void + testShuffle() + { + testcase("Shuffle"); + Livecache<> c(clock_, journal_); + for (auto i = 0; i < 100; ++i) + add(beast::IP::randomEP(true), c, xrpl::randInt(Tuning::kMAX_HOPS + 1)); + + using at_hop = std::vector; + using all_hops = std::array; + + auto cmpEp = [](Endpoint const& a, Endpoint const& b) { + return (b.hops < a.hops || (b.hops == a.hops && b.address < a.address)); + }; + all_hops before; + all_hops beforeSorted; + 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(beforeSorted[i.first])); + std::sort(beforeSorted[i.first].begin(), beforeSorted[i.first].end(), cmpEp); + } + + c.hops.shuffle(); + + all_hops after; + all_hops afterSorted; + 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(afterSorted[i.first])); + std::sort(afterSorted[i.first].begin(), afterSorted[i.first].end(), cmpEp); + } + + // each hop bucket should contain the same items + // before and after sort, albeit in different order + bool allMatch = true; + for (auto i = 0; i < before.size(); ++i) + { + BEAST_EXPECT(before[i].size() == after[i].size()); + allMatch = allMatch && (before[i] == after[i]); + BEAST_EXPECT(beforeSorted[i] == afterSorted[i]); + } + BEAST_EXPECT(!allMatch); + } + + void + run() override + { + testBasicInsert(); + testInsertUpdate(); + testExpire(); + testHistogram(); + testShuffle(); + } +}; + +BEAST_DEFINE_TESTSUITE(Livecache, peerfinder, xrpl); + +} // namespace xrpl::PeerFinder diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp new file mode 100644 index 00000000000..17ce6835ef9 --- /dev/null +++ b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp @@ -0,0 +1,532 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::PeerFinder { + +class PeerFinder_test : public beast::unit_test::Suite +{ + test::SuiteJournal journal_; + +public: + PeerFinder_test() : journal_("PeerFinder_test", *this) + { + } + + struct TestStore : Store + { + std::size_t + load(load_callback const& cb) override + { + return 0; + } + + void + save(std::vector const&) override + { + } + }; + + struct TestChecker + { + void + stop() + { + } + + void + wait() + { + } + + template + void + asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler) + { + boost::system::error_code ec; + handler(ep, ep, ec); + } + }; + + void + testBackoff1() + { + auto const seconds = 10000; + testcase("backoff 1"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + logic.config(c); + } + std::size_t n = 0; + for (std::size_t i = 0; i < seconds; ++i) + { + auto const list = logic.autoconnect(); + if (!list.empty()) + { + BEAST_EXPECT(list.size() == 1); + auto const [slot, _] = logic.newOutboundSlot(list.front()); + BEAST_EXPECT( + logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5"))); + logic.onClosed(slot); + ++n; + } + clock.advance(std::chrono::seconds(1)); + logic.oncePerSecond(); + } + // Less than 20 attempts + BEAST_EXPECT(n < 20); + } + + // with activate + void + testBackoff2() + { + auto const seconds = 10000; + testcase("backoff 2"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + logic.config(c); + } + + PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first); + std::size_t n = 0; + + for (std::size_t i = 0; i < seconds; ++i) + { + auto const list = logic.autoconnect(); + if (!list.empty()) + { + BEAST_EXPECT(list.size() == 1); + auto const [slot, _] = logic.newOutboundSlot(list.front()); + if (!BEAST_EXPECT( + logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5")))) + return; + if (!BEAST_EXPECT(logic.activate(slot, pk, false) == PeerFinder::Result::Success)) + return; + logic.onClosed(slot); + ++n; + } + clock.advance(std::chrono::seconds(1)); + logic.oncePerSecond(); + } + // No more often than once per minute + BEAST_EXPECT(n <= (seconds + 59) / 60); + } + + // test accepting an incoming slot for an already existing outgoing slot + void + testDuplicateOutIn() + { + testcase("duplicate out/in"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); + auto const [slot1, r] = logic.newOutboundSlot(remote); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r == Result::Success); + BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); + + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + auto const [slot2, r2] = logic.newInboundSlot(local, remote); + BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); + BEAST_EXPECT(r2 == Result::DuplicatePeer); + + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.onClosed(slot2); + + logic.onClosed(slot1); + } + + // test establishing outgoing slot for an already existing incoming slot + void + testDuplicateInOut() + { + testcase("duplicate in/out"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + + auto const [slot1, r] = logic.newInboundSlot(local, remote); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r == Result::Success); + BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); + + auto const [slot2, r2] = logic.newOutboundSlot(remote); + BEAST_EXPECT(r2 == Result::DuplicatePeer); + BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.onClosed(slot2); + logic.onClosed(slot1); + } + + void + testPeerLimitExceeded() + { + testcase("peer limit exceeded"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + auto const [slot, r] = + logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(r == Result::Success); + + auto const [slot1, r1] = + logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r1 == Result::Success); + + auto const [slot2, r2] = + logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1027")); + BEAST_EXPECT(r2 == Result::IpLimitExceeded); + + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.onClosed(slot2); + logic.onClosed(slot1); + logic.onClosed(slot); + } + + void + testActivateDuplicatePeer() + { + testcase("test activate duplicate peer"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + + PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); + + auto const [slot, rSlot] = + logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(rSlot == Result::Success); + + auto const [slot2, r2Slot] = + logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1026")); + BEAST_EXPECT(slot2 != nullptr); + BEAST_EXPECT(r2Slot == Result::Success); + + BEAST_EXPECT(logic.onConnected(slot, local)); + BEAST_EXPECT(logic.onConnected(slot2, local)); + + BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); + + // activating a different slot with the same node ID (pk) must fail + BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::DuplicatePeer); + + logic.onClosed(slot); + + // accept the same key for a new slot after removing the old slot + BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::Success); + logic.onClosed(slot2); + } + + void + testActivateInboundDisabled() + { + testcase("test activate inbound disabled"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + + auto const [slot, rSlot] = + logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(rSlot == Result::Success); + + BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::InboundDisabled); + + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + c.inPeers = 1; + logic.config(c); + } + // new inbound slot must succeed when inbound connections are enabled + BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); + + // creating a new inbound slot must succeed as IP Limit is not exceeded + auto const [slot2, r2Slot] = + logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); + BEAST_EXPECT(slot2 != nullptr); + BEAST_EXPECT(r2Slot == Result::Success); + + PublicKey const pk2(randomKeyPair(KeyType::Secp256k1).first); + + // an inbound slot exceeding inPeers limit must fail + BEAST_EXPECT(logic.activate(slot2, pk2, false) == Result::Full); + + logic.onClosed(slot2); + logic.onClosed(slot); + } + + void + testAddFixedPeerNoPort() + { + testcase("test addFixedPeer no port"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + try + { + logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.2")); + fail("invalid endpoint successfully added"); + } + catch (std::runtime_error const& e) + { + pass(); + } + } + + void + testOnConnectedSelfConnection() + { + testcase("test onConnected self connection"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + + auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1234"); + auto const [slot, r] = logic.newOutboundSlot(local); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(r == Result::Success); + + // Must fail when a slot is to our own IP address + BEAST_EXPECT(!logic.onConnected(slot, local)); + logic.onClosed(slot); + } + + void + testConfig() + { + // if peers_max is configured then peers_in_max and peers_out_max + // are ignored + auto run = [&](std::string const& test, + std::optional maxPeers, + std::optional maxIn, + std::optional maxOut, + std::uint16_t port, + std::uint16_t expectOut, + std::uint16_t expectIn, + std::uint16_t expectIpLimit) { + xrpl::Config c; + + testcase(test); + + std::string toLoad; + int max = 0; + if (maxPeers) + { + max = maxPeers.value(); + toLoad += "[peers_max]\n" + std::to_string(max) + "\n" + "[peers_in_max]\n" + + std::to_string(maxIn.value_or(0)) + "\n" + "[peers_out_max]\n" + + std::to_string(maxOut.value_or(0)) + "\n"; + } + else if (maxIn && maxOut) + { + toLoad += "[peers_in_max]\n" + std::to_string(*maxIn) + "\n" + "[peers_out_max]\n" + + std::to_string(*maxOut) + "\n"; + } + + c.loadFromString(toLoad); + BEAST_EXPECT( + (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 && c.PEERS_OUT_MAX == 0) || + (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); + + Config const config = Config::makeConfig(c, port, false, 0); + + Counts counts; + counts.onConfig(config); + BEAST_EXPECT( + counts.outMax() == expectOut && counts.inMax() == expectIn && + config.ipLimit == expectIpLimit); + + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.config(config); + + BEAST_EXPECT(logic.config() == config); + }; + + // if max_peers == 0 => maxPeers = 21, + // else if max_peers < 10 => maxPeers = 10 else maxPeers = + // max_peers + // expectOut => if legacy => max(0.15 * maxPeers, 10), + // if legacy && !wantIncoming => maxPeers else max_out_peers + // expectIn => if legacy && wantIncoming => maxPeers - outPeers + // else if !wantIncoming => 0 else max_in_peers + // ipLimit => if expectIn <= 21 => 2 else 2 + min(5, expectIn/21) + // ipLimit = max(1, min(ipLimit, expectIn/2)) + + // legacy test with max_peers + run("legacy no config", {}, {}, {}, 4000, 10, 11, 2); + run("legacy max_peers 0", 0, 100, 10, 4000, 10, 11, 2); + run("legacy max_peers 5", 5, 100, 10, 4000, 10, 0, 1); + run("legacy max_peers 20", 20, 100, 10, 4000, 10, 10, 2); + run("legacy max_peers 100", 100, 100, 10, 4000, 15, 85, 6); + run("legacy max_peers 20, private", 20, 100, 10, 0, 20, 0, 1); + + // test with max_in_peers and max_out_peers + run("new in 100/out 10", {}, 100, 10, 4000, 10, 100, 6); + run("new in 0/out 10", {}, 0, 10, 4000, 10, 0, 1); + run("new in 100/out 10, private", {}, 100, 10, 0, 10, 0, 6); + } + + void + testInvalidConfig() + { + testcase("invalid config"); + + auto run = [&](std::string const& toLoad) { + xrpl::Config c; + try + { + c.loadFromString(toLoad); + fail(); + } + catch (...) + { + pass(); + } + }; + run(R"xrpldConfig( +[peers_in_max] +100 +)xrpldConfig"); + run(R"xrpldConfig( +[peers_out_max] +100 +)xrpldConfig"); + run(R"xrpldConfig( +[peers_in_max] +100 +[peers_out_max] +5 +)xrpldConfig"); + run(R"xrpldConfig( +[peers_in_max] +1001 +[peers_out_max] +10 +)xrpldConfig"); + run(R"xrpldConfig( +[peers_in_max] +10 +[peers_out_max] +1001 +)xrpldConfig"); + } + + void + run() override + { + testBackoff1(); + testBackoff2(); + testDuplicateOutIn(); + testDuplicateInOut(); + testConfig(); + testInvalidConfig(); + testPeerLimitExceeded(); + testActivateDuplicatePeer(); + testActivateInboundDisabled(); + testAddFixedPeerNoPort(); + testOnConnectedSelfConnection(); + } +}; + +BEAST_DEFINE_TESTSUITE(PeerFinder, peerfinder, xrpl); + +} // namespace xrpl::PeerFinder From a7d3e533beee1e480469532836676163b54846c9 Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 12:52:08 +0200 Subject: [PATCH 2/7] test: Migrate peerfinder Beast tests to GTest, with GMock --- src/test/peerfinder/Livecache_test.cpp | 214 ----- src/test/peerfinder/PeerFinder_test.cpp | 532 ------------- src/tests/libxrpl/CMakeLists.txt | 15 + .../libxrpl/peerfinder/Livecache_test.cpp | 347 +++++---- .../libxrpl/peerfinder/PeerFinder_test.cpp | 735 ++++++++---------- src/tests/libxrpl/peerfinder/main.cpp | 9 + 6 files changed, 522 insertions(+), 1330 deletions(-) delete mode 100644 src/test/peerfinder/Livecache_test.cpp delete mode 100644 src/test/peerfinder/PeerFinder_test.cpp create mode 100644 src/tests/libxrpl/peerfinder/main.cpp diff --git a/src/test/peerfinder/Livecache_test.cpp b/src/test/peerfinder/Livecache_test.cpp deleted file mode 100644 index 28d5beef887..00000000000 --- a/src/test/peerfinder/Livecache_test.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace xrpl::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 - void - add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) - { - Endpoint const 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::kLIVE_CACHE_SECONDS_TO_LIVE - 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 kNUM_EPS = 40; - Livecache<> c(clock_, journal_); - for (auto i = 0; i < kNUM_EPS; ++i) - add(beast::IP::randomEP(true), c, xrpl::randInt()); - 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 == kNUM_EPS); - } - - void - testShuffle() - { - testcase("Shuffle"); - Livecache<> c(clock_, journal_); - for (auto i = 0; i < 100; ++i) - add(beast::IP::randomEP(true), c, xrpl::randInt(Tuning::kMAX_HOPS + 1)); - - using at_hop = std::vector; - using all_hops = std::array; - - auto cmpEp = [](Endpoint const& a, Endpoint const& b) { - return (b.hops < a.hops || (b.hops == a.hops && b.address < a.address)); - }; - all_hops before; - all_hops beforeSorted; - 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(beforeSorted[i.first])); - std::sort(beforeSorted[i.first].begin(), beforeSorted[i.first].end(), cmpEp); - } - - c.hops.shuffle(); - - all_hops after; - all_hops afterSorted; - 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(afterSorted[i.first])); - std::sort(afterSorted[i.first].begin(), afterSorted[i.first].end(), cmpEp); - } - - // each hop bucket should contain the same items - // before and after sort, albeit in different order - bool allMatch = true; - for (auto i = 0; i < before.size(); ++i) - { - BEAST_EXPECT(before[i].size() == after[i].size()); - allMatch = allMatch && (before[i] == after[i]); - BEAST_EXPECT(beforeSorted[i] == afterSorted[i]); - } - BEAST_EXPECT(!allMatch); - } - - void - run() override - { - testBasicInsert(); - testInsertUpdate(); - testExpire(); - testHistogram(); - testShuffle(); - } -}; - -BEAST_DEFINE_TESTSUITE(Livecache, peerfinder, xrpl); - -} // namespace xrpl::PeerFinder diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp deleted file mode 100644 index 17ce6835ef9..00000000000 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ /dev/null @@ -1,532 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace xrpl::PeerFinder { - -class PeerFinder_test : public beast::unit_test::Suite -{ - test::SuiteJournal journal_; - -public: - PeerFinder_test() : journal_("PeerFinder_test", *this) - { - } - - struct TestStore : Store - { - std::size_t - load(load_callback const& cb) override - { - return 0; - } - - void - save(std::vector const&) override - { - } - }; - - struct TestChecker - { - void - stop() - { - } - - void - wait() - { - } - - template - void - asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler) - { - boost::system::error_code ec; - handler(ep, ep, ec); - } - }; - - void - testBackoff1() - { - auto const seconds = 10000; - testcase("backoff 1"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - logic.config(c); - } - std::size_t n = 0; - for (std::size_t i = 0; i < seconds; ++i) - { - auto const list = logic.autoconnect(); - if (!list.empty()) - { - BEAST_EXPECT(list.size() == 1); - auto const [slot, _] = logic.newOutboundSlot(list.front()); - BEAST_EXPECT( - logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5"))); - logic.onClosed(slot); - ++n; - } - clock.advance(std::chrono::seconds(1)); - logic.oncePerSecond(); - } - // Less than 20 attempts - BEAST_EXPECT(n < 20); - } - - // with activate - void - testBackoff2() - { - auto const seconds = 10000; - testcase("backoff 2"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - logic.config(c); - } - - PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first); - std::size_t n = 0; - - for (std::size_t i = 0; i < seconds; ++i) - { - auto const list = logic.autoconnect(); - if (!list.empty()) - { - BEAST_EXPECT(list.size() == 1); - auto const [slot, _] = logic.newOutboundSlot(list.front()); - if (!BEAST_EXPECT( - logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5")))) - return; - if (!BEAST_EXPECT(logic.activate(slot, pk, false) == PeerFinder::Result::Success)) - return; - logic.onClosed(slot); - ++n; - } - clock.advance(std::chrono::seconds(1)); - logic.oncePerSecond(); - } - // No more often than once per minute - BEAST_EXPECT(n <= (seconds + 59) / 60); - } - - // test accepting an incoming slot for an already existing outgoing slot - void - testDuplicateOutIn() - { - testcase("duplicate out/in"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } - - auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); - auto const [slot1, r] = logic.newOutboundSlot(remote); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r == Result::Success); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - auto const [slot2, r2] = logic.newInboundSlot(local, remote); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - BEAST_EXPECT(r2 == Result::DuplicatePeer); - - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); - - logic.onClosed(slot1); - } - - // test establishing outgoing slot for an already existing incoming slot - void - testDuplicateInOut() - { - testcase("duplicate in/out"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } - - auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - - auto const [slot1, r] = logic.newInboundSlot(local, remote); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r == Result::Success); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - - auto const [slot2, r2] = logic.newOutboundSlot(remote); - BEAST_EXPECT(r2 == Result::DuplicatePeer); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); - logic.onClosed(slot1); - } - - void - testPeerLimitExceeded() - { - testcase("peer limit exceeded"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } - - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - auto const [slot, r] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(r == Result::Success); - - auto const [slot1, r1] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r1 == Result::Success); - - auto const [slot2, r2] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1027")); - BEAST_EXPECT(r2 == Result::IpLimitExceeded); - - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); - logic.onClosed(slot1); - logic.onClosed(slot); - } - - void - testActivateDuplicatePeer() - { - testcase("test activate duplicate peer"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } - - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - - PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); - - auto const [slot, rSlot] = - logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(rSlot == Result::Success); - - auto const [slot2, r2Slot] = - logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot2 != nullptr); - BEAST_EXPECT(r2Slot == Result::Success); - - BEAST_EXPECT(logic.onConnected(slot, local)); - BEAST_EXPECT(logic.onConnected(slot2, local)); - - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); - - // activating a different slot with the same node ID (pk) must fail - BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::DuplicatePeer); - - logic.onClosed(slot); - - // accept the same key for a new slot after removing the old slot - BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::Success); - logic.onClosed(slot2); - } - - void - testActivateInboundDisabled() - { - testcase("test activate inbound disabled"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } - - PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - - auto const [slot, rSlot] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(rSlot == Result::Success); - - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::InboundDisabled); - - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - c.inPeers = 1; - logic.config(c); - } - // new inbound slot must succeed when inbound connections are enabled - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); - - // creating a new inbound slot must succeed as IP Limit is not exceeded - auto const [slot2, r2Slot] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot2 != nullptr); - BEAST_EXPECT(r2Slot == Result::Success); - - PublicKey const pk2(randomKeyPair(KeyType::Secp256k1).first); - - // an inbound slot exceeding inPeers limit must fail - BEAST_EXPECT(logic.activate(slot2, pk2, false) == Result::Full); - - logic.onClosed(slot2); - logic.onClosed(slot); - } - - void - testAddFixedPeerNoPort() - { - testcase("test addFixedPeer no port"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - try - { - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.2")); - fail("invalid endpoint successfully added"); - } - catch (std::runtime_error const& e) - { - pass(); - } - } - - void - testOnConnectedSelfConnection() - { - testcase("test onConnected self connection"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1234"); - auto const [slot, r] = logic.newOutboundSlot(local); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(r == Result::Success); - - // Must fail when a slot is to our own IP address - BEAST_EXPECT(!logic.onConnected(slot, local)); - logic.onClosed(slot); - } - - void - testConfig() - { - // if peers_max is configured then peers_in_max and peers_out_max - // are ignored - auto run = [&](std::string const& test, - std::optional maxPeers, - std::optional maxIn, - std::optional maxOut, - std::uint16_t port, - std::uint16_t expectOut, - std::uint16_t expectIn, - std::uint16_t expectIpLimit) { - xrpl::Config c; - - testcase(test); - - std::string toLoad; - int max = 0; - if (maxPeers) - { - max = maxPeers.value(); - toLoad += "[peers_max]\n" + std::to_string(max) + "\n" + "[peers_in_max]\n" + - std::to_string(maxIn.value_or(0)) + "\n" + "[peers_out_max]\n" + - std::to_string(maxOut.value_or(0)) + "\n"; - } - else if (maxIn && maxOut) - { - toLoad += "[peers_in_max]\n" + std::to_string(*maxIn) + "\n" + "[peers_out_max]\n" + - std::to_string(*maxOut) + "\n"; - } - - c.loadFromString(toLoad); - BEAST_EXPECT( - (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 && c.PEERS_OUT_MAX == 0) || - (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); - - Config const config = Config::makeConfig(c, port, false, 0); - - Counts counts; - counts.onConfig(config); - BEAST_EXPECT( - counts.outMax() == expectOut && counts.inMax() == expectIn && - config.ipLimit == expectIpLimit); - - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.config(config); - - BEAST_EXPECT(logic.config() == config); - }; - - // if max_peers == 0 => maxPeers = 21, - // else if max_peers < 10 => maxPeers = 10 else maxPeers = - // max_peers - // expectOut => if legacy => max(0.15 * maxPeers, 10), - // if legacy && !wantIncoming => maxPeers else max_out_peers - // expectIn => if legacy && wantIncoming => maxPeers - outPeers - // else if !wantIncoming => 0 else max_in_peers - // ipLimit => if expectIn <= 21 => 2 else 2 + min(5, expectIn/21) - // ipLimit = max(1, min(ipLimit, expectIn/2)) - - // legacy test with max_peers - run("legacy no config", {}, {}, {}, 4000, 10, 11, 2); - run("legacy max_peers 0", 0, 100, 10, 4000, 10, 11, 2); - run("legacy max_peers 5", 5, 100, 10, 4000, 10, 0, 1); - run("legacy max_peers 20", 20, 100, 10, 4000, 10, 10, 2); - run("legacy max_peers 100", 100, 100, 10, 4000, 15, 85, 6); - run("legacy max_peers 20, private", 20, 100, 10, 0, 20, 0, 1); - - // test with max_in_peers and max_out_peers - run("new in 100/out 10", {}, 100, 10, 4000, 10, 100, 6); - run("new in 0/out 10", {}, 0, 10, 4000, 10, 0, 1); - run("new in 100/out 10, private", {}, 100, 10, 0, 10, 0, 6); - } - - void - testInvalidConfig() - { - testcase("invalid config"); - - auto run = [&](std::string const& toLoad) { - xrpl::Config c; - try - { - c.loadFromString(toLoad); - fail(); - } - catch (...) - { - pass(); - } - }; - run(R"xrpldConfig( -[peers_in_max] -100 -)xrpldConfig"); - run(R"xrpldConfig( -[peers_out_max] -100 -)xrpldConfig"); - run(R"xrpldConfig( -[peers_in_max] -100 -[peers_out_max] -5 -)xrpldConfig"); - run(R"xrpldConfig( -[peers_in_max] -1001 -[peers_out_max] -10 -)xrpldConfig"); - run(R"xrpldConfig( -[peers_in_max] -10 -[peers_out_max] -1001 -)xrpldConfig"); - } - - void - run() override - { - testBackoff1(); - testBackoff2(); - testDuplicateOutIn(); - testDuplicateInOut(); - testConfig(); - testInvalidConfig(); - testPeerLimitExceeded(); - testActivateDuplicatePeer(); - testActivateInboundDisabled(); - testAddFixedPeerNoPort(); - testOnConnectedSelfConnection(); - } -}; - -BEAST_DEFINE_TESTSUITE(PeerFinder, peerfinder, xrpl); - -} // namespace xrpl::PeerFinder diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index ee076985194..adf66fc6393 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -35,6 +35,21 @@ xrpl_add_test(json) target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test) add_dependencies(xrpl.tests xrpl.test.json) +xrpl_add_test( + peerfinder + ${CMAKE_SOURCE_DIR}/src/xrpld/core/detail/Config.cpp + ${CMAKE_SOURCE_DIR}/src/xrpld/peerfinder/detail/Bootcache.cpp + ${CMAKE_SOURCE_DIR}/src/xrpld/peerfinder/detail/Endpoint.cpp + ${CMAKE_SOURCE_DIR}/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp + ${CMAKE_SOURCE_DIR}/src/xrpld/peerfinder/detail/SlotImp.cpp +) +target_include_directories(xrpl.test.peerfinder PRIVATE ${CMAKE_SOURCE_DIR}/src) +target_link_libraries( + xrpl.test.peerfinder + PRIVATE xrpl.imports.test Xrpl::boost Xrpl::libs Xrpl::opts +) +add_dependencies(xrpl.tests xrpl.test.peerfinder) + xrpl_add_test(tx) target_link_libraries(xrpl.test.tx PRIVATE xrpl.imports.test) add_dependencies(xrpl.tests xrpl.test.tx) diff --git a/src/tests/libxrpl/peerfinder/Livecache_test.cpp b/src/tests/libxrpl/peerfinder/Livecache_test.cpp index 28d5beef887..f7050ad0598 100644 --- a/src/tests/libxrpl/peerfinder/Livecache_test.cpp +++ b/src/tests/libxrpl/peerfinder/Livecache_test.cpp @@ -1,6 +1,3 @@ -#include -#include - #include #include #include @@ -8,207 +5,235 @@ #include #include #include -#include +#include #include #include #include #include +#include "xrpl/beast/net/IPAddressV4.h" +#include "xrpl/beast/net/IPAddressV6.h" +#include +#include + #include #include #include #include +#include #include #include namespace xrpl::PeerFinder { +namespace { -bool -operator==(Endpoint const& a, Endpoint const& b) +beast::Journal +journal() { - return (a.hops == b.hops && a.address == b.address); + return beast::Journal{TestSink::instance()}; } -class Livecache_test : public beast::unit_test::Suite +beast::IP::Endpoint +endpoint(std::uint16_t index, bool v4 = true) { - TestStopwatch clock_; - test::SuiteJournal journal_; + auto const port = static_cast(10000 + index); -public: - Livecache_test() : journal_("Livecache_test", *this) + if (v4) { + auto bytes = beast::IP::AddressV4::bytes_type{ + {54, + static_cast((index / 256) % 256), + static_cast(index % 256), + 1}}; + return beast::IP::Endpoint{beast::IP::Address{beast::IP::AddressV4{bytes}}, port}; } - // Add the address as an endpoint - template - void - add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0) - { - Endpoint const cep{ep, hops}; - c.insert(cep); - } + auto bytes = beast::IP::AddressV6::bytes_type{ + {0x20, + 0x01, + 0x0d, + 0xb8, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + static_cast((index / 256) % 256), + static_cast(index % 256), + 1}}; + return beast::IP::Endpoint{beast::IP::Address{beast::IP::AddressV6{bytes}}, port}; +} - void - testBasicInsert() - { - testcase("Basic Insert"); - Livecache<> c(clock_, journal_); - BEAST_EXPECT(c.empty()); +template +void +addEndpoint(beast::IP::Endpoint const& ep, Cache& cache, std::uint32_t hops = 0) +{ + cache.insert(Endpoint{ep, hops}); +} - for (auto i = 0; i < 10; ++i) - add(beast::IP::randomEP(true), c); +bool +sameEndpoint(Endpoint const& lhs, Endpoint const& rhs) +{ + return lhs.hops == rhs.hops && lhs.address == rhs.address; +} - BEAST_EXPECT(!c.empty()); - BEAST_EXPECT(c.size() == 10); +bool +sameEndpoints(std::vector const& lhs, std::vector const& rhs) +{ + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin(), sameEndpoint); +} - for (auto i = 0; i < 10; ++i) - add(beast::IP::randomEP(false), c); +} // namespace - BEAST_EXPECT(!c.empty()); - BEAST_EXPECT(c.size() == 20); - } +TEST(Livecache, BasicInsert) +{ + TestStopwatch clock; + Livecache<> cache(clock, journal()); + EXPECT_TRUE(cache.empty()); + + for (auto i = 0; i < 10; ++i) + addEndpoint(endpoint(i, true), cache); - void - testInsertUpdate() + EXPECT_FALSE(cache.empty()); + EXPECT_EQ(cache.size(), 10u); + + for (auto i = 10; i < 20; ++i) + addEndpoint(endpoint(i, false), cache); + + EXPECT_FALSE(cache.empty()); + EXPECT_EQ(cache.size(), 20u); +} + +TEST(Livecache, InsertUpdateKeepsLowestHopCount) +{ + TestStopwatch clock; + Livecache<> cache(clock, journal()); + + auto const ep1 = Endpoint{endpoint(1), 2}; + cache.insert(ep1); + ASSERT_EQ(cache.size(), 1u); + EXPECT_EQ((cache.hops.begin() + 2)->begin()->hops, 2u); + + auto const ep2 = Endpoint{ep1.address, 4}; + cache.insert(ep2); + EXPECT_EQ(cache.size(), 1u); + EXPECT_EQ((cache.hops.begin() + 2)->begin()->hops, 2u); + + auto const ep3 = Endpoint{ep1.address, 2}; + cache.insert(ep3); + EXPECT_EQ(cache.size(), 1u); + EXPECT_EQ((cache.hops.begin() + 2)->begin()->hops, 2u); + + auto const ep4 = Endpoint{ep1.address, 1}; + cache.insert(ep4); + EXPECT_EQ(cache.size(), 1u); + EXPECT_EQ((cache.hops.begin() + 1)->begin()->hops, 1u); +} + +TEST(Livecache, ExpireRemovesEntriesAfterTtl) +{ + using namespace std::chrono_literals; + + TestStopwatch clock; + Livecache<> cache(clock, journal()); + + cache.insert(Endpoint{endpoint(1), 1}); + ASSERT_EQ(cache.size(), 1u); + + cache.expire(); + EXPECT_EQ(cache.size(), 1u); + + clock.advance(Tuning::kLIVE_CACHE_SECONDS_TO_LIVE - 1s); + cache.expire(); + EXPECT_EQ(cache.size(), 1u); + + clock.advance(1s); + cache.expire(); + EXPECT_TRUE(cache.empty()); +} + +TEST(Livecache, HistogramCountsAllEntries) +{ + constexpr auto kNUM_ENDPOINTS = 40; + + TestStopwatch clock; + Livecache<> cache(clock, journal()); + for (auto i = 0; i < kNUM_ENDPOINTS; ++i) { - 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); + addEndpoint(endpoint(static_cast(i)), cache, xrpl::randInt()); } - void - testExpire() + auto const histogram = cache.hops.histogram(); + ASSERT_FALSE(histogram.empty()); + + std::vector values; + boost::split(values, histogram, boost::algorithm::is_any_of(",")); + + auto sum = 0; + for (auto const& value : values) { - 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::kLIVE_CACHE_SECONDS_TO_LIVE - 1s); - c.expire(); - BEAST_EXPECT(c.size() == 1); - // now advance to the point of expiration - clock_.advance(1s); - c.expire(); - BEAST_EXPECT(c.empty()); + auto const count = boost::lexical_cast(boost::trim_copy(value)); + sum += count; + EXPECT_GE(count, 0); } + EXPECT_EQ(sum, kNUM_ENDPOINTS); +} - void - testHistogram() +TEST(Livecache, ShufflePreservesBucketContents) +{ + TestStopwatch clock; + Livecache<> cache(clock, journal()); + for (auto i = 0; i < 100; ++i) { - testcase("Histogram"); - constexpr auto kNUM_EPS = 40; - Livecache<> c(clock_, journal_); - for (auto i = 0; i < kNUM_EPS; ++i) - add(beast::IP::randomEP(true), c, xrpl::randInt()); - 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 == kNUM_EPS); + addEndpoint( + endpoint(static_cast(i)), cache, xrpl::randInt(Tuning::kMAX_HOPS + 1)); } - void - testShuffle() + using AtHop = std::vector; + using AllHops = std::array; + + auto const compareEndpoint = [](Endpoint const& lhs, Endpoint const& rhs) { + return rhs.hops < lhs.hops || (rhs.hops == lhs.hops && rhs.address < lhs.address); + }; + + AllHops before; + AllHops beforeSorted; + for (auto i = std::make_pair(0, cache.hops.begin()); i.second != cache.hops.end(); + ++i.first, ++i.second) { - testcase("Shuffle"); - Livecache<> c(clock_, journal_); - for (auto i = 0; i < 100; ++i) - add(beast::IP::randomEP(true), c, xrpl::randInt(Tuning::kMAX_HOPS + 1)); - - using at_hop = std::vector; - using all_hops = std::array; - - auto cmpEp = [](Endpoint const& a, Endpoint const& b) { - return (b.hops < a.hops || (b.hops == a.hops && b.address < a.address)); - }; - all_hops before; - all_hops beforeSorted; - 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(beforeSorted[i.first])); - std::sort(beforeSorted[i.first].begin(), beforeSorted[i.first].end(), cmpEp); - } - - c.hops.shuffle(); - - all_hops after; - all_hops afterSorted; - 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(afterSorted[i.first])); - std::sort(afterSorted[i.first].begin(), afterSorted[i.first].end(), cmpEp); - } - - // each hop bucket should contain the same items - // before and after sort, albeit in different order - bool allMatch = true; - for (auto i = 0; i < before.size(); ++i) - { - BEAST_EXPECT(before[i].size() == after[i].size()); - allMatch = allMatch && (before[i] == after[i]); - BEAST_EXPECT(beforeSorted[i] == afterSorted[i]); - } - BEAST_EXPECT(!allMatch); + 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(beforeSorted[i.first])); + std::sort(beforeSorted[i.first].begin(), beforeSorted[i.first].end(), compareEndpoint); } - void - run() override + cache.hops.shuffle(); + + AllHops after; + AllHops afterSorted; + for (auto i = std::make_pair(0, cache.hops.begin()); i.second != cache.hops.end(); + ++i.first, ++i.second) { - testBasicInsert(); - testInsertUpdate(); - testExpire(); - testHistogram(); - testShuffle(); + 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(afterSorted[i.first])); + std::sort(afterSorted[i.first].begin(), afterSorted[i.first].end(), compareEndpoint); } -}; -BEAST_DEFINE_TESTSUITE(Livecache, peerfinder, xrpl); + auto allBucketsKeptOriginalOrder = true; + for (auto i = 0u; i < before.size(); ++i) + { + EXPECT_EQ(before[i].size(), after[i].size()); + allBucketsKeptOriginalOrder = + allBucketsKeptOriginalOrder && sameEndpoints(before[i], after[i]); + EXPECT_TRUE(sameEndpoints(beforeSorted[i], afterSorted[i])); + } + EXPECT_FALSE(allBucketsKeptOriginalOrder); +} } // namespace xrpl::PeerFinder diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp index 17ce6835ef9..5b6da8ee0d6 100644 --- a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp +++ b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -8,525 +6,416 @@ #include #include -#include +#include #include #include #include -#include + +#include +#include +#include #include #include #include +#include #include #include #include +#include #include namespace xrpl::PeerFinder { +namespace { + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; + +beast::Journal +journal() +{ + return beast::Journal{TestSink::instance()}; +} -class PeerFinder_test : public beast::unit_test::Suite +beast::IP::Endpoint +endpoint(std::string const& value) { - test::SuiteJournal journal_; + return beast::IP::Endpoint::fromString(value); +} +class MockStore : public Store +{ public: - PeerFinder_test() : journal_("PeerFinder_test", *this) + MOCK_METHOD(std::size_t, load, (Store::load_callback const& cb), (override)); + MOCK_METHOD(void, save, (std::vector const& entries), (override)); +}; + +void +allowEmptyStore(MockStore& store) +{ + ON_CALL(store, load(_)).WillByDefault(Return(0)); + ON_CALL(store, save(_)).WillByDefault([](std::vector const&) {}); +} + +class MockChecker +{ +public: + MOCK_METHOD(void, stop, ()); + MOCK_METHOD(void, wait, ()); + MOCK_METHOD(void, recordAsyncConnect, (beast::IP::Endpoint const& ep)); + + template + void + asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler) { + recordAsyncConnect(ep); + std::forward(handler)(boost::system::error_code{}); } +}; - struct TestStore : Store +class PeerFinderTest : public ::testing::Test +{ +public: + PeerFinderTest() { - std::size_t - load(load_callback const& cb) override - { - return 0; - } - - void - save(std::vector const&) override - { - } - }; + allowEmptyStore(store_); + } - struct TestChecker +protected: + void + configure(std::size_t ipLimit = 2) { - void - stop() - { - } + Config config; + config.autoConnect = false; + config.listeningPort = 1024; + config.ipLimit = static_cast(ipLimit); + logic_.config(config); + } - void - wait() - { - } + NiceMock store_; + NiceMock checker_; + TestStopwatch clock_; + Logic> logic_{clock_, store_, checker_, journal()}; +}; - template - void - asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler) - { - boost::system::error_code ec; - handler(ep, ep, ec); - } - }; +TEST_F(PeerFinderTest, BackoffLimitsRepeatedConnectionAttempts) +{ + auto constexpr kSECONDS = 10000; - void - testBackoff1() + logic_.addFixedPeer("test", endpoint("65.0.0.1:5")); + configure(); + + std::size_t attempts = 0; + for (std::size_t i = 0; i < kSECONDS; ++i) { - auto const seconds = 10000; - testcase("backoff 1"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - logic.config(c); - } - std::size_t n = 0; - for (std::size_t i = 0; i < seconds; ++i) + auto const list = logic_.autoconnect(); + if (!list.empty()) { - auto const list = logic.autoconnect(); - if (!list.empty()) - { - BEAST_EXPECT(list.size() == 1); - auto const [slot, _] = logic.newOutboundSlot(list.front()); - BEAST_EXPECT( - logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5"))); - logic.onClosed(slot); - ++n; - } - clock.advance(std::chrono::seconds(1)); - logic.oncePerSecond(); + ASSERT_EQ(list.size(), 1u); + auto const [slot, result] = logic_.newOutboundSlot(list.front()); + ASSERT_NE(slot, nullptr); + ASSERT_EQ(result, Result::Success); + EXPECT_TRUE(logic_.onConnected(slot, endpoint("65.0.0.2:5"))); + logic_.onClosed(slot); + ++attempts; } - // Less than 20 attempts - BEAST_EXPECT(n < 20); + clock_.advance(std::chrono::seconds(1)); + logic_.oncePerSecond(); } - // with activate - void - testBackoff2() - { - auto const seconds = 10000; - testcase("backoff 2"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5")); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - logic.config(c); - } + EXPECT_LT(attempts, 20u); +} - PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first); - std::size_t n = 0; +TEST_F(PeerFinderTest, ActivatedPeerBackoffAllowsAtMostOneAttemptPerMinute) +{ + auto constexpr kSECONDS = 10000; - for (std::size_t i = 0; i < seconds; ++i) - { - auto const list = logic.autoconnect(); - if (!list.empty()) - { - BEAST_EXPECT(list.size() == 1); - auto const [slot, _] = logic.newOutboundSlot(list.front()); - if (!BEAST_EXPECT( - logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5")))) - return; - if (!BEAST_EXPECT(logic.activate(slot, pk, false) == PeerFinder::Result::Success)) - return; - logic.onClosed(slot); - ++n; - } - clock.advance(std::chrono::seconds(1)); - logic.oncePerSecond(); - } - // No more often than once per minute - BEAST_EXPECT(n <= (seconds + 59) / 60); - } + logic_.addFixedPeer("test", endpoint("65.0.0.1:5")); + configure(); - // test accepting an incoming slot for an already existing outgoing slot - void - testDuplicateOutIn() + PublicKey const publicKey(randomKeyPair(KeyType::Secp256k1).first); + + std::size_t attempts = 0; + for (std::size_t i = 0; i < kSECONDS; ++i) { - testcase("duplicate out/in"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); + auto const list = logic_.autoconnect(); + if (!list.empty()) { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); + ASSERT_EQ(list.size(), 1u); + auto const [slot, result] = logic_.newOutboundSlot(list.front()); + ASSERT_NE(slot, nullptr); + ASSERT_EQ(result, Result::Success); + ASSERT_TRUE(logic_.onConnected(slot, endpoint("65.0.0.2:5"))); + ASSERT_EQ(logic_.activate(slot, publicKey, false), Result::Success); + logic_.onClosed(slot); + ++attempts; } + clock_.advance(std::chrono::seconds(1)); + logic_.oncePerSecond(); + } - auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); - auto const [slot1, r] = logic.newOutboundSlot(remote); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r == Result::Success); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); + EXPECT_LE(attempts, (kSECONDS + 59u) / 60u); +} - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - auto const [slot2, r2] = logic.newInboundSlot(local, remote); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - BEAST_EXPECT(r2 == Result::DuplicatePeer); +TEST_F(PeerFinderTest, DuplicateInboundSlotIsRejectedForExistingOutboundSlot) +{ + configure(); + + auto const remote = endpoint("65.0.0.1:5"); + auto const [slot1, result1] = logic_.newOutboundSlot(remote); + ASSERT_NE(slot1, nullptr); + EXPECT_EQ(result1, Result::Success); + EXPECT_EQ(logic_.connectedAddresses.count(remote.address()), 1u); + + auto const local = endpoint("65.0.0.2:1024"); + auto const [slot2, result2] = logic_.newInboundSlot(local, remote); + EXPECT_EQ(logic_.connectedAddresses.count(remote.address()), 1u); + EXPECT_EQ(result2, Result::DuplicatePeer); + EXPECT_EQ(slot2, nullptr); + + if (slot2) + logic_.onClosed(slot2); + logic_.onClosed(slot1); +} + +TEST_F(PeerFinderTest, DuplicateOutboundSlotIsRejectedForExistingInboundSlot) +{ + configure(); - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); + auto const remote = endpoint("65.0.0.1:5"); + auto const local = endpoint("65.0.0.2:1024"); - logic.onClosed(slot1); - } + auto const [slot1, result1] = logic_.newInboundSlot(local, remote); + ASSERT_NE(slot1, nullptr); + EXPECT_EQ(result1, Result::Success); + EXPECT_EQ(logic_.connectedAddresses.count(remote.address()), 1u); - // test establishing outgoing slot for an already existing incoming slot - void - testDuplicateInOut() - { - testcase("duplicate in/out"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } + auto const [slot2, result2] = logic_.newOutboundSlot(remote); + EXPECT_EQ(result2, Result::DuplicatePeer); + EXPECT_EQ(logic_.connectedAddresses.count(remote.address()), 1u); + EXPECT_EQ(slot2, nullptr); - auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5"); - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + if (slot2) + logic_.onClosed(slot2); + logic_.onClosed(slot1); +} - auto const [slot1, r] = logic.newInboundSlot(local, remote); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r == Result::Success); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); +TEST_F(PeerFinderTest, PeerLimitExceededRejectsAdditionalInboundSlot) +{ + configure(); - auto const [slot2, r2] = logic.newOutboundSlot(remote); - BEAST_EXPECT(r2 == Result::DuplicatePeer); - BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1); - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); - logic.onClosed(slot1); - } + auto const local = endpoint("65.0.0.2:1024"); + auto const [slot, result] = logic_.newInboundSlot(local, endpoint("55.104.0.2:1025")); + ASSERT_NE(slot, nullptr); + EXPECT_EQ(result, Result::Success); - void - testPeerLimitExceeded() - { - testcase("peer limit exceeded"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } + auto const [slot1, result1] = logic_.newInboundSlot(local, endpoint("55.104.0.2:1026")); + ASSERT_NE(slot1, nullptr); + EXPECT_EQ(result1, Result::Success); - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); - auto const [slot, r] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(r == Result::Success); - - auto const [slot1, r1] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot1 != nullptr); - BEAST_EXPECT(r1 == Result::Success); - - auto const [slot2, r2] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1027")); - BEAST_EXPECT(r2 == Result::IpLimitExceeded); - - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.onClosed(slot2); - logic.onClosed(slot1); - logic.onClosed(slot); - } + auto const [slot2, result2] = logic_.newInboundSlot(local, endpoint("55.104.0.2:1027")); + EXPECT_EQ(result2, Result::IpLimitExceeded); + EXPECT_EQ(slot2, nullptr); - void - testActivateDuplicatePeer() - { - testcase("test activate duplicate peer"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } + if (slot2) + logic_.onClosed(slot2); + logic_.onClosed(slot1); + logic_.onClosed(slot); +} - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); +TEST_F(PeerFinderTest, ActivateRejectsDuplicatePublicKey) +{ + configure(); - PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); + auto const local = endpoint("65.0.0.2:1024"); + PublicKey const publicKey(randomKeyPair(KeyType::Secp256k1).first); - auto const [slot, rSlot] = - logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(rSlot == Result::Success); + auto const [slot, result] = logic_.newOutboundSlot(endpoint("55.104.0.2:1025")); + ASSERT_NE(slot, nullptr); + EXPECT_EQ(result, Result::Success); - auto const [slot2, r2Slot] = - logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot2 != nullptr); - BEAST_EXPECT(r2Slot == Result::Success); + auto const [slot2, result2] = logic_.newOutboundSlot(endpoint("55.104.0.2:1026")); + ASSERT_NE(slot2, nullptr); + EXPECT_EQ(result2, Result::Success); - BEAST_EXPECT(logic.onConnected(slot, local)); - BEAST_EXPECT(logic.onConnected(slot2, local)); + EXPECT_TRUE(logic_.onConnected(slot, local)); + EXPECT_TRUE(logic_.onConnected(slot2, local)); - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); + EXPECT_EQ(logic_.activate(slot, publicKey, false), Result::Success); + EXPECT_EQ(logic_.activate(slot2, publicKey, false), Result::DuplicatePeer); - // activating a different slot with the same node ID (pk) must fail - BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::DuplicatePeer); + logic_.onClosed(slot); - logic.onClosed(slot); + EXPECT_EQ(logic_.activate(slot2, publicKey, false), Result::Success); + logic_.onClosed(slot2); +} - // accept the same key for a new slot after removing the old slot - BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::Success); - logic.onClosed(slot2); - } +TEST_F(PeerFinderTest, ActivateRejectsInboundWhenInboundConnectionsAreDisabled) +{ + configure(); + + PublicKey const publicKey(randomKeyPair(KeyType::Secp256k1).first); + auto const local = endpoint("65.0.0.2:1024"); + + auto const [slot, result] = logic_.newInboundSlot(local, endpoint("55.104.0.2:1025")); + ASSERT_NE(slot, nullptr); + EXPECT_EQ(result, Result::Success); + + EXPECT_EQ(logic_.activate(slot, publicKey, false), Result::InboundDisabled); - void - testActivateInboundDisabled() { - testcase("test activate inbound disabled"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - logic.config(c); - } + Config config; + config.autoConnect = false; + config.listeningPort = 1024; + config.ipLimit = 2; + config.inPeers = 1; + logic_.config(config); + } - PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first); - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024"); + EXPECT_EQ(logic_.activate(slot, publicKey, false), Result::Success); - auto const [slot, rSlot] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025")); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(rSlot == Result::Success); + auto const [slot2, result2] = logic_.newInboundSlot(local, endpoint("55.104.0.2:1026")); + ASSERT_NE(slot2, nullptr); + EXPECT_EQ(result2, Result::Success); - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::InboundDisabled); + PublicKey const publicKey2(randomKeyPair(KeyType::Secp256k1).first); + EXPECT_EQ(logic_.activate(slot2, publicKey2, false), Result::Full); - { - Config c; - c.autoConnect = false; - c.listeningPort = 1024; - c.ipLimit = 2; - c.inPeers = 1; - logic.config(c); - } - // new inbound slot must succeed when inbound connections are enabled - BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success); + logic_.onClosed(slot2); + logic_.onClosed(slot); +} - // creating a new inbound slot must succeed as IP Limit is not exceeded - auto const [slot2, r2Slot] = - logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026")); - BEAST_EXPECT(slot2 != nullptr); - BEAST_EXPECT(r2Slot == Result::Success); +TEST_F(PeerFinderTest, AddFixedPeerRejectsEndpointWithoutPort) +{ + EXPECT_THROW(logic_.addFixedPeer("test", endpoint("65.0.0.2")), std::runtime_error); +} - PublicKey const pk2(randomKeyPair(KeyType::Secp256k1).first); +TEST_F(PeerFinderTest, OnConnectedRejectsSelfConnection) +{ + auto const local = endpoint("65.0.0.2:1234"); + auto const [slot, result] = logic_.newOutboundSlot(local); + ASSERT_NE(slot, nullptr); + EXPECT_EQ(result, Result::Success); - // an inbound slot exceeding inPeers limit must fail - BEAST_EXPECT(logic.activate(slot2, pk2, false) == Result::Full); + EXPECT_FALSE(logic_.onConnected(slot, local)); + logic_.onClosed(slot); +} - logic.onClosed(slot2); - logic.onClosed(slot); - } +TEST(PeerFinderConfig, AppliesLegacyAndExplicitPeerLimits) +{ + struct ConfigCase + { + std::string name; + std::optional maxPeers; + std::optional maxIn; + std::optional maxOut; + std::uint16_t port; + std::uint16_t expectedOut; + std::uint16_t expectedIn; + std::uint16_t expectedIpLimit; + }; - void - testAddFixedPeerNoPort() + std::vector const cases{ + {.name="legacy no config", .maxPeers={}, .maxIn={}, .maxOut={}, .port=4000, .expectedOut=10, .expectedIn=11, .expectedIpLimit=2}, + {.name="legacy max_peers 0", .maxPeers=0, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=11, .expectedIpLimit=2}, + {.name="legacy max_peers 5", .maxPeers=5, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=0, .expectedIpLimit=1}, + {.name="legacy max_peers 20", .maxPeers=20, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=10, .expectedIpLimit=2}, + {.name="legacy max_peers 100", .maxPeers=100, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=15, .expectedIn=85, .expectedIpLimit=6}, + {.name="legacy max_peers 20, private", .maxPeers=20, .maxIn=100, .maxOut=10, .port=0, .expectedOut=20, .expectedIn=0, .expectedIpLimit=1}, + {.name="new in 100/out 10", .maxPeers={}, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=100, .expectedIpLimit=6}, + {.name="new in 0/out 10", .maxPeers={}, .maxIn=0, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=0, .expectedIpLimit=1}, + {.name="new in 100/out 10, private", .maxPeers={}, .maxIn=100, .maxOut=10, .port=0, .expectedOut=10, .expectedIn=0, .expectedIpLimit=6}}; + + for (auto const& testCase : cases) { - testcase("test addFixedPeer no port"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - try + SCOPED_TRACE(testCase.name); + + xrpl::Config serverConfig; + std::string configText; + auto max = 0; + if (testCase.maxPeers) { - logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.2")); - fail("invalid endpoint successfully added"); + max = *testCase.maxPeers; + configText += "[peers_max]\n" + std::to_string(max) + "\n[peers_in_max]\n" + + std::to_string(testCase.maxIn.value_or(0)) + "\n[peers_out_max]\n" + + std::to_string(testCase.maxOut.value_or(0)) + "\n"; } - catch (std::runtime_error const& e) + else if (testCase.maxIn && testCase.maxOut) { - pass(); + configText += "[peers_in_max]\n" + std::to_string(*testCase.maxIn) + + "\n[peers_out_max]\n" + std::to_string(*testCase.maxOut) + "\n"; } - } - void - testOnConnectedSelfConnection() - { - testcase("test onConnected self connection"); - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); + serverConfig.loadFromString(configText); + if (testCase.maxPeers || (!testCase.maxIn && !testCase.maxOut)) + { + EXPECT_EQ(serverConfig.PEERS_MAX, static_cast(max)); + EXPECT_EQ(serverConfig.PEERS_IN_MAX, 0u); + EXPECT_EQ(serverConfig.PEERS_OUT_MAX, 0u); + } + else + { + EXPECT_EQ(serverConfig.PEERS_IN_MAX, *testCase.maxIn); + EXPECT_EQ(serverConfig.PEERS_OUT_MAX, *testCase.maxOut); + } - auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1234"); - auto const [slot, r] = logic.newOutboundSlot(local); - BEAST_EXPECT(slot != nullptr); - BEAST_EXPECT(r == Result::Success); + Config const config = Config::makeConfig(serverConfig, testCase.port, false, 0); - // Must fail when a slot is to our own IP address - BEAST_EXPECT(!logic.onConnected(slot, local)); - logic.onClosed(slot); - } + Counts counts; + counts.onConfig(config); + EXPECT_EQ(counts.outMax(), testCase.expectedOut); + EXPECT_EQ(counts.inMax(), testCase.expectedIn); + EXPECT_EQ(config.ipLimit, testCase.expectedIpLimit); - void - testConfig() - { - // if peers_max is configured then peers_in_max and peers_out_max - // are ignored - auto run = [&](std::string const& test, - std::optional maxPeers, - std::optional maxIn, - std::optional maxOut, - std::uint16_t port, - std::uint16_t expectOut, - std::uint16_t expectIn, - std::uint16_t expectIpLimit) { - xrpl::Config c; - - testcase(test); - - std::string toLoad; - int max = 0; - if (maxPeers) - { - max = maxPeers.value(); - toLoad += "[peers_max]\n" + std::to_string(max) + "\n" + "[peers_in_max]\n" + - std::to_string(maxIn.value_or(0)) + "\n" + "[peers_out_max]\n" + - std::to_string(maxOut.value_or(0)) + "\n"; - } - else if (maxIn && maxOut) - { - toLoad += "[peers_in_max]\n" + std::to_string(*maxIn) + "\n" + "[peers_out_max]\n" + - std::to_string(*maxOut) + "\n"; - } - - c.loadFromString(toLoad); - BEAST_EXPECT( - (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 && c.PEERS_OUT_MAX == 0) || - (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); - - Config const config = Config::makeConfig(c, port, false, 0); - - Counts counts; - counts.onConfig(config); - BEAST_EXPECT( - counts.outMax() == expectOut && counts.inMax() == expectIn && - config.ipLimit == expectIpLimit); - - TestStore store; - TestChecker checker; - TestStopwatch clock; - Logic logic(clock, store, checker, journal_); - logic.config(config); - - BEAST_EXPECT(logic.config() == config); - }; - - // if max_peers == 0 => maxPeers = 21, - // else if max_peers < 10 => maxPeers = 10 else maxPeers = - // max_peers - // expectOut => if legacy => max(0.15 * maxPeers, 10), - // if legacy && !wantIncoming => maxPeers else max_out_peers - // expectIn => if legacy && wantIncoming => maxPeers - outPeers - // else if !wantIncoming => 0 else max_in_peers - // ipLimit => if expectIn <= 21 => 2 else 2 + min(5, expectIn/21) - // ipLimit = max(1, min(ipLimit, expectIn/2)) - - // legacy test with max_peers - run("legacy no config", {}, {}, {}, 4000, 10, 11, 2); - run("legacy max_peers 0", 0, 100, 10, 4000, 10, 11, 2); - run("legacy max_peers 5", 5, 100, 10, 4000, 10, 0, 1); - run("legacy max_peers 20", 20, 100, 10, 4000, 10, 10, 2); - run("legacy max_peers 100", 100, 100, 10, 4000, 15, 85, 6); - run("legacy max_peers 20, private", 20, 100, 10, 0, 20, 0, 1); - - // test with max_in_peers and max_out_peers - run("new in 100/out 10", {}, 100, 10, 4000, 10, 100, 6); - run("new in 0/out 10", {}, 0, 10, 4000, 10, 0, 1); - run("new in 100/out 10, private", {}, 100, 10, 0, 10, 0, 6); + NiceMock store; + allowEmptyStore(store); + NiceMock checker; + TestStopwatch clock; + Logic> logic(clock, store, checker, journal()); + logic.config(config); + + EXPECT_EQ(logic.config(), config); } +} - void - testInvalidConfig() - { - testcase("invalid config"); - - auto run = [&](std::string const& toLoad) { - xrpl::Config c; - try - { - c.loadFromString(toLoad); - fail(); - } - catch (...) - { - pass(); - } - }; - run(R"xrpldConfig( +TEST(PeerFinderConfig, RejectsIncompleteOrOutOfRangePeerLimits) +{ + std::vector const configs{ + R"xrpldConfig( [peers_in_max] 100 -)xrpldConfig"); - run(R"xrpldConfig( +)xrpldConfig", + R"xrpldConfig( [peers_out_max] 100 -)xrpldConfig"); - run(R"xrpldConfig( +)xrpldConfig", + R"xrpldConfig( [peers_in_max] 100 [peers_out_max] 5 -)xrpldConfig"); - run(R"xrpldConfig( +)xrpldConfig", + R"xrpldConfig( [peers_in_max] 1001 [peers_out_max] 10 -)xrpldConfig"); - run(R"xrpldConfig( +)xrpldConfig", + R"xrpldConfig( [peers_in_max] 10 [peers_out_max] 1001 -)xrpldConfig"); - } +)xrpldConfig"}; - void - run() override + for (auto const& configText : configs) { - testBackoff1(); - testBackoff2(); - testDuplicateOutIn(); - testDuplicateInOut(); - testConfig(); - testInvalidConfig(); - testPeerLimitExceeded(); - testActivateDuplicatePeer(); - testActivateInboundDisabled(); - testAddFixedPeerNoPort(); - testOnConnectedSelfConnection(); + xrpl::Config config; + EXPECT_THROW(config.loadFromString(configText), std::exception); } -}; - -BEAST_DEFINE_TESTSUITE(PeerFinder, peerfinder, xrpl); +} +} // namespace } // namespace xrpl::PeerFinder diff --git a/src/tests/libxrpl/peerfinder/main.cpp b/src/tests/libxrpl/peerfinder/main.cpp new file mode 100644 index 00000000000..f9114bffc4d --- /dev/null +++ b/src/tests/libxrpl/peerfinder/main.cpp @@ -0,0 +1,9 @@ +#include +#include + +int +main(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} From 591aed5f900cc8e651ded583e876cc217a2018ae Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 13:11:06 +0200 Subject: [PATCH 3/7] fix: run pre-commit hooks --- .../libxrpl/peerfinder/Livecache_test.cpp | 4 +- .../libxrpl/peerfinder/PeerFinder_test.cpp | 82 ++++++++++++++++--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/tests/libxrpl/peerfinder/Livecache_test.cpp b/src/tests/libxrpl/peerfinder/Livecache_test.cpp index f7050ad0598..f5bb53e6baa 100644 --- a/src/tests/libxrpl/peerfinder/Livecache_test.cpp +++ b/src/tests/libxrpl/peerfinder/Livecache_test.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -12,8 +14,6 @@ #include #include -#include "xrpl/beast/net/IPAddressV4.h" -#include "xrpl/beast/net/IPAddressV6.h" #include #include diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp index 5b6da8ee0d6..8441eaee4ce 100644 --- a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp +++ b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp @@ -11,7 +11,6 @@ #include #include - #include #include #include @@ -318,15 +317,78 @@ TEST(PeerFinderConfig, AppliesLegacyAndExplicitPeerLimits) }; std::vector const cases{ - {.name="legacy no config", .maxPeers={}, .maxIn={}, .maxOut={}, .port=4000, .expectedOut=10, .expectedIn=11, .expectedIpLimit=2}, - {.name="legacy max_peers 0", .maxPeers=0, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=11, .expectedIpLimit=2}, - {.name="legacy max_peers 5", .maxPeers=5, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=0, .expectedIpLimit=1}, - {.name="legacy max_peers 20", .maxPeers=20, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=10, .expectedIpLimit=2}, - {.name="legacy max_peers 100", .maxPeers=100, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=15, .expectedIn=85, .expectedIpLimit=6}, - {.name="legacy max_peers 20, private", .maxPeers=20, .maxIn=100, .maxOut=10, .port=0, .expectedOut=20, .expectedIn=0, .expectedIpLimit=1}, - {.name="new in 100/out 10", .maxPeers={}, .maxIn=100, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=100, .expectedIpLimit=6}, - {.name="new in 0/out 10", .maxPeers={}, .maxIn=0, .maxOut=10, .port=4000, .expectedOut=10, .expectedIn=0, .expectedIpLimit=1}, - {.name="new in 100/out 10, private", .maxPeers={}, .maxIn=100, .maxOut=10, .port=0, .expectedOut=10, .expectedIn=0, .expectedIpLimit=6}}; + {.name = "legacy no config", + .maxPeers = {}, + .maxIn = {}, + .maxOut = {}, + .port = 4000, + .expectedOut = 10, + .expectedIn = 11, + .expectedIpLimit = 2}, + {.name = "legacy max_peers 0", + .maxPeers = 0, + .maxIn = 100, + .maxOut = 10, + .port = 4000, + .expectedOut = 10, + .expectedIn = 11, + .expectedIpLimit = 2}, + {.name = "legacy max_peers 5", + .maxPeers = 5, + .maxIn = 100, + .maxOut = 10, + .port = 4000, + .expectedOut = 10, + .expectedIn = 0, + .expectedIpLimit = 1}, + {.name = "legacy max_peers 20", + .maxPeers = 20, + .maxIn = 100, + .maxOut = 10, + .port = 4000, + .expectedOut = 10, + .expectedIn = 10, + .expectedIpLimit = 2}, + {.name = "legacy max_peers 100", + .maxPeers = 100, + .maxIn = 100, + .maxOut = 10, + .port = 4000, + .expectedOut = 15, + .expectedIn = 85, + .expectedIpLimit = 6}, + {.name = "legacy max_peers 20, private", + .maxPeers = 20, + .maxIn = 100, + .maxOut = 10, + .port = 0, + .expectedOut = 20, + .expectedIn = 0, + .expectedIpLimit = 1}, + {.name = "new in 100/out 10", + .maxPeers = {}, + .maxIn = 100, + .maxOut = 10, + .port = 4000, + .expectedOut = 10, + .expectedIn = 100, + .expectedIpLimit = 6}, + {.name = "new in 0/out 10", + .maxPeers = {}, + .maxIn = 0, + .maxOut = 10, + .port = 4000, + .expectedOut = 10, + .expectedIn = 0, + .expectedIpLimit = 1}, + {.name = "new in 100/out 10, private", + .maxPeers = {}, + .maxIn = 100, + .maxOut = 10, + .port = 0, + .expectedOut = 10, + .expectedIn = 0, + .expectedIpLimit = 6}}; for (auto const& testCase : cases) { From c98705e1e9648ccba9e2f68697b4dcf495e09e64 Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 13:16:27 +0200 Subject: [PATCH 4/7] fix: Run levelization --- .github/scripts/levelization/results/ordering.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index c2000d17681..12732f8ab9e 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -143,12 +143,6 @@ test.overlay > xrpl.protocol test.overlay > xrpl.resource test.overlay > xrpl.server test.overlay > xrpl.shamap -test.peerfinder > test.beast -test.peerfinder > test.unit_test -test.peerfinder > xrpl.basics -test.peerfinder > xrpld.core -test.peerfinder > xrpld.peerfinder -test.peerfinder > xrpl.protocol test.protocol > test.jtx test.protocol > test.unit_test test.protocol > xrpl.basics @@ -189,6 +183,8 @@ test.unit_test > xrpl.basics test.unit_test > xrpl.protocol tests.libxrpl > xrpl.basics tests.libxrpl > xrpl.core +tests.libxrpl > xrpld.core +tests.libxrpl > xrpld.peerfinder tests.libxrpl > xrpl.json tests.libxrpl > xrpl.ledger tests.libxrpl > xrpl.net From ee8a026287f572c8141f16f670c1b680ac457351 Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 13:41:32 +0200 Subject: [PATCH 5/7] fix: Run clang tidy --- src/tests/libxrpl/peerfinder/PeerFinder_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp index 8441eaee4ce..1827d9af406 100644 --- a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp +++ b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp @@ -419,8 +419,8 @@ TEST(PeerFinderConfig, AppliesLegacyAndExplicitPeerLimits) } else { - EXPECT_EQ(serverConfig.PEERS_IN_MAX, *testCase.maxIn); - EXPECT_EQ(serverConfig.PEERS_OUT_MAX, *testCase.maxOut); + EXPECT_EQ(serverConfig.PEERS_IN_MAX, testCase.maxIn.value_or(0)); + EXPECT_EQ(serverConfig.PEERS_OUT_MAX, testCase.maxOut.value_or(0)); } Config const config = Config::makeConfig(serverConfig, testCase.port, false, 0); From 7c391775cfda317f79df15e953c4927a3bc76b81 Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 15:46:02 +0200 Subject: [PATCH 6/7] fix: Use snake_case in tests --- .../libxrpl/peerfinder/Livecache_test.cpp | 10 ++++----- .../libxrpl/peerfinder/PeerFinder_test.cpp | 22 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tests/libxrpl/peerfinder/Livecache_test.cpp b/src/tests/libxrpl/peerfinder/Livecache_test.cpp index f5bb53e6baa..5ef0463d608 100644 --- a/src/tests/libxrpl/peerfinder/Livecache_test.cpp +++ b/src/tests/libxrpl/peerfinder/Livecache_test.cpp @@ -91,7 +91,7 @@ sameEndpoints(std::vector const& lhs, std::vector const& rhs } // namespace -TEST(Livecache, BasicInsert) +TEST(Livecache, basic_insert) { TestStopwatch clock; Livecache<> cache(clock, journal()); @@ -110,7 +110,7 @@ TEST(Livecache, BasicInsert) EXPECT_EQ(cache.size(), 20u); } -TEST(Livecache, InsertUpdateKeepsLowestHopCount) +TEST(Livecache, insert_update_keeps_lowest_hop_count) { TestStopwatch clock; Livecache<> cache(clock, journal()); @@ -136,7 +136,7 @@ TEST(Livecache, InsertUpdateKeepsLowestHopCount) EXPECT_EQ((cache.hops.begin() + 1)->begin()->hops, 1u); } -TEST(Livecache, ExpireRemovesEntriesAfterTtl) +TEST(Livecache, expire_removes_entries_after_ttl) { using namespace std::chrono_literals; @@ -158,7 +158,7 @@ TEST(Livecache, ExpireRemovesEntriesAfterTtl) EXPECT_TRUE(cache.empty()); } -TEST(Livecache, HistogramCountsAllEntries) +TEST(Livecache, histogram_counts_all_entries) { constexpr auto kNUM_ENDPOINTS = 40; @@ -185,7 +185,7 @@ TEST(Livecache, HistogramCountsAllEntries) EXPECT_EQ(sum, kNUM_ENDPOINTS); } -TEST(Livecache, ShufflePreservesBucketContents) +TEST(Livecache, shuffle_preserves_bucket_contents) { TestStopwatch clock; Livecache<> cache(clock, journal()); diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp index 1827d9af406..5544ee3db7c 100644 --- a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp +++ b/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp @@ -99,7 +99,7 @@ class PeerFinderTest : public ::testing::Test Logic> logic_{clock_, store_, checker_, journal()}; }; -TEST_F(PeerFinderTest, BackoffLimitsRepeatedConnectionAttempts) +TEST_F(PeerFinderTest, backoff_limits_repeated_connection_attempts) { auto constexpr kSECONDS = 10000; @@ -127,7 +127,7 @@ TEST_F(PeerFinderTest, BackoffLimitsRepeatedConnectionAttempts) EXPECT_LT(attempts, 20u); } -TEST_F(PeerFinderTest, ActivatedPeerBackoffAllowsAtMostOneAttemptPerMinute) +TEST_F(PeerFinderTest, activated_peer_backoff_allows_at_most_one_attempt_per_minute) { auto constexpr kSECONDS = 10000; @@ -158,7 +158,7 @@ TEST_F(PeerFinderTest, ActivatedPeerBackoffAllowsAtMostOneAttemptPerMinute) EXPECT_LE(attempts, (kSECONDS + 59u) / 60u); } -TEST_F(PeerFinderTest, DuplicateInboundSlotIsRejectedForExistingOutboundSlot) +TEST_F(PeerFinderTest, duplicate_inbound_slot_is_rejected_for_existing_outbound_slot) { configure(); @@ -179,7 +179,7 @@ TEST_F(PeerFinderTest, DuplicateInboundSlotIsRejectedForExistingOutboundSlot) logic_.onClosed(slot1); } -TEST_F(PeerFinderTest, DuplicateOutboundSlotIsRejectedForExistingInboundSlot) +TEST_F(PeerFinderTest, duplicate_outbound_slot_is_rejected_for_existing_inbound_slot) { configure(); @@ -201,7 +201,7 @@ TEST_F(PeerFinderTest, DuplicateOutboundSlotIsRejectedForExistingInboundSlot) logic_.onClosed(slot1); } -TEST_F(PeerFinderTest, PeerLimitExceededRejectsAdditionalInboundSlot) +TEST_F(PeerFinderTest, peer_limit_exceeded_rejects_additional_inbound_slot) { configure(); @@ -224,7 +224,7 @@ TEST_F(PeerFinderTest, PeerLimitExceededRejectsAdditionalInboundSlot) logic_.onClosed(slot); } -TEST_F(PeerFinderTest, ActivateRejectsDuplicatePublicKey) +TEST_F(PeerFinderTest, activate_rejects_duplicate_public_key) { configure(); @@ -251,7 +251,7 @@ TEST_F(PeerFinderTest, ActivateRejectsDuplicatePublicKey) logic_.onClosed(slot2); } -TEST_F(PeerFinderTest, ActivateRejectsInboundWhenInboundConnectionsAreDisabled) +TEST_F(PeerFinderTest, activate_rejects_inbound_when_inbound_connections_are_disabled) { configure(); @@ -286,12 +286,12 @@ TEST_F(PeerFinderTest, ActivateRejectsInboundWhenInboundConnectionsAreDisabled) logic_.onClosed(slot); } -TEST_F(PeerFinderTest, AddFixedPeerRejectsEndpointWithoutPort) +TEST_F(PeerFinderTest, add_fixed_peer_rejects_endpoint_without_port) { EXPECT_THROW(logic_.addFixedPeer("test", endpoint("65.0.0.2")), std::runtime_error); } -TEST_F(PeerFinderTest, OnConnectedRejectsSelfConnection) +TEST_F(PeerFinderTest, on_connected_rejects_self_connection) { auto const local = endpoint("65.0.0.2:1234"); auto const [slot, result] = logic_.newOutboundSlot(local); @@ -302,7 +302,7 @@ TEST_F(PeerFinderTest, OnConnectedRejectsSelfConnection) logic_.onClosed(slot); } -TEST(PeerFinderConfig, AppliesLegacyAndExplicitPeerLimits) +TEST(PeerFinderConfig, applies_legacy_and_explicit_peer_limits) { struct ConfigCase { @@ -442,7 +442,7 @@ TEST(PeerFinderConfig, AppliesLegacyAndExplicitPeerLimits) } } -TEST(PeerFinderConfig, RejectsIncompleteOrOutOfRangePeerLimits) +TEST(PeerFinderConfig, rejects_incomplete_or_out_of_range_peer_limits) { std::vector const configs{ R"xrpldConfig( From 6b34d0f7ce91486b5d33d3f522e68791c06dab2c Mon Sep 17 00:00:00 2001 From: Marek Foss Date: Wed, 6 May 2026 15:53:03 +0200 Subject: [PATCH 7/7] fix: Remove _test suffix from peerfinder test filenames --- .../libxrpl/peerfinder/{Livecache_test.cpp => Livecache.cpp} | 3 ++- .../libxrpl/peerfinder/{PeerFinder_test.cpp => PeerFinder.cpp} | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename src/tests/libxrpl/peerfinder/{Livecache_test.cpp => Livecache.cpp} (99%) rename src/tests/libxrpl/peerfinder/{PeerFinder_test.cpp => PeerFinder.cpp} (100%) diff --git a/src/tests/libxrpl/peerfinder/Livecache_test.cpp b/src/tests/libxrpl/peerfinder/Livecache.cpp similarity index 99% rename from src/tests/libxrpl/peerfinder/Livecache_test.cpp rename to src/tests/libxrpl/peerfinder/Livecache.cpp index 5ef0463d608..0654eec3575 100644 --- a/src/tests/libxrpl/peerfinder/Livecache_test.cpp +++ b/src/tests/libxrpl/peerfinder/Livecache.cpp @@ -1,5 +1,6 @@ -#include #include + +#include #include #include diff --git a/src/tests/libxrpl/peerfinder/PeerFinder_test.cpp b/src/tests/libxrpl/peerfinder/PeerFinder.cpp similarity index 100% rename from src/tests/libxrpl/peerfinder/PeerFinder_test.cpp rename to src/tests/libxrpl/peerfinder/PeerFinder.cpp