/* * Copyright (C) 2025 Savoir-faire Linux Inc. * * Author: GitHub Copilot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "valuecachetester.h" #include "../src/value_cache.h" namespace test { CPPUNIT_TEST_SUITE_REGISTRATION(ValueCacheTester); void ValueCacheTester::setUp() { } void ValueCacheTester::tearDown() { } void ValueCacheTester::testUpdate() { int addCount = 0; dht::ValueCache cache([&](const std::vector>& vals, bool expired){ if (!expired) addCount += vals.size(); }); dht::TypeStore types; auto v1 = std::make_shared(); v1->id = 1; v1->seq = 1; v1->data = {'A'}; auto v2 = std::make_shared(); v2->id = 1; v2->seq = 2; v2->data = {'B'}; auto now = std::chrono::steady_clock::now(); // Add v1 cache.onValues({v1}, {}, {}, types, now); CPPUNIT_ASSERT_EQUAL(1, addCount); // Update v1 -> v2 cache.onValues({v2}, {}, {}, types, now); CPPUNIT_ASSERT_EQUAL_MESSAGE("Callback should be called for update", 2, addCount); } void ValueCacheTester::testUpdateExpiration() { int addCount = 0; int expireCount = 0; dht::ValueCache cache([&](const std::vector>& vals, bool expired){ if (expired) expireCount += vals.size(); else addCount += vals.size(); }); dht::TypeStore types; auto v1 = std::make_shared(); v1->id = 1; v1->seq = 1; v1->data = {'A'}; auto v2 = std::make_shared(); v2->id = 1; v2->seq = 2; v2->data = {'B'}; auto now = std::chrono::steady_clock::now(); // Add v1 cache.onValues({v1}, {}, {}, types, now); CPPUNIT_ASSERT_EQUAL(1, addCount); CPPUNIT_ASSERT_EQUAL(0, expireCount); // Update v1 -> v2 cache.onValues({v2}, {}, {}, types, now); CPPUNIT_ASSERT_EQUAL(2, addCount); CPPUNIT_ASSERT_EQUAL_MESSAGE("Old value should be expired on update", 1, expireCount); } void ValueCacheTester::testExpiration() { int expireCount = 0; dht::ValueCache cache([&](const std::vector>& vals, bool expired){ if (expired) expireCount += vals.size(); }); dht::TypeStore types; auto v1 = std::make_shared(); v1->id = 10; v1->data = {'A'}; // Default expiration is 10 minutes auto now = std::chrono::steady_clock::now(); cache.onValues({v1}, {}, {}, types, now); // Advance time past expiration auto future = now + std::chrono::minutes(11); // expireValues returns next expiration time cache.expireValues(future); CPPUNIT_ASSERT_EQUAL(1, expireCount); CPPUNIT_ASSERT_EQUAL((size_t)0, cache.size()); } void ValueCacheTester::testRefresh() { int expireCount = 0; dht::ValueCache cache([&](const std::vector>& vals, bool expired){ if (expired) expireCount += vals.size(); }); dht::TypeStore types; auto v1 = std::make_shared(); v1->id = 20; v1->data = {'B'}; auto now = std::chrono::steady_clock::now(); cache.onValues({v1}, {}, {}, types, now); // Refresh at +5 mins auto later = now + std::chrono::minutes(5); cache.onValues({}, {v1->id}, {}, types, later); // Should not expire at +11 mins (original expiration was +10, so +10 from start) // But refresh should extend it to +10 from 'later' = +15 from start. auto checkTime = now + std::chrono::minutes(11); cache.expireValues(checkTime); CPPUNIT_ASSERT_EQUAL(0, expireCount); CPPUNIT_ASSERT_EQUAL((size_t)1, cache.size()); // Should expire at +16 mins auto future = now + std::chrono::minutes(16); cache.expireValues(future); CPPUNIT_ASSERT_EQUAL(1, expireCount); } void ValueCacheTester::testExplicitExpiration() { int expireCount = 0; dht::ValueCache cache([&](const std::vector>& vals, bool expired){ if (expired) expireCount += vals.size(); }); dht::TypeStore types; auto v1 = std::make_shared(); v1->id = 30; v1->data = {'C'}; auto now = std::chrono::steady_clock::now(); cache.onValues({v1}, {}, {}, types, now); cache.onValues({}, {}, {v1->id}, types, now); CPPUNIT_ASSERT_EQUAL(1, expireCount); CPPUNIT_ASSERT_EQUAL((size_t)0, cache.size()); } void ValueCacheTester::testClear() { dht::ValueCache cache([](const std::vector>&, bool){}); dht::TypeStore types; auto now = std::chrono::steady_clock::now(); auto v1 = std::make_shared(); v1->id = 1; auto v2 = std::make_shared(); v2->id = 2; cache.onValues({v1, v2}, {}, {}, types, now); CPPUNIT_ASSERT_EQUAL((size_t)2, cache.size()); auto cbs = cache.clear(); // Execute callbacks for (auto& cb : cbs) cb(); CPPUNIT_ASSERT_EQUAL((size_t)0, cache.size()); } void ValueCacheTester::testSyncStatus() { bool synced = false; dht::ValueCache cache([](const std::vector>&, bool){}, [&](dht::ListenSyncStatus status){ synced = (status == dht::ListenSyncStatus::SYNCED); }); // Initial status is UNSYNCED (or ADDED then UNSYNCED?) // Constructor calls syncCallback(ADDED). // Default status is UNSYNCED. cache.onSynced(true); CPPUNIT_ASSERT(synced); cache.onSynced(false); CPPUNIT_ASSERT(!synced); } void ValueCacheTester::testMaxValues() { dht::ValueCache cache([](const std::vector>&, bool){}); dht::TypeStore types; auto now = std::chrono::steady_clock::now(); // MAX_VALUES is 4096 for (int i = 0; i < 4100; ++i) { auto v = std::make_shared(); v->id = (dht::Value::Id)i + 1; cache.onValues({v}, {}, {}, types, now + std::chrono::milliseconds(i)); } // Should be capped at 4096 CPPUNIT_ASSERT_EQUAL((size_t)4096, cache.size()); } } // namespace test