Skip to content

Commit 72e30e8

Browse files
committed
Add unit tests for ProtectEvictionCandidatesByRatio()
Thank you to Vasil Dimov (vasild) for the suggestion to use std::unordered_set rather than std::vector for the IsProtected() peer id arguments.
1 parent ca63b53 commit 72e30e8

File tree

1 file changed

+97
-7
lines changed

1 file changed

+97
-7
lines changed

src/test/net_peer_eviction_tests.cpp

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
1717

18+
namespace {
19+
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
20+
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
21+
} // namespace
22+
1823
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
1924
{
2025
std::vector<NodeEvictionCandidate> candidates;
@@ -36,6 +41,98 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
3641
return candidates;
3742
}
3843

44+
// Create `num_peers` random nodes, apply setup function `candidate_setup_fn`,
45+
// call ProtectEvictionCandidatesByRatio() to apply protection logic, and then
46+
// return true if all of `protected_peer_ids` and none of `unprotected_peer_ids`
47+
// are protected from eviction, i.e. removed from the eviction candidates.
48+
bool IsProtected(int num_peers,
49+
std::function<void(NodeEvictionCandidate&)> candidate_setup_fn,
50+
const std::unordered_set<NodeId>& protected_peer_ids,
51+
const std::unordered_set<NodeId>& unprotected_peer_ids,
52+
FastRandomContext& random_context)
53+
{
54+
std::vector<NodeEvictionCandidate> candidates{GetRandomNodeEvictionCandidates(num_peers, random_context)};
55+
for (NodeEvictionCandidate& candidate : candidates) {
56+
candidate_setup_fn(candidate);
57+
}
58+
Shuffle(candidates.begin(), candidates.end(), random_context);
59+
60+
const size_t size{candidates.size()};
61+
const size_t expected{size - size / 2}; // Expect half the candidates will be protected.
62+
ProtectEvictionCandidatesByRatio(candidates);
63+
BOOST_CHECK_EQUAL(candidates.size(), expected);
64+
65+
size_t unprotected_count{0};
66+
for (const NodeEvictionCandidate& candidate : candidates) {
67+
if (protected_peer_ids.count(candidate.id)) {
68+
// this peer should have been removed from the eviction candidates
69+
BOOST_TEST_MESSAGE(strprintf("expected candidate to be protected: %d", candidate.id));
70+
return false;
71+
}
72+
if (unprotected_peer_ids.count(candidate.id)) {
73+
// this peer remains in the eviction candidates, as expected
74+
++unprotected_count;
75+
}
76+
}
77+
78+
const bool is_protected{unprotected_count == unprotected_peer_ids.size()};
79+
if (!is_protected) {
80+
BOOST_TEST_MESSAGE(strprintf("unprotected: expected %d, actual %d",
81+
unprotected_peer_ids.size(), unprotected_count));
82+
}
83+
return is_protected;
84+
}
85+
86+
BOOST_AUTO_TEST_CASE(peer_protection_test)
87+
{
88+
FastRandomContext random_context{true};
89+
int num_peers{12};
90+
91+
// Expect half of the peers with greatest uptime (the lowest nTimeConnected)
92+
// to be protected from eviction.
93+
BOOST_CHECK(IsProtected(
94+
num_peers, [](NodeEvictionCandidate& c) {
95+
c.nTimeConnected = c.id;
96+
c.m_is_local = false;
97+
},
98+
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
99+
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
100+
random_context));
101+
102+
// Verify in the opposite direction.
103+
BOOST_CHECK(IsProtected(
104+
num_peers, [num_peers](NodeEvictionCandidate& c) {
105+
c.nTimeConnected = num_peers - c.id;
106+
c.m_is_local = false;
107+
},
108+
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
109+
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
110+
random_context));
111+
112+
// Test protection of localhost peers...
113+
114+
// Expect 1/4 localhost peers to be protected from eviction,
115+
// independently of other characteristics.
116+
BOOST_CHECK(IsProtected(
117+
num_peers, [](NodeEvictionCandidate& c) {
118+
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
119+
},
120+
/* protected_peer_ids */ {1, 9, 11},
121+
/* unprotected_peer_ids */ {},
122+
random_context));
123+
124+
// Expect 1/4 localhost peers and 1/4 of the others to be protected
125+
// from eviction, sorted by longest uptime (lowest nTimeConnected).
126+
BOOST_CHECK(IsProtected(
127+
num_peers, [](NodeEvictionCandidate& c) {
128+
c.nTimeConnected = c.id;
129+
c.m_is_local = (c.id > 6);
130+
},
131+
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
132+
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
133+
random_context));
134+
}
135+
39136
// Returns true if any of the node ids in node_ids are selected for eviction.
40137
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
41138
{
@@ -59,11 +156,6 @@ bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandida
59156
return IsEvicted(candidates, node_ids, random_context);
60157
}
61158

62-
namespace {
63-
constexpr int NODE_EVICTION_TEST_ROUNDS{10};
64-
constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
65-
} // namespace
66-
67159
BOOST_AUTO_TEST_CASE(peer_eviction_test)
68160
{
69161
FastRandomContext random_context{true};
@@ -150,8 +242,6 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
150242
}
151243

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

0 commit comments

Comments
 (0)