mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-09 12:57:03 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be210e27da | ||
|
|
f7243fca37 | ||
|
|
1f6efd5736 | ||
|
|
54fbad3a7e | ||
|
|
1315489a7b | ||
|
|
c189fa0e82 | ||
|
|
3eae03289e | ||
|
|
e5e5a45b91 | ||
|
|
7a71beb892 | ||
|
|
74a894229f | ||
|
|
c81c1f70c8 |
No files matched your search
+1
-1
@@ -10,7 +10,7 @@ if(POLICY CMP0167)
|
||||
endif()
|
||||
|
||||
set (opendht_VERSION_MAJOR 4)
|
||||
set (opendht_VERSION_MINOR 3.0)
|
||||
set (opendht_VERSION_MINOR 4.0)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
|
||||
+19
-1
@@ -5,7 +5,7 @@ project(
|
||||
'cython',
|
||||
license: 'MIT',
|
||||
license_files: 'LICENSE',
|
||||
version: '4.3.0',
|
||||
version: '4.4.0',
|
||||
meson_version: '>=1.1.0',
|
||||
default_options: ['cpp_std=c++20', 'warning_level=3'],
|
||||
)
|
||||
@@ -292,6 +292,24 @@ if get_option('tests').enabled()
|
||||
)
|
||||
test('NetworkEngine', test_networkengine)
|
||||
|
||||
test_opcache = executable(
|
||||
'test_opcache',
|
||||
'tests/test_opcache.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_inc,
|
||||
dependencies: [opendht_dep, cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('OpCache', test_opcache)
|
||||
|
||||
test_valuecache = executable(
|
||||
'test_valuecache',
|
||||
'tests/test_valuecache.cpp',
|
||||
'tests/tests_runner.cpp',
|
||||
include_directories: opendht_inc,
|
||||
dependencies: [opendht_dep, cppunit, jsoncpp, fmt, openssl, msgpack],
|
||||
)
|
||||
test('ValueCache', test_valuecache)
|
||||
|
||||
test_crypto = executable(
|
||||
'test_crypto',
|
||||
'tests/test_crypto.cpp',
|
||||
|
||||
+4
-2
@@ -39,8 +39,10 @@ OpValueCache::onValuesExpired(const std::vector<Sp<Value>>& vals, const system_c
|
||||
if (vit != values.end()) {
|
||||
if (vit->second.updated > t)
|
||||
continue;
|
||||
if (v->seq < vit->second.data->seq)
|
||||
continue; // source held an older version, not counted
|
||||
// Mirror onValuesAdded: a version that was never counted (different
|
||||
// content, not a newer seq) must not decrement the refcount
|
||||
if (*v != *vit->second.data and v->seq <= vit->second.data->seq)
|
||||
continue;
|
||||
|
||||
vit->second.updated = t;
|
||||
vit->second.refCount--;
|
||||
|
||||
+1
-1
@@ -189,7 +189,7 @@ private:
|
||||
v->second.created = now;
|
||||
v->second.expiration = now + te;
|
||||
v->second.typeExpiration = te;
|
||||
if (value->seq > v->second.data->seq) {
|
||||
if (*value != *v->second.data and value->seq > v->second.data->seq) {
|
||||
// edited value (same rule as OpValueCache): emit as add
|
||||
v->second.data = value;
|
||||
nvals.emplace_back(value);
|
||||
|
||||
+61
-47
@@ -16,6 +16,21 @@ using namespace std::chrono_literals;
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
namespace {
|
||||
// The result is produced on the DHT thread, which may be blocked in a listen
|
||||
// callback waiting for the same mutex: never hold it while waiting.
|
||||
dht::PushNotificationResult
|
||||
sendPush(dht::DhtRunner& node, const std::map<std::string, std::string>& data, std::unique_lock<std::mutex>& lk)
|
||||
{
|
||||
lk.unlock();
|
||||
auto ret = node.pushNotificationReceived(data).get();
|
||||
lk.lock();
|
||||
return ret;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
void
|
||||
DhtProxyTester::setUp()
|
||||
{
|
||||
@@ -332,7 +347,7 @@ DhtProxyTester::testPushListenValueEdit()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for first add callback
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
@@ -357,7 +372,7 @@ DhtProxyTester::testPushListenValueEdit()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for the second add
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
@@ -389,7 +404,7 @@ DhtProxyTester::testPushListenValueEdit()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
|
||||
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
|
||||
|
||||
// Wait for the expire callback
|
||||
CPPUNIT_ASSERT_MESSAGE("Should receive expire callback via push", cv.wait_for(lk, 10s, [&] {
|
||||
@@ -541,7 +556,7 @@ DhtProxyTester::testPushNotification()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::Values);
|
||||
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::Values);
|
||||
|
||||
// Wait for client to receive the value
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return gotValues; }));
|
||||
@@ -570,7 +585,7 @@ DhtProxyTester::testPushNotification()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::Values);
|
||||
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::Values);
|
||||
|
||||
// Wait for client to receive the edited value
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return gotValues; }));
|
||||
@@ -587,7 +602,7 @@ DhtProxyTester::testPushNotification()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
|
||||
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
|
||||
|
||||
cv.wait_for(lk, 500ms, [&] { return gotExpiredValues; });
|
||||
|
||||
@@ -1118,7 +1133,8 @@ DhtProxyTester::testPushMultiValueEditExpire()
|
||||
auto key = dht::InfoHash::get("pushMultiEditExpire");
|
||||
auto identity = dht::crypto::generateIdentity("MultiEditAuthor");
|
||||
|
||||
struct Event {
|
||||
struct Event
|
||||
{
|
||||
dht::Value::Id id;
|
||||
uint16_t seq;
|
||||
bool expired;
|
||||
@@ -1176,7 +1192,7 @@ DhtProxyTester::testPushMultiValueEditExpire()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for both values to arrive
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
@@ -1209,7 +1225,7 @@ DhtProxyTester::testPushMultiValueEditExpire()
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
push_data.erase("exp");
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for the edited value (id=100, seq=2) to arrive
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
@@ -1232,13 +1248,11 @@ DhtProxyTester::testPushMultiValueEditExpire()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
|
||||
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
|
||||
|
||||
// Wait for expire callback for value B
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
return std::any_of(events.begin(), events.end(), [](const Event& e) {
|
||||
return e.id == 200 && e.expired;
|
||||
});
|
||||
return std::any_of(events.begin(), events.end(), [](const Event& e) { return e.id == 200 && e.expired; });
|
||||
}));
|
||||
|
||||
// Verify: value A (edited) should NOT have been expired
|
||||
@@ -1288,7 +1302,8 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
|
||||
auto key = dht::InfoHash::get("pushRefreshNoExpire");
|
||||
auto identity = dht::crypto::generateIdentity("RefreshAuthor");
|
||||
|
||||
struct Event {
|
||||
struct Event
|
||||
{
|
||||
dht::Value::Id id;
|
||||
bool expired;
|
||||
};
|
||||
@@ -1328,7 +1343,7 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for all 3 values to arrive
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
@@ -1354,7 +1369,7 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
push_data.erase("exp");
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait a bit for any callbacks
|
||||
cv.wait_for(lk, 1s);
|
||||
@@ -1375,13 +1390,11 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
|
||||
// Wait for expire callback
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
|
||||
return std::any_of(events.begin(), events.end(), [](const Event& e) {
|
||||
return e.id == 20 && e.expired;
|
||||
});
|
||||
return std::any_of(events.begin(), events.end(), [](const Event& e) { return e.id == 20 && e.expired; });
|
||||
}));
|
||||
|
||||
// Verify only value 20 expired, not 10 or 30
|
||||
@@ -1426,7 +1439,12 @@ DhtProxyTester::testProxyListenEditChain()
|
||||
auto key = dht::InfoHash::get("proxyEditChain");
|
||||
auto identity = dht::crypto::generateIdentity("ProxyChainEditTester");
|
||||
|
||||
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
|
||||
struct Event
|
||||
{
|
||||
dht::Value::Id id;
|
||||
uint16_t seq;
|
||||
bool expired;
|
||||
};
|
||||
std::vector<Event> events;
|
||||
|
||||
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
|
||||
@@ -1493,8 +1511,7 @@ DhtProxyTester::testProxyListenEditChain()
|
||||
for (const auto& e : events)
|
||||
if (!e.expired && e.seq > maxSeq)
|
||||
maxSeq = e.seq;
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Latest add should be the final version",
|
||||
(uint16_t) NUM_EDITS, maxSeq);
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Latest add should be the final version", (uint16_t) NUM_EDITS, maxSeq);
|
||||
}
|
||||
|
||||
// Wait for natural expiration
|
||||
@@ -1518,8 +1535,7 @@ DhtProxyTester::testProxyListenEditChain()
|
||||
}
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should have exactly 1 expire via proxy", 1, expireCount);
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Expired value should be the latest",
|
||||
(uint16_t) NUM_EDITS, expiredSeq);
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Expired value should be the latest", (uint16_t) NUM_EDITS, expiredSeq);
|
||||
}
|
||||
|
||||
nodeClient.cancelListen(key, ftoken.get());
|
||||
@@ -1557,7 +1573,12 @@ DhtProxyTester::testPushRapidEditsNoPhantom()
|
||||
auto key = dht::InfoHash::get("pushRapidEdits");
|
||||
auto identity = dht::crypto::generateIdentity("RapidEditAuthor");
|
||||
|
||||
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
|
||||
struct Event
|
||||
{
|
||||
dht::Value::Id id;
|
||||
uint16_t seq;
|
||||
bool expired;
|
||||
};
|
||||
std::vector<Event> events;
|
||||
|
||||
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
|
||||
@@ -1593,12 +1614,9 @@ DhtProxyTester::testPushRapidEditsNoPhantom()
|
||||
std::map<std::string, std::string> push_data;
|
||||
push_data["key"] = key.toString();
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
lk.unlock();
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
lk.lock();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
}
|
||||
|
||||
// Wait for edits to arrive (at least 2: initial + latest)
|
||||
@@ -1669,7 +1687,12 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
|
||||
auto key = dht::InfoHash::get("pushRefreshAfterEdit");
|
||||
auto identity = dht::crypto::generateIdentity("RefreshEditAuthor");
|
||||
|
||||
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
|
||||
struct Event
|
||||
{
|
||||
dht::Value::Id id;
|
||||
uint16_t seq;
|
||||
bool expired;
|
||||
};
|
||||
std::vector<Event> events;
|
||||
|
||||
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
|
||||
@@ -1705,12 +1728,9 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
|
||||
std::map<std::string, std::string> push_data;
|
||||
push_data["key"] = key.toString();
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
lk.unlock();
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
lk.lock();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
}
|
||||
|
||||
// Wait for initial add
|
||||
@@ -1742,12 +1762,9 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
|
||||
std::map<std::string, std::string> push_data;
|
||||
push_data["key"] = key.toString();
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
lk.unlock();
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
lk.lock();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
}
|
||||
|
||||
// Wait for the edit add (seq=3)
|
||||
@@ -1765,12 +1782,9 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
|
||||
std::map<std::string, std::string> push_data;
|
||||
push_data["key"] = key.toString();
|
||||
push_data["t"] = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
lk.unlock();
|
||||
nodeClient.pushNotificationReceived(push_data).get();
|
||||
lk.lock();
|
||||
sendPush(nodeClient, push_data, lk);
|
||||
}
|
||||
|
||||
// Wait a bit for any callbacks
|
||||
|
||||
@@ -210,6 +210,32 @@ OpCacheTester::testStaleExpireIgnored()
|
||||
CPPUNIT_ASSERT_EQUAL((uint16_t) 2, events.back().seq);
|
||||
}
|
||||
|
||||
void
|
||||
OpCacheTester::testSameSeqConflictExpireIgnored()
|
||||
{
|
||||
// A different payload with the same seq (e.g. the same signed content
|
||||
// re-signed with a randomized signature) is ignored on add and not
|
||||
// counted: its expiration must be ignored too.
|
||||
std::vector<Event> events;
|
||||
dht::OpValueCache cache(recordEvents(events));
|
||||
|
||||
auto x = makeValue(60, 0, "x");
|
||||
auto y = makeValue(60, 0, "y");
|
||||
|
||||
cache.onValuesAdded({x}); // A
|
||||
cache.onValuesAdded({y}); // B: same seq, different content
|
||||
CPPUNIT_ASSERT_EQUAL(1u, count(events, false));
|
||||
CPPUNIT_ASSERT(cache.get(60)->data == x->data);
|
||||
|
||||
cache.onValuesExpired({y}); // B
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("An uncounted version must not expire the value", (size_t) 1, cache.size());
|
||||
CPPUNIT_ASSERT_EQUAL(0u, count(events, true));
|
||||
|
||||
cache.onValuesExpired({x}); // A
|
||||
CPPUNIT_ASSERT_EQUAL((size_t) 0, cache.size());
|
||||
CPPUNIT_ASSERT_EQUAL(1u, count(events, true));
|
||||
}
|
||||
|
||||
void
|
||||
OpCacheTester::testCallbacks()
|
||||
{
|
||||
@@ -543,6 +569,29 @@ OpCacheTester::testSourceDropKeepsValue()
|
||||
CPPUNIT_ASSERT_EQUAL((size_t) 0, searchCache.get(dht::Value::Filter {}).size());
|
||||
}
|
||||
|
||||
void
|
||||
OpCacheTester::testSeqOnlyRefreshNotCounted()
|
||||
{
|
||||
// A node re-announcing the same content with a bumped seq must not be
|
||||
// counted as an additional source by the OpValueCache above it, or the
|
||||
// value would survive the node's departure.
|
||||
std::vector<Event> events;
|
||||
dht::OpValueCache parent(recordEvents(events));
|
||||
dht::TypeStore types;
|
||||
auto now = dht::clock::now();
|
||||
|
||||
auto v = makeValue(42, 0, "same");
|
||||
auto vBumped = makeValue(42, 1, "same");
|
||||
{
|
||||
dht::ValueCache node([&](const Values& vals, bool expired) { parent.onValue(vals, expired); });
|
||||
node.onValues({v}, {}, {}, types, now);
|
||||
node.onValues({vBumped}, {}, {}, types, now);
|
||||
CPPUNIT_ASSERT_EQUAL(1u, count(events, false));
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Node gone: its value must be expired once", 1u, count(events, true));
|
||||
CPPUNIT_ASSERT_EQUAL((size_t) 0, parent.size());
|
||||
}
|
||||
|
||||
void
|
||||
OpCacheTester::testHighChurnRefCountConsistency()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ class OpCacheTester : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testEditChainMultiSource);
|
||||
CPPUNIT_TEST(testStaleAddIgnored);
|
||||
CPPUNIT_TEST(testStaleExpireIgnored);
|
||||
CPPUNIT_TEST(testSameSeqConflictExpireIgnored);
|
||||
CPPUNIT_TEST(testCallbacks);
|
||||
CPPUNIT_TEST(testFilters);
|
||||
CPPUNIT_TEST(testSyncStatus);
|
||||
@@ -26,6 +27,7 @@ class OpCacheTester : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testGetCallbackReturnsFalse);
|
||||
CPPUNIT_TEST(testTimestampOrdering);
|
||||
CPPUNIT_TEST(testSourceDropKeepsValue);
|
||||
CPPUNIT_TEST(testSeqOnlyRefreshNotCounted);
|
||||
CPPUNIT_TEST(testHighChurnRefCountConsistency);
|
||||
CPPUNIT_TEST(testValueUpdateSingleSourcePhantom);
|
||||
CPPUNIT_TEST(testExpireAfterUpdate);
|
||||
@@ -43,6 +45,7 @@ public:
|
||||
void testEditChainMultiSource();
|
||||
void testStaleAddIgnored();
|
||||
void testStaleExpireIgnored();
|
||||
void testSameSeqConflictExpireIgnored();
|
||||
void testCallbacks();
|
||||
void testFilters();
|
||||
void testSyncStatus();
|
||||
@@ -53,6 +56,7 @@ public:
|
||||
void testGetCallbackReturnsFalse();
|
||||
void testTimestampOrdering();
|
||||
void testSourceDropKeepsValue();
|
||||
void testSeqOnlyRefreshNotCounted();
|
||||
void testHighChurnRefCountConsistency();
|
||||
void testValueUpdateSingleSourcePhantom();
|
||||
void testExpireAfterUpdate();
|
||||
|
||||
@@ -353,6 +353,47 @@ ValueCacheTester::testSameSeqEditIgnored()
|
||||
CPPUNIT_ASSERT_EQUAL(2, addCount);
|
||||
}
|
||||
|
||||
void
|
||||
ValueCacheTester::testSameContentHigherSeqIsRefresh()
|
||||
{
|
||||
// Value::operator== ignores seq: identical content with a bumped seq is
|
||||
// the same value re-announced, not an edition. Re-emitting it would make
|
||||
// a source-counting OpValueCache count this node twice.
|
||||
int addCount = 0;
|
||||
dht::ValueCache cache([&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
|
||||
if (!expired)
|
||||
addCount += vals.size();
|
||||
});
|
||||
dht::TypeStore types;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
auto v1 = std::make_shared<dht::Value>();
|
||||
v1->id = 1;
|
||||
v1->seq = 0;
|
||||
v1->data = {'A'};
|
||||
cache.onValues({v1}, {}, {}, types, now);
|
||||
CPPUNIT_ASSERT_EQUAL(1, addCount);
|
||||
|
||||
auto sameContent = std::make_shared<dht::Value>();
|
||||
sameContent->id = 1;
|
||||
sameContent->seq = 1;
|
||||
sameContent->data = {'A'};
|
||||
auto later = now + std::chrono::minutes(5);
|
||||
cache.onValues({sameContent}, {}, {}, types, later);
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Same content with a higher seq is a refresh, not an edit", 1, addCount);
|
||||
|
||||
// The refresh still extends the expiration (default type: 10 minutes)
|
||||
cache.expireValues(now + std::chrono::minutes(11));
|
||||
CPPUNIT_ASSERT_EQUAL((size_t) 1, cache.size());
|
||||
|
||||
auto edited = std::make_shared<dht::Value>();
|
||||
edited->id = 1;
|
||||
edited->seq = 2;
|
||||
edited->data = {'B'};
|
||||
cache.onValues({edited}, {}, {}, types, later);
|
||||
CPPUNIT_ASSERT_EQUAL(2, addCount);
|
||||
}
|
||||
|
||||
void
|
||||
ValueCacheTester::testShortExpirationRefreshLost()
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@ class ValueCacheTester : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testMaxValues);
|
||||
CPPUNIT_TEST(testUpdateTypeExpiration);
|
||||
CPPUNIT_TEST(testSameSeqEditIgnored);
|
||||
CPPUNIT_TEST(testSameContentHigherSeqIsRefresh);
|
||||
CPPUNIT_TEST(testShortExpirationRefreshLost);
|
||||
CPPUNIT_TEST(testShortExpirationHighChurn);
|
||||
CPPUNIT_TEST(testGracePeriodWhileSynced);
|
||||
@@ -40,6 +41,7 @@ public:
|
||||
void testMaxValues();
|
||||
void testUpdateTypeExpiration();
|
||||
void testSameSeqEditIgnored();
|
||||
void testSameContentHigherSeqIsRefresh();
|
||||
void testShortExpirationRefreshLost();
|
||||
void testShortExpirationHighChurn();
|
||||
void testGracePeriodWhileSynced();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "opendht",
|
||||
"version-string": "4.3.0",
|
||||
"version-string": "4.4.0",
|
||||
"port-version": 1,
|
||||
"description": "OpenDHT: A lightweight C++17 Distributed Hash Table library.",
|
||||
"homepage": "https://opendht.net",
|
||||
|
||||
Reference in new issue
Block a user