From e2e39f616330bd152886532cbfabe22a8915cb32 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 20 Jun 2024 10:09:13 +0800 Subject: [PATCH] Add GTest --- .gitignore | 1 + CMakeLists.txt | 14 +++++ googletest | 1 + linked_stack.h | 81 +++++++++++++++++++++++++++ stack.h | 130 ++++++++++++++++++++++++++++++++++++++++++++ test/stack_test.cpp | 78 ++++++++++++++++++++++++++ 6 files changed, 305 insertions(+) create mode 160000 googletest create mode 100644 linked_stack.h create mode 100644 stack.h create mode 100644 test/stack_test.cpp diff --git a/.gitignore b/.gitignore index 567609b..6f31401 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index d54b30c..c1f0190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ option(ENABLE_CXX17 "Enable C++ 17" OFF) option(INSTALL_DEP_FILES "Install a file with dependences." OFF) option(ENABLE_SSL "Enable SSL" OFF) option(ENABLE_ZLIB "Use Zlib to uncompress http data." OFF) +option(ENABLE_UTILS_TESTING "Test utils with GTest." OFF) if (ENABLE_STANDALONE) project(utils) @@ -142,6 +143,8 @@ set(SOURCE_FILE_HEADERS file_reader.h urlparse.h http_client.h + stack.h + linked_stack.h ) if (NOT HAVE_STRPTIME) @@ -185,3 +188,14 @@ if (INSTALL_DEP_FILES) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/utils_dep.txt" DESTINATION ${CMAKE_INSTALL_PREFIX}) endif() endif() + +if (ENABLE_UTILS_TESTING) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory(googletest) + enable_testing() + add_executable(stack_test test/stack_test.cpp) + target_link_libraries(stack_test GTest::gtest_main utils) + include(GoogleTest) + gtest_discover_tests(stack_test) +endif() diff --git a/googletest b/googletest new file mode 160000 index 0000000..1d17ea1 --- /dev/null +++ b/googletest @@ -0,0 +1 @@ +Subproject commit 1d17ea141d2c11b8917d2c7d029f1c4e2b9769b2 diff --git a/linked_stack.h b/linked_stack.h new file mode 100644 index 0000000..09dfa1f --- /dev/null +++ b/linked_stack.h @@ -0,0 +1,81 @@ +#ifndef _UTIL_LINKED_STACK_H +#define _UTIL_LINKED_STACK_H +#include +#include +#include + +template +struct LinkedStack { + T d; + struct LinkedStack* next; +}; + +template +size_t linked_stack_length(struct LinkedStack* top) { + if (!top) return 0; + struct LinkedStack* now = top; + size_t i = 1; + while (now->next) { + now = now->next; + i += 1; + } + return i; +} + +template +void linked_stack_clear(struct LinkedStack*& top, std::function free_func = std::function()) { + if (!top) return; + struct LinkedStack* tmp; + do { + if (free_func) { + free_func(top->d); + } + tmp = top; + top = top->next; + free(tmp); + } while (top); +} + +template +void linked_stack_clear(struct LinkedStack*& top, F free_func) { + linked_stack_clear(top, std::function(free_func)); +} + +template +bool linked_stack_push(struct LinkedStack*& top, T ele) { + struct LinkedStack* t = (struct LinkedStack*)malloc(sizeof(struct LinkedStack)); + if (!t) { + return false; + } + t->d = ele; + t->next = top; + top = t; + return true; +} + +template +bool linked_stack_pop(struct LinkedStack*& top, T& ele) { + if (!top) return false; + ele = top->d; + struct LinkedStack* tmp = top; + top = top->next; + free(tmp); + return true; +} + +template +void linked_stack_iter(struct LinkedStack* top, std::function callback, Args... args) { + if (!top || !callback) return; + struct LinkedStack* now = top; + do { + callback(now->d, args...); + now = now->next; + } while (now); +} + +template +void linked_stack_iter(struct LinkedStack* top, F callback, Args... args) { + linked_stack_iter(top, std::function(callback), args...); +} + +#endif diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..b25635f --- /dev/null +++ b/stack.h @@ -0,0 +1,130 @@ +#ifndef _UTIL_STACK_H +#define _UTIL_STACK_H +#include +#include +#include + +template +struct Stack { + T* base; + T* top; + size_t capacity; + size_t inc_step; +}; + + +/** + * @brief Initailize stack and allocate memory for it. + * @param stack Stack + * @param capacity Default capacity + */ +template +bool init_stack(struct Stack& stack, size_t capacity = 1, size_t inc_step = 1) { + if (inc_step < 1) inc_step = 1; + if (capacity < 1) capacity = 1; + stack.base = (T*)malloc(capacity * sizeof(T)); + if (!stack.base) return false; + stack.top = stack.base; + stack.capacity = capacity; + stack.inc_step = inc_step; + return true; +} + +template +void free_stack(struct Stack& stack, std::function free_func = std::function()) { + if (!stack.base) return; + if (free_func) { + stack_iter(stack, free_func); + } + free(stack.base); + stack.base = nullptr; + stack.top = nullptr; + stack.capacity = 0; + stack.inc_step = 0; +} + +template +inline void free_stack(struct Stack& stack, F free_func) { + free_stack(stack, std::function(free_func)); +} + +template +void clear_stack(struct Stack& stack, std::function free_func = std::function()) { + if (!stack.base) return; + if (free_func) { + stack_iter(stack, free_func); + } + stack.top = stack.base; +} + +template +inline void clear_stack(struct Stack& stack, F free_func) { + clear_stack(stack, std::function(free_func)); +} + +template +bool stack_is_empty(struct Stack& stack) { + if (!stack.base) return true; + return stack.base == stack.top; +} + +template +size_t stack_length(struct Stack& stack) { + if (!stack.base) return 0; + return stack.top - stack.base; +} + +template +bool stack_get_top(struct Stack& stack, T& ele) { + if (!stack.base) return false; + if (stack.base == stack.top) return false; + ele = *(stack.top - 1); + return true; +} + +template +bool stack_push(struct Stack& stack, T ele) { + if (!stack.base) return false; + if (stack.top - stack.base >= stack.capacity) { + T* obase = stack.base; + stack.base = (T*)realloc(stack.base, (stack.capacity + stack.inc_step) * sizeof(T)); + if (!stack.base) { + /// Free original buffer + free(obase); + stack.top = nullptr; + stack.capacity = 0; + stack.inc_step = 0; + return false; + } + stack.top = stack.base + stack.capacity; + stack.capacity += stack.inc_step; + } + *(stack.top++) = ele; + return true; +} + +template +bool stack_pop(struct Stack& stack, T& ele) { + if (!stack.base) return false; + if (stack.top == stack.base) return false; + ele = *(--stack.top); + return true; +} + +template +void stack_iter(struct Stack& stack, std::function callback, Args... args) { + if (!stack.base || !callback) return; + if (stack.top == stack.base) return; + T* now = stack.top; + while (now > stack.base) { + now -= 1; + callback(*now, args...); + } +} + +template +inline void stack_iter(struct Stack& stack, F callback, Args... args) { + stack_iter(stack, std::function(callback), args...); +} + +#endif diff --git a/test/stack_test.cpp b/test/stack_test.cpp new file mode 100644 index 0000000..8cc84a7 --- /dev/null +++ b/test/stack_test.cpp @@ -0,0 +1,78 @@ +#include "gtest/gtest.h" +#include "stack.h" +#include "linked_stack.h" +#include + +TEST(Stack_Test, Stack1) { + struct Stack stack; + ASSERT_EQ(init_stack(stack, 10), true) << "Can not initiailze stack."; + ASSERT_EQ(stack_is_empty(stack), true) << "Stack is not empty."; + ASSERT_EQ(stack_push(stack, 3), true) << "Failed to push elements to stack"; + int top; + ASSERT_EQ(stack_get_top(stack, top), true) << "Failed to get top element."; + ASSERT_EQ(top, 3) << "Value is not expeced."; + ASSERT_EQ(stack_length(stack), 1) << "Length should be 1"; + ASSERT_EQ(stack_is_empty(stack), false) << "Stack is empty."; + ASSERT_EQ(stack_push(stack, 5), true) << "Failed to push to stack."; + ASSERT_EQ(stack_length(stack), 2) << "Length should be 2"; + std::string text; + stack_iter(stack, [&text](int ele) { + if (!text.empty()) { + text += ","; + } + text += std::to_string(ele); + }); + ASSERT_EQ(text, "5,3") << "Failed to join."; + ASSERT_EQ(stack_pop(stack, top), true) << "Failed to pop."; + ASSERT_EQ(top, 5) << "Poped value not equal."; + ASSERT_EQ(stack_length(stack), 1) << "Length should be 1"; + free_stack(stack); +} + +TEST(Stack_Test, Stack2) { + struct Stack stack; + ASSERT_EQ(init_stack(stack, 1, 2), true) << "Can not initiailze stack."; + ASSERT_EQ(stack_push(stack, 3), true) << "Failed to push elements to stack"; + ASSERT_EQ(stack_push(stack, 4), true) << "Failed to push elements to stack"; + ASSERT_EQ(stack.capacity, 3) << "Capacity not expected"; + ASSERT_EQ(stack_push(stack, 5), true) << "Failed to push elements to stack"; + ASSERT_EQ(stack_push(stack, 6), true) << "Failed to push elements to stack"; + ASSERT_EQ(stack.capacity, 5) << "Capacity not expected"; + std::string text; + stack_iter(stack, [&text](int ele) { + if (!text.empty()) { + text += ","; + } + text += std::to_string(ele); + }); + ASSERT_EQ(text, "6,5,4,3") << "Failed to join."; + ASSERT_EQ(stack_length(stack), 4) << "Length should be 4"; + free_stack(stack); +} + +TEST(Stack_Test, LinkedStack1) { + struct LinkedStack* top = nullptr; + EXPECT_EQ(linked_stack_push(top, 3), true); + EXPECT_EQ(top->next, nullptr); + EXPECT_EQ(top->d, 3); + EXPECT_EQ(linked_stack_push(top, 4), true); + EXPECT_EQ(top->d, 4); + EXPECT_EQ(top->next->d, 3); + int e; + EXPECT_EQ(linked_stack_pop(top, e), true); + EXPECT_EQ(e, 4); + EXPECT_EQ(linked_stack_push(top, 8), true); + EXPECT_EQ(linked_stack_push(top, 9), true); + EXPECT_EQ(linked_stack_push(top, 10), true); + std::string text; + linked_stack_iter(top, [&text](int ele) { + if (!text.empty()) { + text += ","; + } + text += std::to_string(ele); + }); + EXPECT_EQ(text, "10,9,8,3"); + EXPECT_EQ(linked_stack_length(top), 4); + linked_stack_clear(top); + EXPECT_EQ(top, nullptr); +}