From 2bdb38129c5cca75f05fa927bf5d7e968697aeba Mon Sep 17 00:00:00 2001 From: gc87 Date: Sun, 28 Aug 2022 20:07:57 +0800 Subject: [PATCH] feat: add memory cache unit test --- CMakeLists.txt | 3 +- include/neuron/neuron.h | 1 + .../neuron/utils/mem_cache.h | 7 +- plugins/mqtt/CMakeLists.txt | 1 - .../neu_mem_cache.c => src/utils/mem_cache.c | 54 ++++++-- tests/CMakeLists.txt | 9 ++ tests/cache_test.cc | 120 ++++++++++++++++++ 7 files changed, 177 insertions(+), 18 deletions(-) rename plugins/mqtt/neu_mem_cache.h => include/neuron/utils/mem_cache.h (85%) rename plugins/mqtt/neu_mem_cache.c => src/utils/mem_cache.c (66%) create mode 100644 tests/cache_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index f1fe29a9..fcf456a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,8 @@ set(NEURON_BASE_SOURCES src/utils/json.c src/utils/neu_jwt.c src/utils/base64.c - src/utils/async_queue.c) + src/utils/async_queue.c + src/utils/mem_cache.c) add_library(neuron-base SHARED) target_sources(neuron-base PRIVATE ${NEURON_BASE_SOURCES} ${NEURON_SRC_PARSE}) diff --git a/include/neuron/neuron.h b/include/neuron/neuron.h index 0bc4bd73..6f0d0522 100644 --- a/include/neuron/neuron.h +++ b/include/neuron/neuron.h @@ -35,6 +35,7 @@ extern "C" { #include "utils/base64.h" #include "utils/log.h" +#include "utils/mem_cache.h" #include "utils/zlog.h" #include "utils/neu_jwt.h" diff --git a/plugins/mqtt/neu_mem_cache.h b/include/neuron/utils/mem_cache.h similarity index 85% rename from plugins/mqtt/neu_mem_cache.h rename to include/neuron/utils/mem_cache.h index eb76db54..e7963abe 100644 --- a/plugins/mqtt/neu_mem_cache.h +++ b/include/neuron/utils/mem_cache.h @@ -39,12 +39,13 @@ typedef struct { typedef void (*cache_dump)(cache_item_t *item, void *ctx); neu_mem_cache_t *neu_mem_cache_create(const size_t max_bytes, - const size_t max_msgs); + const size_t max_items); int neu_mem_cache_add(neu_mem_cache_t *cache, cache_item_t *item); cache_item_t neu_mem_cache_earliest(neu_mem_cache_t *cache); cache_item_t neu_mem_cache_latest(neu_mem_cache_t *cache); -void neu_mem_cache_used(neu_mem_cache_t *cache, size_t *bytes, size_t *msgs); -int neu_mem_cache_dump(neu_mem_cache_t *cache, void *callback, void *ctx); +void neu_mem_cache_used(neu_mem_cache_t *cache, size_t *used_bytes, + size_t *used_items); +int neu_mem_cache_dump(neu_mem_cache_t *cache, cache_dump callback, void *ctx); int neu_mem_cache_clear(neu_mem_cache_t *cache); void neu_mem_cache_destroy(neu_mem_cache_t *cache); diff --git a/plugins/mqtt/CMakeLists.txt b/plugins/mqtt/CMakeLists.txt index 75aa8dcb..d89dfd99 100644 --- a/plugins/mqtt/CMakeLists.txt +++ b/plugins/mqtt/CMakeLists.txt @@ -9,7 +9,6 @@ file(COPY ${CMAKE_SOURCE_DIR}/plugins/mqtt/mqtt.json DESTINATION ${CMAKE_BINARY_ set(src mqtt_plugin.c mqtt_util.c - neu_mem_cache.c command/read_write.c command/heartbeat.c command/command.c) diff --git a/plugins/mqtt/neu_mem_cache.c b/src/utils/mem_cache.c similarity index 66% rename from plugins/mqtt/neu_mem_cache.c rename to src/utils/mem_cache.c index 3dd8302d..fa4f7e67 100644 --- a/plugins/mqtt/neu_mem_cache.c +++ b/src/utils/mem_cache.c @@ -2,7 +2,7 @@ #include -#include "neu_mem_cache.h" +#include "utils/mem_cache.h" typedef struct element { cache_item_t item; @@ -12,14 +12,14 @@ typedef struct element { struct neu_mem_cache { size_t max_bytes; // 0 no limit - size_t max_msgs; // 0 no limit + size_t max_items; // 0 no limit size_t used_bytes; - size_t used_msgs; + size_t used_items; element *head; }; neu_mem_cache_t *neu_mem_cache_create(const size_t max_bytes, - const size_t max_msgs) + const size_t max_items) { neu_mem_cache_t *cache = calloc(1, sizeof(neu_mem_cache_t)); if (NULL == cache) { @@ -27,9 +27,9 @@ neu_mem_cache_t *neu_mem_cache_create(const size_t max_bytes, } cache->max_bytes = max_bytes; - cache->max_msgs = max_msgs; + cache->max_items = max_items; cache->used_bytes = 0; - cache->used_msgs = 0; + cache->used_items = 0; cache->head = NULL; return cache; } @@ -43,11 +43,22 @@ int neu_mem_cache_add(neu_mem_cache_t *cache, cache_item_t *item) return -1; } + if ((0 != cache->used_bytes) && + (item->size + cache->used_bytes) > cache->max_bytes) { + free(el); + return -1; + } + + if ((0 != cache->used_items) && (cache->used_items >= cache->max_bytes)) { + free(el); + return -1; + } + el->item = *item; DL_PREPEND(cache->head, el); cache->used_bytes += item->size; - cache->used_msgs += 1; + cache->used_items += 1; return 0; } @@ -63,8 +74,10 @@ cache_item_t neu_mem_cache_earliest(neu_mem_cache_t *cache) item = elm->item; DL_DELETE(cache->head, elm); + free(elm); + cache->used_bytes -= item.size; - cache->used_msgs -= 1; + cache->used_items -= 1; return item; } @@ -80,8 +93,10 @@ cache_item_t neu_mem_cache_latest(neu_mem_cache_t *cache) item = elm->item; DL_DELETE(cache->head, elm); + free(elm); + cache->used_bytes -= item.size; - cache->used_msgs -= 1; + cache->used_items -= 1; return item; } @@ -90,15 +105,27 @@ void neu_mem_cache_used(neu_mem_cache_t *cache, size_t *bytes, size_t *msgs) assert(NULL != cache); *bytes = cache->used_bytes; - *msgs = cache->used_msgs; + *msgs = cache->used_items; } -int neu_mem_cache_dump(neu_mem_cache_t *cache, void *callback, void *ctx) +int neu_mem_cache_dump(neu_mem_cache_t *cache, cache_dump callback, void *ctx) { assert(NULL != cache); - (void) callback; - (void) ctx; + if (NULL != callback) { + element *el = NULL; + element *tmp = NULL; + DL_FOREACH_SAFE(cache->head, el, tmp) + { + callback(&el->item, ctx); + + if (NULL != el->item.release) { + el->item.release(&el->item); + } + + LL_DELETE(cache->head, el); + } + } return 0; } @@ -116,6 +143,7 @@ int neu_mem_cache_clear(neu_mem_cache_t *cache) } LL_DELETE(cache->head, el); + free(el); } return 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d64fe4a9..480ecb2f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,6 +52,14 @@ target_include_directories(tag_sort_test PRIVATE target_link_libraries(tag_sort_test neuron-base gtest_main gtest) #target_link_directories(modbus_point_test PRIVATE /usr/local/lib) +add_executable(cache_test cache_test.cc + ${CMAKE_SOURCE_DIR}/src/utils/mem_cache.c) +target_include_directories(cache_test PRIVATE + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/include +) +target_link_libraries(cache_test neuron-base gtest_main gtest) + include(GoogleTest) gtest_discover_tests(json_test) gtest_discover_tests(http_test) @@ -59,3 +67,4 @@ gtest_discover_tests(mqtt_test) gtest_discover_tests(jwt_test) gtest_discover_tests(base64_test) gtest_discover_tests(tag_sort_test) +gtest_discover_tests(cache_test) diff --git a/tests/cache_test.cc b/tests/cache_test.cc new file mode 100644 index 00000000..b59b2356 --- /dev/null +++ b/tests/cache_test.cc @@ -0,0 +1,120 @@ +#include + +#include + +zlog_category_t *neuron = NULL; + +TEST(CacheTest, neu_mem_cache_create) +{ + const size_t max_bytes = 1024 * 1024; + const size_t max_items = 1024; + neu_mem_cache_t *cache = neu_mem_cache_create(max_bytes, max_items); + EXPECT_NE(nullptr, cache); + neu_mem_cache_destroy(cache); +} + +TEST(CacheTest, neu_mem_cache_add) +{ + const size_t max_bytes = 1024 * 1024; + const size_t max_items = 1024; + neu_mem_cache_t *cache = neu_mem_cache_create(max_bytes, max_items); + EXPECT_NE(nullptr, cache); + + const char * str = "hello,world!"; + cache_item_t item = { + .size = strlen(str) + 1, + .data = (void *) str, + .release = NULL, + }; + + int rc = neu_mem_cache_add(cache, &item); + EXPECT_EQ(0, rc); + + neu_mem_cache_destroy(cache); +} + +TEST(CacheTest, neu_mem_cache_used) +{ + const size_t max_bytes = 1024 * 1024; + const size_t max_items = 1024; + neu_mem_cache_t *cache = neu_mem_cache_create(max_bytes, max_items); + EXPECT_NE(nullptr, cache); + + size_t used_bytes = 0; + size_t used_msgs = 0; + neu_mem_cache_used(cache, &used_bytes, &used_msgs); + EXPECT_EQ(0, used_bytes); + EXPECT_EQ(0, used_msgs); + + const char * str = "hello,world!"; + cache_item_t item = { + .size = strlen(str) + 1, + .data = (void *) str, + .release = NULL, + }; + + int rc = neu_mem_cache_add(cache, &item); + EXPECT_EQ(0, rc); + + neu_mem_cache_used(cache, &used_bytes, &used_msgs); + EXPECT_EQ(strlen(str) + 1, used_bytes); + EXPECT_EQ(1, used_msgs); + + cache_item_t item1 = neu_mem_cache_earliest(cache); + EXPECT_NE(nullptr, item1.data); + EXPECT_STREQ((char *) item1.data, str); + EXPECT_EQ(item1.size, strlen(str) + 1); + + neu_mem_cache_used(cache, &used_bytes, &used_msgs); + EXPECT_EQ(0, used_bytes); + EXPECT_EQ(0, used_msgs); + + neu_mem_cache_destroy(cache); +} + +TEST(CacheTest, neu_mem_cache_latest) +{ + const size_t max_bytes = 1024 * 1024; + const size_t max_items = 1024; + neu_mem_cache_t *cache = neu_mem_cache_create(max_bytes, max_items); + EXPECT_NE(nullptr, cache); + + size_t used_bytes = 0; + size_t used_items = 0; + neu_mem_cache_used(cache, &used_bytes, &used_items); + EXPECT_EQ(0, used_bytes); + EXPECT_EQ(0, used_items); + + const char * str = "hello,world!"; + cache_item_t item = { + .size = strlen(str) + 1, + .data = (void *) str, + .release = NULL, + }; + + int rc = neu_mem_cache_add(cache, &item); + EXPECT_EQ(0, rc); + + neu_mem_cache_used(cache, &used_bytes, &used_items); + EXPECT_EQ(strlen(str) + 1, used_bytes); + EXPECT_EQ(1, used_items); + + cache_item_t item1 = neu_mem_cache_latest(cache); + EXPECT_NE(nullptr, item1.data); + EXPECT_STREQ((char *) item1.data, str); + EXPECT_EQ(item1.size, strlen(str) + 1); + + neu_mem_cache_used(cache, &used_bytes, &used_items); + EXPECT_EQ(0, used_bytes); + EXPECT_EQ(0, used_items); + + neu_mem_cache_destroy(cache); +} + +int main(int argc, char **argv) +{ + zlog_init("./config/dev.conf"); + neuron = zlog_get_category("neuron"); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}