diff --git a/CMakeLists.txt b/CMakeLists.txt index e17acde..5d62c8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,14 @@ -cmake_minimum_required(VERSION 3.28) +cmake_minimum_required(VERSION 3.27) + project(Cachetron C CXX) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED ON) +# Google Test requires at least C++14 +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Check if the system is Linux if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(FATAL_ERROR "This program only targets Linux.") @@ -22,6 +27,7 @@ endif () # Additional options for the Debug build set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -Wpedantic") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wpedantic") # Sanitizers for debugging (Disabled by default, slows down the program. Uncomment to enable.) # GCC does not support all sanitizers, so Clang is recommended for this purpose. Requires llvm-symbolizer. @@ -37,36 +43,40 @@ if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang") # set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -fsanitize=memory -fPIE -fno-optimize-sibling-calls -fno-omit-frame-pointer -g -O1") endif () -# Tests -add_subdirectory(tests) +# Google Test +include(FetchContent) + +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/f10e11fb27301fba21caa71030bb5024e67aa135.zip +) + +FetchContent_MakeAvailable(googletest) + +enable_testing() + +# Data structures +file(GLOB_RECURSE DS_SOURCES + data_structures/*.c + data_structures/*.h) +# server executable add_executable(server server.c common.h thread_pool.c thread_pool.h commands.h - data_structures/trees/avl.c - data_structures/trees/avl.h - data_structures/vector/vector_c.c - data_structures/vector/vector_c.h - data_structures/string/string_c.c - data_structures/string/string_c.h - data_structures/hashmap/hashtable.c - data_structures/hashmap/hashtable.h - data_structures/set/zset.c - data_structures/set/zset.h - data_structures/list/list.c - data_structures/list/list.h - data_structures/trees/heap.c - data_structures/trees/heap.h - data_structures/queue/deque_c.c - data_structures/queue/deque_c.h) + ${DS_SOURCES}) +# client executable add_executable(client client.c + common.h data_structures/vector/vector_c.c data_structures/vector/vector_c.h data_structures/string/string_c.c - data_structures/string/string_c.h - common.h) \ No newline at end of file + data_structures/string/string_c.h) + +# Tests +add_subdirectory(tests) diff --git a/README.md b/README.md index e475911..875c32f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Cachetron uses CMake as its build system, and **it is intended for Linux systems To build Cachetron, you will need to have the following installed: -- `CMake` 3.28 or later +- `CMake` 3.27 or later - `Clang 18` and later or `GCC 13` and later. **C23 support is required**. - `Ninja` 1.11 or later (optional, but recommended) - `Python` 3.11 or later (optional, for running tests) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 02da785..2625a33 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,27 @@ -add_executable(test_vector test_vector.c ../data_structures/vector/vector_c.c ../data_structures/vector/vector_c.h) +# Test executables +add_executable(test_vector + test_vector.cpp + ../data_structures/vector/vector_c.c + ../data_structures/vector/vector_c.h) -add_executable(test_string test_string.c ../data_structures/string/string_c.c ../data_structures/string/string_c.h) +add_executable(test_string + test_string.cpp + ../data_structures/string/string_c.c + ../data_structures/string/string_c.h) -add_executable(test_deque test_deque.c ../data_structures/queue/deque_c.c ../data_structures/queue/deque_c.h) +add_executable(test_deque + test_deque.cpp + ../data_structures/queue/deque_c.c + ../data_structures/queue/deque_c.h) + +# Link test executables with gtest +target_link_libraries(test_vector gtest gtest_main) +target_link_libraries(test_string gtest gtest_main) +target_link_libraries(test_deque gtest gtest_main) + +include(GoogleTest) + +# Discover tests +gtest_discover_tests(test_vector) +gtest_discover_tests(test_string) +gtest_discover_tests(test_deque) diff --git a/tests/test_deque.c b/tests/test_deque.c deleted file mode 100644 index b27baea..0000000 --- a/tests/test_deque.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../data_structures/queue/deque_c.h" -#include - -void create_deque_should_initialize_empty_deque() { - Deque *deque = create_deque(); - assert(deque != nullptr); - assert(deque_empty(deque)); - assert(deque_size(deque) == 0); - destroy_deque(deque); -} - -void deque_push_front_should_add_element_to_front() { - Deque *deque = create_deque(); - int data = 5; - assert(deque_push_front(deque, &data)); - assert(deque_size(deque) == 1); - assert(*(int *)deque_front(deque) == data); - destroy_deque(deque); -} - -void deque_push_back_should_add_element_to_back() { - Deque *deque = create_deque(); - int data = 5; - assert(deque_push_back(deque, &data)); - assert(deque_size(deque) == 1); - assert(*(int *)deque_back(deque) == data); - destroy_deque(deque); -} - -void deque_pop_front_should_remove_and_return_front_element() { - Deque *deque = create_deque(); - int data = 5; - deque_push_front(deque, &data); - assert(*(int *)deque_pop_front(deque) == data); - assert(deque_empty(deque)); - destroy_deque(deque); -} - -void deque_pop_back_should_remove_and_return_back_element() { - Deque *deque = create_deque(); - int data = 5; - deque_push_back(deque, &data); - assert(*(int *)deque_pop_back(deque) == data); - assert(deque_empty(deque)); - destroy_deque(deque); -} - -void deque_resize_should_double_capacity_when_full() { - Deque *deque = create_deque(); - for (int i = 0; i < 16; ++i) { - deque_push_back(deque, &i); - } - assert(deque_size(deque) == 16); - int data = 17; - assert(deque_push_back(deque, &data)); - assert(deque_size(deque) == 17); - destroy_deque(deque); -} - -int main() { - create_deque_should_initialize_empty_deque(); - deque_push_front_should_add_element_to_front(); - deque_push_back_should_add_element_to_back(); - deque_pop_front_should_remove_and_return_front_element(); - deque_pop_back_should_remove_and_return_back_element(); - deque_resize_should_double_capacity_when_full(); - return 0; -} diff --git a/tests/test_deque.cpp b/tests/test_deque.cpp new file mode 100644 index 0000000..2b4e6e9 --- /dev/null +++ b/tests/test_deque.cpp @@ -0,0 +1,58 @@ +#include +#include "../data_structures/queue/deque_c.h" + +TEST(DequeCTest, CreateDequeShouldInitializeEmptyDeque) { + Deque *deque = create_deque(); + ASSERT_NE(deque, nullptr); + ASSERT_TRUE(deque_empty(deque)); + ASSERT_EQ(deque_size(deque), 0); + destroy_deque(deque); +} + +TEST(DequeCTest, DequePushFrontShouldAddElementToFront) { + Deque *deque = create_deque(); + int data = 5; + ASSERT_TRUE(deque_push_front(deque, &data)); + ASSERT_EQ(deque_size(deque), 1); + ASSERT_EQ(*(int *) deque_front(deque), data); + destroy_deque(deque); +} + +TEST(DequeCTest, DequePushBackShouldAddElementToBack) { + Deque *deque = create_deque(); + int data = 5; + ASSERT_TRUE(deque_push_back(deque, &data)); + ASSERT_EQ(deque_size(deque), 1); + ASSERT_EQ(*(int *) deque_back(deque), data); + destroy_deque(deque); +} + +TEST(DequeCTest, DequePopFrontShouldRemoveAndReturnFrontElement) { + Deque *deque = create_deque(); + int data = 5; + deque_push_front(deque, &data); + ASSERT_EQ(*(int *) deque_pop_front(deque), data); + ASSERT_TRUE(deque_empty(deque)); + destroy_deque(deque); +} + +TEST(DequeCTest, DequePopBackShouldRemoveAndReturnBackElement) { + Deque *deque = create_deque(); + int data = 5; + deque_push_back(deque, &data); + ASSERT_EQ(*(int *) deque_pop_back(deque), data); + ASSERT_TRUE(deque_empty(deque)); + destroy_deque(deque); +} + +TEST(DequeCTest, DequeResizeShouldDoubleCapacityWhenFull) { + Deque *deque = create_deque(); + for (int i = 0; i < 16; ++i) { + deque_push_back(deque, &i); + } + ASSERT_EQ(deque_size(deque), 16); + int data = 17; + ASSERT_TRUE(deque_push_back(deque, &data)); + ASSERT_EQ(deque_size(deque), 17); + destroy_deque(deque); +} diff --git a/tests/test_string.c b/tests/test_string.cpp similarity index 99% rename from tests/test_string.c rename to tests/test_string.cpp index 101e383..df89505 100644 --- a/tests/test_string.c +++ b/tests/test_string.cpp @@ -1,8 +1,7 @@ -#include -#include -#include -#include - +#include +#include +#include +#include #include "../data_structures/string/string_c.h" void string_new_creates_empty_string() { diff --git a/tests/test_vector.c b/tests/test_vector.c deleted file mode 100644 index 12b3a6d..0000000 --- a/tests/test_vector.c +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include "../data_structures/vector/vector_c.h" - -#if !(__GNUC__ >= 13 || __clang_major__ >= 19) -#define constexpr const -#endif - -void vector_new_creates_empty_vector() { - vector_c *v = vector_new(sizeof(int)); - assert(v != nullptr); - assert(vector_size(v) == 0); - assert(vector_capacity(v) == 16); - vector_free(v); -} - -void vector_push_back_increases_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_push_back(v, &value); - assert(vector_size(v) == 1); - vector_free(v); -} - -void vector_push_back_stores_correct_value() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_push_back(v, &value); - assert(*(int *)vector_at(v, 0) == value); - vector_free(v); -} - -void vector_pop_back_decreases_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_push_back(v, &value); - vector_pop_back(v); - assert(vector_size(v) == 0); - vector_free(v); -} - -void vector_append_increases_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int values[] = {1, 2, 3, 4, 5}; - vector_append(v, values, 5); - assert(vector_size(v) == 5); - vector_free(v); -} - -void vector_append_stores_correct_values() { - vector_c *v = vector_new(sizeof(int)); - constexpr int values[] = {1, 2, 3, 4, 5}; - vector_append(v, values, 5); - for (size_t i = 0; i < 5; ++i) { - assert(*(int *)vector_at(v, i) == values[i]); - } - vector_free(v); -} - -void vector_insert_increases_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_insert(v, 0, &value); - assert(vector_size(v) == 1); - vector_free(v); -} - -void vector_insert_stores_correct_value() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_insert(v, 0, &value); - assert(*(int *)vector_at(v, 0) == value); - vector_free(v); -} - -void vector_erase_decreases_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_push_back(v, &value); - vector_erase(v, 0); - assert(vector_size(v) == 0); - vector_free(v); -} - -void vector_clear_resets_size() { - vector_c *v = vector_new(sizeof(int)); - constexpr int value = 5; - vector_push_back(v, &value); - vector_clear(v); - assert(vector_size(v) == 0); - vector_free(v); -} - -void vector_size_returns_zero_for_nullptr() { - assert(vector_size(nullptr) == 0); -} - -void vector_capacity_returns_zero_for_nullptr() { - assert(vector_capacity(nullptr) == 0); -} - -void vector_push_back_does_not_crash_for_nullptr() { - constexpr int value = 5; - vector_push_back(nullptr, &value); -} - -void vector_at_returns_null_for_nullptr() { - assert(vector_at(nullptr, 0) == nullptr); -} - -void vector_pop_back_does_not_crash_for_nullptr() { - vector_pop_back(nullptr); -} - -void vector_append_does_not_crash_for_nullptr() { - constexpr int values[] = {1, 2, 3, 4, 5}; - vector_append(nullptr, values, 5); -} - -void vector_insert_does_not_crash_for_nullptr() { - constexpr int value = 5; - vector_insert(nullptr, 0, &value); -} - -void vector_erase_does_not_crash_for_nullptr() { - vector_erase(nullptr, 0); -} - -void vector_clear_does_not_crash_for_nullptr() { - vector_clear(nullptr); -} - -int main() { - vector_new_creates_empty_vector(); - vector_push_back_increases_size(); - vector_push_back_stores_correct_value(); - vector_pop_back_decreases_size(); - vector_append_increases_size(); - vector_append_stores_correct_values(); - vector_insert_increases_size(); - vector_insert_stores_correct_value(); - vector_erase_decreases_size(); - vector_clear_resets_size(); - - vector_size_returns_zero_for_nullptr(); - vector_capacity_returns_zero_for_nullptr(); - vector_push_back_does_not_crash_for_nullptr(); - vector_at_returns_null_for_nullptr(); - vector_pop_back_does_not_crash_for_nullptr(); - vector_append_does_not_crash_for_nullptr(); - vector_insert_does_not_crash_for_nullptr(); - vector_erase_does_not_crash_for_nullptr(); - vector_clear_does_not_crash_for_nullptr(); - return 0; -} diff --git a/tests/test_vector.cpp b/tests/test_vector.cpp new file mode 100644 index 0000000..0607388 --- /dev/null +++ b/tests/test_vector.cpp @@ -0,0 +1,126 @@ +#include +#include "../data_structures/vector/vector_c.h" + +TEST(VectorCTest, NewCreatesEmptyVector) { + vector_c *v = vector_new(sizeof(int)); + ASSERT_NE(v, nullptr); + ASSERT_EQ(vector_size(v), 0); + ASSERT_EQ(vector_capacity(v), 16); + vector_free(v); +} + +TEST(VectorCTest, PushBackIncreasesSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_push_back(v, &value); + ASSERT_EQ(vector_size(v), 1); + vector_free(v); +} + +TEST(VectorCTest, PushBackStoresCorrectValue) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_push_back(v, &value); + ASSERT_EQ(*(int *) vector_at(v, 0), value); + vector_free(v); +} + +TEST(VectorCTest, PopBackDecreasesSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_push_back(v, &value); + vector_pop_back(v); + ASSERT_EQ(vector_size(v), 0); + vector_free(v); +} + +TEST(VectorCTest, AppendIncreasesSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int values[] = {1, 2, 3, 4, 5}; + vector_append(v, values, 5); + ASSERT_EQ(vector_size(v), 5); + vector_free(v); +} + +TEST(VectorCTest, AppendStoresCorrectValues) { + vector_c *v = vector_new(sizeof(int)); + constexpr int values[] = {1, 2, 3, 4, 5}; + vector_append(v, values, 5); + for (size_t i = 0; i < 5; ++i) { + ASSERT_EQ(*(int *) vector_at(v, i), values[i]); + } + vector_free(v); +} + +TEST(VectorCTest, InsertIncreasesSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_insert(v, 0, &value); + ASSERT_EQ(vector_size(v), 1); + vector_free(v); +} + +TEST(VectorCTest, InsertStoresCorrectValue) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_insert(v, 0, &value); + ASSERT_EQ(*(int *) vector_at(v, 0), value); + vector_free(v); +} + +TEST(VectorCTest, EraseDecreasesSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_push_back(v, &value); + vector_erase(v, 0); + ASSERT_EQ(vector_size(v), 0); + vector_free(v); +} + +TEST(VectorCTest, ClearResetsSize) { + vector_c *v = vector_new(sizeof(int)); + constexpr int value = 5; + vector_push_back(v, &value); + vector_clear(v); + ASSERT_EQ(vector_size(v), 0); + vector_free(v); +} + +TEST(VectorCTest, SizeReturnsZeroForNullptr) { + ASSERT_EQ(vector_size(nullptr), 0); +} + +TEST(VectorCTest, CapacityReturnsZeroForNullptr) { + ASSERT_EQ(vector_capacity(nullptr), 0); +} + +TEST(VectorCTest, PushBackDoesNotCrashForNullptr) { + constexpr int value = 5; + vector_push_back(nullptr, &value); +} + +TEST(VectorCTest, AtReturnsNullForNullptr) { + ASSERT_EQ(vector_at(nullptr, 0), nullptr); +} + +TEST(VectorCTest, PopBackDoesNotCrashForNullptr) { + vector_pop_back(nullptr); +} + +TEST(VectorCTest, AppendDoesNotCrashForNullptr) { + constexpr int values[] = {1, 2, 3, 4, 5}; + vector_append(nullptr, values, 5); +} + +TEST(VectorCTest, InsertDoesNotCrashForNullptr) { + constexpr int value = 5; + vector_insert(nullptr, 0, &value); +} + +TEST(VectorCTest, EraseDoesNotCrashForNullptr) { + vector_erase(nullptr, 0); +} + +TEST(VectorCTest, ClearDoesNotCrashForNullptr) { + vector_clear(nullptr); +}