Skip to content

Commit

Permalink
Add unit tests for ProtectEvictionCandidatesByRatio()
Browse files Browse the repository at this point in the history
Summary:
This is a backport of [[bitcoin/bitcoin#20197 | core#20197]] [4/6]
bitcoin/bitcoin@72e30e8

Depends on D10977

Test Plan: `ninja check`

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Differential Revision: https://reviews.bitcoinabc.org/D10978
  • Loading branch information
jonatack authored and PiRK committed Feb 4, 2022
1 parent 7128106 commit e1af69c
Showing 1 changed file with 100 additions and 9 deletions.
109 changes: 100 additions & 9 deletions src/test/net_peer_eviction_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)

namespace {
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
} // namespace

std::vector<NodeEvictionCandidate>
GetRandomNodeEvictionCandidates(const int n_candidates,
FastRandomContext &random_context) {
Expand Down Expand Up @@ -44,6 +49,101 @@ GetRandomNodeEvictionCandidates(const int n_candidates,
return candidates;
}

// Create `num_peers` random nodes, apply setup function `candidate_setup_fn`,
// call ProtectEvictionCandidatesByRatio() to apply protection logic, and then
// return true if all of `protected_peer_ids` and none of `unprotected_peer_ids`
// are protected from eviction, i.e. removed from the eviction candidates.
bool IsProtected(
int num_peers,
std::function<void(NodeEvictionCandidate &)> candidate_setup_fn,
const std::unordered_set<NodeId> &protected_peer_ids,
const std::unordered_set<NodeId> &unprotected_peer_ids,
FastRandomContext &random_context) {
std::vector<NodeEvictionCandidate> candidates{
GetRandomNodeEvictionCandidates(num_peers, random_context)};
for (NodeEvictionCandidate &candidate : candidates) {
candidate_setup_fn(candidate);
}
Shuffle(candidates.begin(), candidates.end(), random_context);

const size_t size{candidates.size()};
// Expect half the candidates will be protected.
const size_t expected{size - size / 2};
ProtectEvictionCandidatesByRatio(candidates);
BOOST_CHECK_EQUAL(candidates.size(), expected);

size_t unprotected_count{0};
for (const NodeEvictionCandidate &candidate : candidates) {
if (protected_peer_ids.count(candidate.id)) {
// this peer should have been removed from the eviction candidates
BOOST_TEST_MESSAGE(strprintf(
"expected candidate to be protected: %d", candidate.id));
return false;
}
if (unprotected_peer_ids.count(candidate.id)) {
// this peer remains in the eviction candidates, as expected
++unprotected_count;
}
}

const bool is_protected{unprotected_count == unprotected_peer_ids.size()};
if (!is_protected) {
BOOST_TEST_MESSAGE(strprintf("unprotected: expected %d, actual %d",
unprotected_peer_ids.size(),
unprotected_count));
}
return is_protected;
}

BOOST_AUTO_TEST_CASE(peer_protection_test) {
FastRandomContext random_context{true};
int num_peers{12};

// Expect half of the peers with greatest uptime (the lowest nTimeConnected)
// to be protected from eviction.
BOOST_CHECK(IsProtected(
num_peers,
[](NodeEvictionCandidate &c) {
c.nTimeConnected = c.id;
c.m_is_local = false;
},
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11}, random_context));

// Verify in the opposite direction.
BOOST_CHECK(IsProtected(
num_peers,
[num_peers](NodeEvictionCandidate &c) {
c.nTimeConnected = num_peers - c.id;
c.m_is_local = false;
},
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5}, random_context));

// Test protection of localhost peers...

// Expect 1/4 localhost peers to be protected from eviction,
// independently of other characteristics.
BOOST_CHECK(IsProtected(
num_peers,
[](NodeEvictionCandidate &c) {
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
},
/* protected_peer_ids */ {1, 9, 11},
/* unprotected_peer_ids */ {}, random_context));

// Expect 1/4 localhost peers and 1/4 of the others to be protected
// from eviction, sorted by longest uptime (lowest nTimeConnected).
BOOST_CHECK(IsProtected(
num_peers,
[](NodeEvictionCandidate &c) {
c.nTimeConnected = c.id;
c.m_is_local = (c.id > 6);
},
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11}, random_context));
}

// Returns true if any of the node ids in node_ids are selected for eviction.
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates,
const std::unordered_set<NodeId> &node_ids,
Expand Down Expand Up @@ -72,11 +172,6 @@ bool IsEvicted(const int number_of_nodes,
return IsEvicted(candidates, node_ids, random_context);
}

namespace {
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
} // namespace

BOOST_AUTO_TEST_CASE(node_eviction_test) {
FastRandomContext random_context{true};

Expand Down Expand Up @@ -210,10 +305,6 @@ BOOST_AUTO_TEST_CASE(node_eviction_test) {
}

// Cases left to test:
// * "Protect the half of the remaining nodes which have been
// connected the longest. [...]"
// * "Pick out up to 1/4 peers that are localhost, sorted by longest
// uptime. [...]"
// * "If any remaining peers are preferred for eviction consider
// only them. [...]"
// * "Identify the network group with the most connections and
Expand Down

0 comments on commit e1af69c

Please sign in to comment.