Skip to content

Commit

Permalink
[core] Expmap takes additional template parameter for clock type. Thi…
Browse files Browse the repository at this point in the history
…s improves testability and allows to expire based on different time base, if required. (#1636)
  • Loading branch information
KerstinKeller authored Jun 21, 2024
1 parent b0c5a4c commit 3274646
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 30 deletions.
15 changes: 7 additions & 8 deletions ecal/core/src/util/ecal_expmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,14 @@ namespace eCAL
**/
template<class Key,
class T,
class clock_type = std::chrono::steady_clock,
class Compare = std::less<Key>,
class Alloc = std::allocator<std::pair<const Key, T> > >
class CExpMap
{
public:
using clock_type = std::chrono::steady_clock;

// Key access history, most recent at back
using key_tracker_type = std::list<std::pair<clock_type::time_point, Key>>;
using key_tracker_type = std::list<std::pair<typename clock_type::time_point, Key>>;

// Key to value and key history iterator
using key_to_value_type = std::map<Key, std::pair<T, typename key_tracker_type::iterator>>;
Expand Down Expand Up @@ -147,12 +146,12 @@ namespace eCAL

// Constructor specifies the timeout of the map
CExpMap() : _timeout(std::chrono::milliseconds(5000)) {};
explicit CExpMap(clock_type::duration t) : _timeout(t) {};
explicit CExpMap(typename clock_type::duration t) : _timeout(t) {};

/**
* @brief set expiration time
**/
void set_expiration(clock_type::duration t) { _timeout = t; };
void set_expiration(typename clock_type::duration t) { _timeout = t; };

// Iterators:
iterator begin() noexcept
Expand Down Expand Up @@ -261,7 +260,7 @@ namespace eCAL
{
// Assert method is never called when cache is empty
//assert(!_key_tracker.empty());
clock_type::time_point eviction_limit = get_curr_time() - _timeout;
typename clock_type::time_point eviction_limit = get_curr_time() - _timeout;

auto it(_key_tracker.begin());

Expand Down Expand Up @@ -328,7 +327,7 @@ namespace eCAL
return ret;
}

clock_type::time_point get_curr_time()
typename clock_type::time_point get_curr_time()
{
return clock_type::now();
}
Expand All @@ -340,7 +339,7 @@ namespace eCAL
key_to_value_type _key_to_value;

// Timeout of map
clock_type::duration _timeout;
typename clock_type::duration _timeout;
};
}
}
75 changes: 53 additions & 22 deletions ecal/tests/cpp/expmap_test/src/expmap_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* ========================= eCAL LICENSE =================================
*
* Copyright (C) 2016 - 2019 Continental Corporation
* Copyright (C) 2016 - 2024 Continental Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -27,10 +27,41 @@

#include <gtest/gtest.h>

class TestingClock {
public:
// Define the required types for TrivialClock
using duration = std::chrono::milliseconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<TestingClock>;
static const bool is_steady = false;

// Function to get the current time
static time_point now() noexcept {
return time_point(current_time);
}

// Function to manually set the current time
static void set_time(const time_point& tp) {
current_time = tp.time_since_epoch();
}

// Function to manually increment the current time by a given duration
static void increment_time(const duration& d) {
current_time += d;
}

private:
static duration current_time;
};

// Initialize the static member
TestingClock::duration TestingClock::current_time{ 0 };

TEST(core_cpp_core, ExpMap_SetGet)
{
// create the map with 2500 ms expiration
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));

// set "A"
expmap["A"] = 1;
Expand All @@ -43,7 +74,7 @@ TEST(core_cpp_core, ExpMap_SetGet)
EXPECT_EQ(1, expmap.size());

// sleep
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));

// access and reset timer
EXPECT_EQ(1, expmap["A"]);
Expand All @@ -54,41 +85,41 @@ TEST(core_cpp_core, ExpMap_SetGet)
EXPECT_EQ(1, expmap.size());

// sleep
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));

// check size
//content = expmap.clone();
expmap.remove_deprecated();
EXPECT_EQ(1, expmap.size());

// sleep
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));

// check size
//content = expmap.clone();
expmap.remove_deprecated();
EXPECT_EQ(0, expmap.size());

expmap["A"] = 1;
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));
expmap["B"] = 2;
expmap["C"] = 3;
expmap.remove_deprecated();
EXPECT_EQ(3, expmap.size());
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));
expmap["B"] = 4;
expmap.remove_deprecated();
EXPECT_EQ(2, expmap.size());
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));
expmap.remove_deprecated();
EXPECT_EQ(1, expmap.size());
// sleep
std::this_thread::sleep_for(std::chrono::milliseconds(150));
TestingClock::increment_time(std::chrono::milliseconds(150));
}

TEST(core_cpp_core, ExpMap_Insert)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
auto ret = expmap.insert(std::make_pair("A", 1));

auto key = (*ret.first).first;
Expand All @@ -100,15 +131,15 @@ TEST(core_cpp_core, ExpMap_Insert)

EXPECT_EQ(i, 1);

std::this_thread::sleep_for(std::chrono::milliseconds(300));
TestingClock::increment_time(std::chrono::milliseconds(300));
expmap.remove_deprecated();
EXPECT_EQ(0, expmap.size());
}

// This tests uses find to find an element
TEST(core_cpp_core, ExpMap_Find)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));

auto it = expmap.find("A");
EXPECT_EQ(expmap.end(), it);
Expand All @@ -119,15 +150,15 @@ TEST(core_cpp_core, ExpMap_Find)
int i = (*it).second;
EXPECT_EQ(i, 1);

std::this_thread::sleep_for(std::chrono::milliseconds(300));
TestingClock::increment_time(std::chrono::milliseconds(300));
expmap.remove_deprecated();
EXPECT_EQ(0, expmap.size());
}

// This test assures that find can be called on a const CExpMap and returns an CExpMap::const_iterator
TEST(core_cpp_core, ExpMap_FindConst)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));

auto it = expmap.find("A");
EXPECT_EQ(expmap.end(), it);
Expand All @@ -137,19 +168,19 @@ TEST(core_cpp_core, ExpMap_FindConst)
const auto& const_ref_exmap = expmap;
auto const_it = const_ref_exmap.find("A");
// assert that we are actually getting a const_iterator here!
static_assert(std::is_same<decltype(const_it), eCAL::Util::CExpMap<std::string, int>::const_iterator>::value, "We're not being returned a const_iterator from find.");
static_assert(std::is_same<decltype(const_it), eCAL::Util::CExpMap<std::string, int, TestingClock>::const_iterator>::value, "We're not being returned a const_iterator from find.");
int i = (*const_it).second;
EXPECT_EQ(i, 1);

std::this_thread::sleep_for(std::chrono::milliseconds(300));
TestingClock::increment_time(std::chrono::milliseconds(300));
expmap.remove_deprecated();
EXPECT_EQ(0, expmap.size());
}

TEST(core_cpp_core, ExpMap_Iterate)
{
// create the map with 2500 ms expiration
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
expmap["A"] = 1;

std::string key;
Expand All @@ -165,7 +196,7 @@ TEST(core_cpp_core, ExpMap_Iterate)
EXPECT_EQ(1, value);
}

void ConstRefIterate(const eCAL::Util::CExpMap<std::string, int>& map)
void ConstRefIterate(const eCAL::Util::CExpMap<std::string, int, TestingClock>& map)
{
std::string key;
int value;
Expand All @@ -183,31 +214,31 @@ void ConstRefIterate(const eCAL::Util::CExpMap<std::string, int>& map)
TEST(core_cpp_core, ExpMap_ConstExpMapIterate)
{
// create the map with 2500 ms expiration
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
expmap["A"] = 1;

ConstRefIterate(expmap);
}

TEST(core_cpp_core, ExpMap_Empty)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
EXPECT_EQ(true, expmap.empty());
expmap["A"] = 1;
EXPECT_EQ(false, expmap.empty());
}

TEST(core_cpp_core, ExpMap_Size)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
EXPECT_EQ(0, expmap.size());
expmap["A"] = 1;
EXPECT_EQ(1, expmap.size());
}

TEST(core_cpp_core, ExpMap_Remove)
{
eCAL::Util::CExpMap<std::string, int> expmap(std::chrono::milliseconds(200));
eCAL::Util::CExpMap<std::string, int, TestingClock> expmap(std::chrono::milliseconds(200));
expmap["A"] = 1;
EXPECT_EQ(1, expmap.size());
EXPECT_TRUE(expmap.erase("A"));
Expand Down

0 comments on commit 3274646

Please sign in to comment.