From aec937b04b0b3c4bd9586f79b82a7bf2ca573f0d Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 5 Jan 2026 16:13:14 -0600 Subject: [PATCH] Add some tests --- CMakeLists.txt | 3 +- cmake/modules/Findcmocka.cmake | 14 ++ cmake/modules/dusktest.cmake | 32 +++ src/assert/assert.c | 44 ++-- src/assert/assert.h | 58 ++++++ src/util/math.c | 2 + src/util/memory.c | 8 +- test/CMakeLists.txt | 10 +- test/{test_main.c => dusktest.h} | 7 +- test/util/CMakeLists.txt | 10 + test/util/test_math.c | 206 ++++++++++++++++++ test/util/test_memory.c | 347 +++++++++++++++++++++++++++++++ 12 files changed, 711 insertions(+), 30 deletions(-) create mode 100644 cmake/modules/Findcmocka.cmake create mode 100644 cmake/modules/dusktest.cmake rename test/{test_main.c => dusktest.h} (57%) create mode 100644 test/util/CMakeLists.txt create mode 100644 test/util/test_math.c create mode 100644 test/util/test_memory.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f887f5..666d64f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(DUSK_CACHE_TARGET "dusk-target") set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}") set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}") set(DUSK_SOURCES_DIR "${DUSK_ROOT_DIR}/src") +set(DUSK_TEST_DIR "${DUSK_ROOT_DIR}/test") set(DUSK_TEMP_DIR "${DUSK_BUILD_DIR}/temp") set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools") set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data") @@ -75,7 +76,7 @@ add_subdirectory(assets) if(DUSK_TARGET_SYSTEM STREQUAL "linux") find_package(SDL2 REQUIRED) find_package(OpenGL REQUIRED) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE + target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC SDL2 OpenGL::GL GL diff --git a/cmake/modules/Findcmocka.cmake b/cmake/modules/Findcmocka.cmake new file mode 100644 index 0000000..4297fab --- /dev/null +++ b/cmake/modules/Findcmocka.cmake @@ -0,0 +1,14 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +include(FetchContent) +FetchContent_Declare( + cmocka + GIT_REPOSITORY https://gitlab.com/cmocka/cmocka.git + GIT_TAG cmocka-2.0.1 +) + +FetchContent_MakeAvailable(cmocka) +set(cmocka_FOUND ON) \ No newline at end of file diff --git a/cmake/modules/dusktest.cmake b/cmake/modules/dusktest.cmake new file mode 100644 index 0000000..421620e --- /dev/null +++ b/cmake/modules/dusktest.cmake @@ -0,0 +1,32 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +find_package(cmocka REQUIRED) + +# Function to create a test executable from a single .c file +function(dusktest TEST_C_FILE) + # Create target name from file name + get_filename_component(TEST_NAME ${TEST_C_FILE} NAME_WE) + + # Create the executable target + add_executable(${TEST_NAME}) + + # Setup some compiler definitions. + target_compile_definitions(${TEST_NAME} PRIVATE + DUSK_TEST_ASSERT=1 + ) + + # Add the test file + target_sources(${TEST_NAME} PRIVATE ${TEST_C_FILE}) + + # Add sources, both common and test-specific + target_include_directories(${TEST_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${DUSK_TEST_DIR}) + + # Link against DuskCore and cmocka + target_link_libraries(${TEST_NAME} PUBLIC ${DUSK_LIBRARY_TARGET_NAME} cmocka) + + # Add as a CTest + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) +endfunction() \ No newline at end of file diff --git a/src/assert/assert.c b/src/assert/assert.c index 0d8b02e..9949aaf 100644 --- a/src/assert/assert.c +++ b/src/assert/assert.c @@ -9,23 +9,39 @@ #include "debug/debug.h" #ifndef ASSERTIONS_FAKED - void assertTrueImpl( - const char *file, - const int32_t line, - const bool x, - const char *message - ) { - if(x != true) { - debugPrint( - "Assertion Failed in %s:%i\n\n%s\n", + #ifdef DUSK_TEST_ASSERT + void assertTrueImpl( + const char *file, + const int32_t line, + const bool x, + const char *message + ) { + mock_assert( + x == true, + message, file, - line, - message + line ); - - abort(); } - } + #else + void assertTrueImpl( + const char *file, + const int32_t line, + const bool x, + const char *message + ) { + if(x != true) { + debugPrint( + "Assertion Failed in %s:%i\n\n%s\n", + file, + line, + message + ); + + abort(); + } + } + #endif void assertFalseImpl( const char *file, diff --git a/src/assert/assert.h b/src/assert/assert.h index 03e401e..268b5f5 100644 --- a/src/assert/assert.h +++ b/src/assert/assert.h @@ -8,6 +8,14 @@ #pragma once #include "dusk.h" +#ifdef DUSK_TEST_ASSERT + #include + + #ifdef ASSERTIONS_FAKED + #error "Cannot fake assertions when DUSK_TEST_ASSERT is set." + #endif +#endif + #ifndef ASSERTIONS_FAKED /** * Assert a given value to be true. @@ -105,27 +113,77 @@ const char *message ); + /** + * Asserts a given value to be true. + * + * @param x Value to assert as true. + * @param message Message to throw against assertion failure. + */ #define assertTrue(x, message) \ assertTrueImpl(__FILE__, __LINE__, x, message) + /** + * Asserts a given statement to be false. + * + * @param x Value to assert as false. + * @param message Message to throw against assertion failure. + */ #define assertFalse(x, message) \ assertFalseImpl(__FILE__, __LINE__, x, message) + /** + * Asserts that a given line of code is unreachable. Essentially a forced + * assertion failure, good for "edge cases" + * + * @param message Message to throw against assertion failure. + */ #define assertUnreachable(message) \ assertUnreachableImpl(__FILE__, __LINE__, message) + + /** + * Asserts a pointer is not null. + * + * @param pointer Pointer to check. + * @param message Message to throw against assertion failure. + */ #define assertNotNull(pointer, message) \ assertNotNullImpl(__FILE__, __LINE__, pointer, message) + /** + * Asserts a pointer is null. + * + * @param pointer Pointer to check. + * @param message Message to throw against assertion failure. + */ #define assertNull(pointer, message) \ assertNullImpl(__FILE__, __LINE__, pointer, message) + /** + * Asserts a function or code path is deprecated. + * + * @param message Message to throw against assertion failure. + */ #define assertDeprecated(message) \ assertDeprecatedImpl(__FILE__, __LINE__, message) + /** + * Asserts a string's length is less than a maximum. + * + * @param str String to check. + * @param len Maximum length. + * @param message Message to throw against assertion failure. + */ #define assertStrLenMax(str, len, message) \ assertTrue(strlen(str) < len, message) + /** + * Asserts a string's length is at least a minimum. + * + * @param str String to check. + * @param len Minimum length. + * @param message Message to throw against assertion failure. + */ #define assertStrLenMin(str, len, message) \ assertTrue(strlen(str) >= len, message) diff --git a/src/util/math.c b/src/util/math.c index 55f8f7a..ca640df 100644 --- a/src/util/math.c +++ b/src/util/math.c @@ -9,6 +9,8 @@ uint32_t mathNextPowTwo(uint32_t value) { if(value == 0) return 1; // Handle zero case + if(value >= 0x80000000) return 1; // Handle overflow case + value--; value |= value >> 1; value |= value >> 2; diff --git a/src/util/memory.c b/src/util/memory.c index 3e0fea8..cef2e86 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -7,6 +7,7 @@ #include "memory.h" #include "assert/assert.h" +#include "util/math.h" void * memoryAllocate(const size_t size) { assertTrue(size > 0, "Cannot allocate 0 bytes of memory."); @@ -83,14 +84,13 @@ void memoryReallocate(void **ptr, const size_t size) { void memoryResize(void **ptr, const size_t oldSize, const size_t newSize) { assertNotNull(ptr, "Cannot resize NULL pointer."); + assertTrue(newSize > 0, "Cannot resize to 0 bytes of memory."); + assertTrue(oldSize > 0, "Old size cannot be 0 bytes."); if(newSize == oldSize) return; - if(oldSize == 0) return memoryReallocate(ptr, newSize); - - assertTrue(newSize > oldSize, "New size must be greater than old size."); void *newPointer = memoryAllocate(newSize); assertNotNull(newPointer, "Memory resizing failed."); - memoryCopy(newPointer, *ptr, oldSize); + memoryCopy(newPointer, *ptr, mathMin(oldSize, newSize)); memoryFree(*ptr); *ptr = newPointer; } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 36c245f..0534717 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,10 +1,6 @@ -# Copyright (c) 2025 Dominic Masters -# +# Copyright (c) 2026 Dominic Masters +# # This software is released under the MIT License. # https://opensource.org/licenses/MIT -# Tests -add_executable(test_main test_main.c) -target_include_directories(test_main PUBLIC ${CMAKE_CURRENT_LIST_DIR}) -target_link_libraries(test_main PUBLIC ${DUSK_LIBRARY_TARGET_NAME}) -add_test( NAME tests1 COMMAND test_main) \ No newline at end of file +add_subdirectory(util) \ No newline at end of file diff --git a/test/test_main.c b/test/dusktest.h similarity index 57% rename from test/test_main.c rename to test/dusktest.h index a83c319..d1d8cb8 100644 --- a/test/test_main.c +++ b/test/dusktest.h @@ -5,7 +5,6 @@ * https://opensource.org/licenses/MIT */ -int main(int argc, char **argv) { - // return 1; - return 0; -} \ No newline at end of file +#pragma once +#include "dusk.h" +#include "assert/assert.h"// Includes cmocka when DUSK_TEST_ASSERT is set \ No newline at end of file diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt new file mode 100644 index 0000000..562d640 --- /dev/null +++ b/test/util/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +include(dusktest) + +# Tests +dusktest(test_math.c) +dusktest(test_memory.c) \ No newline at end of file diff --git a/test/util/test_math.c b/test/util/test_math.c new file mode 100644 index 0000000..fa3d12e --- /dev/null +++ b/test/util/test_math.c @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "dusktest.h" +#include "util/math.h" + +static void test_mathNextPowTwo(void **state) { + (void)state; + + // Positive integers + assert_int_equal(mathNextPowTwo(0), 1); + assert_int_equal(mathNextPowTwo(1), 1); + assert_int_equal(mathNextPowTwo(2), 2); + assert_int_equal(mathNextPowTwo(3), 4); + assert_int_equal(mathNextPowTwo(4), 4); + assert_int_equal(mathNextPowTwo(5), 8); + assert_int_equal(mathNextPowTwo(15), 16); + assert_int_equal(mathNextPowTwo(16), 16); + assert_int_equal(mathNextPowTwo(17), 32); + assert_int_equal(mathNextPowTwo(31), 32); + assert_int_equal(mathNextPowTwo(32), 32); + assert_int_equal(mathNextPowTwo(33), 64); + assert_int_equal(mathNextPowTwo(63), 64); + assert_int_equal(mathNextPowTwo(64), 64); + assert_int_equal(mathNextPowTwo(65), 128); + + // Large values + assert_int_equal(mathNextPowTwo(1000), 1024); + assert_int_equal(mathNextPowTwo(4095), 4096); + + // Zero + assert_int_equal(mathNextPowTwo(0), 1); + + // Max Value + assert_int_equal(mathNextPowTwo(0xFFFFFFFF), 1); +} + +static void test_mathMax(void **state) { + (void)state; + // Test positive integers + assert_int_equal(mathMax(1, 2), 2); + assert_int_equal(mathMax(2, 1), 2); + + // Test negative integers + assert_int_equal(mathMax(-1, -2), -1); + assert_int_equal(mathMax(-4, -2), -2); + + // Test 0 + assert_int_equal(mathMax(0, -1), 0); + assert_int_equal(mathMax(-1, 0), 0); + + // Test large values + assert_int_equal(mathMax(1000000, 999999), 1000000); + assert_int_equal(mathMax(999999, 1000000), 1000000); + + // Test mixed negative and positive + assert_int_equal(mathMax(-32, 5), 5); + assert_int_equal(mathMax(5, -32), 5); + + // Floats with floats + assert_float_equal(mathMax(1.5f, 2.5f), 2.5f, 0.0001f); + assert_float_equal(mathMax(2.5f, 1.5f), 2.5f, 0.0001f); + assert_float_equal(mathMax(-1.5f, -2.5f), -1.5f, 0.0001f); + assert_float_equal(mathMax(-2.5f, -1.5f), -1.5f, 0.0001f); + assert_float_equal(mathMax(0.0f, -1.5f), 0.0f, 0.0001f); + assert_float_equal(mathMax(-1.5f, 0.0f), 0.0f, 0.0001f); + assert_float_equal(mathMax(500.0f, -333.0f), 500.0f, 0.0001f); + assert_float_equal(mathMax(-333.0f, 500.0f), 500.0f, 0.0001f); + + // Floats with integers + assert_float_equal(mathMax(1, 2.5f), 2.5f, 0.0001f); + assert_float_equal(mathMax(2.5f, 1), 2.5f, 0.0001f); + assert_float_equal(mathMax(-1, -2.5f), -1.0f, 0.0001f); + assert_float_equal(mathMax(-2.5f, -1), -1.0f, 0.0001f); + assert_float_equal(mathMax(0, -1.5f), 0.0f, 0.0001f); + assert_float_equal(mathMax(-1.5f, 0), 0.0f, 0.0001f); + assert_float_equal(mathMax(500, -333.5f), 500.0f, 0.0001f); + assert_float_equal(mathMax(-333.5f, 500), 500.0f, 0.0001f); +} + +static void test_mathMin(void **state) { + (void)state; + + // Positive integers + assert_int_equal(mathMin(1, 2), 1); + assert_int_equal(mathMin(2, 1), 1); + assert_int_equal(mathMin(0, 5), 0); + assert_int_equal(mathMin(5, 0), 0); + + // Negative integers + assert_int_equal(mathMin(-1, -2), -2); + assert_int_equal(mathMin(-2, -1), -2); + assert_int_equal(mathMin(-5, -10), -10); + assert_int_equal(mathMin(-10, -5), -10); + + // Mixed negative and positive + assert_int_equal(mathMin(-5, 32), -5); + assert_int_equal(mathMin(32, -5), -5); + + // Large values + assert_int_equal(mathMin(1000000, 999999), 999999); + assert_int_equal(mathMin(999999, 1000000), 999999); + + // Floats with floats + assert_float_equal(mathMin(1.5f, 2.5f), 1.5f, 0.0001f); + assert_float_equal(mathMin(2.5f, 1.5f), 1.5f, 0.0001f); + assert_float_equal(mathMin(-1.5f, -2.5f), -2.5f, 0.0001f); + assert_float_equal(mathMin(-2.5f, -1.5f), -2.5f, 0.0001f); + assert_float_equal(mathMin(0.0f, -1.5f), -1.5f, 0.0001f); + assert_float_equal(mathMin(-1.5f, 0.0f), -1.5f, 0.0001f); + assert_float_equal(mathMin(500.0f, -333.0f), -333.0f, 0.0001f); + assert_float_equal(mathMin(-333.0f, 500.0f), -333.0f, 0.0001f); + + // Floats with integers + assert_float_equal(mathMin(1, 2.5f), 1.0f, 0.0001f); + assert_float_equal(mathMin(2.5f, 1), 1.0f, 0.0001f); + assert_float_equal(mathMin(-1, -2.5f), -2.5f, 0.0001f); + assert_float_equal(mathMin(-2.5f, -1), -2.5f, 0.0001f); + assert_float_equal(mathMin(0, -1.5f), -1.5f, 0.0001f); + assert_float_equal(mathMin(-1.5f, 0), -1.5f, 0.0001f); + assert_float_equal(mathMin(500, -333.5f), -333.5f, 0.0001f); + assert_float_equal(mathMin(-333.5f, 500), -333.5f, 0.0001f); +} + +static void test_mathClamp(void **state) { + (void)state; + + // Positive Integer bounds. + assert_int_equal(mathClamp(5, 1, 10), 5); // Within bounds + assert_int_equal(mathClamp(0, 1, 10), 1); // Below lower bound + assert_int_equal(mathClamp(15, 1, 10), 10); // Above upper bound + + // Negative Integer bounds. + assert_int_equal(mathClamp(-5, -10, -1), -5); // Within bounds + assert_int_equal(mathClamp(-15, -10, -1), -10); // Below lower bound + assert_int_equal(mathClamp(0, -10, -1), -1); // Above upper bound + + // Mixed Integer bounds. + assert_int_equal(mathClamp(0, -5, 5), 0); // Within bounds + assert_int_equal(mathClamp(-10, -5, 5), -5); // Below lower bound + assert_int_equal(mathClamp(10, -5, 5), 5); // Above upper bound + + // Positive Float bounds. + assert_float_equal(mathClamp(5.5f, 1.0f, 10.0f), 5.5f, 0.0001f); // Within + assert_float_equal(mathClamp(0.5f, 1.0f, 10.0f), 1.0f, 0.0001f); // Below + assert_float_equal(mathClamp(15.5f, 1.0f, 10.0f), 10.0f, 0.0001f); // Above + + // Negative Float bounds. + assert_float_equal(mathClamp(-5.5f, -10.0f, -1.0f), -5.5f, 0.0001f); // Within + assert_float_equal(mathClamp(-15.5f, -10.0f, -1.0f), -10.0f, 0.0001f);// Below + assert_float_equal(mathClamp(0.0f, -10.0f, -1.0f), -1.0f, 0.0001f); // Above + + // Mixed Float bounds. + assert_float_equal(mathClamp(0.0f, -5.0f, 5.0f), 0.0f, 0.0001f); // Within + assert_float_equal(mathClamp(-10.0f, -5.0f, 5.0f), -5.0f, 0.0001f); // Below + assert_float_equal(mathClamp(10.0f, -5.0f, 5.0f), 5.0f, 0.0001f); // Above + + // With integers and floats mixed + assert_float_equal(mathClamp(5, 1.0f, 10.0f), 5.0f, 0.0001f); // Within + assert_float_equal(mathClamp(0, 1.0f, 10.0f), 1.0f, 0.0001f); // Below + assert_float_equal(mathClamp(15, 1.0f, 10.0f), 10.0f, 0.0001f); // Above + assert_float_equal(mathClamp(5.5f, 1, 10), 5.5f, 0.0001f); // Within + assert_float_equal(mathClamp(0.5f, 1, 10), 1.0f, 0.0001f); // Below + assert_float_equal(mathClamp(15.5f, 1, 10), 10.0f, 0.0001f); // Above + assert_float_equal(mathClamp(-5, -10.0f, -1.0f), -5.0f, 0.0001f); // Within + assert_float_equal(mathClamp(-15, -10.0f, -1.0f), -10.0f, 0.0001f);// Below + assert_float_equal(mathClamp(0, -10.0f, -1.0f), -1.0f, 0.0001f); // Above + assert_float_equal(mathClamp(-5.5f, -10, -1), -5.5f, 0.0001f); // Within + assert_float_equal(mathClamp(-15.5f, -10, -1), -10.0f, 0.0001f);// Below + assert_float_equal(mathClamp(0.0f, -10, -1), -1.0f, 0.0001f); // Above +} + +static void test_mathAbs(void **state) { + (void)state; + + // Positive integers + assert_int_equal(mathAbs(5), 5); + assert_int_equal(mathAbs(0), 0); + + // Negative integers + assert_int_equal(mathAbs(-5), 5); + assert_int_equal(mathAbs(-100), 100); + + // Positive floats + assert_float_equal(mathAbs(5.5f), 5.5f, 0.0001f); + assert_float_equal(mathAbs(0.0f), 0.0f, 0.0001f); + + // Negative floats + assert_float_equal(mathAbs(-5.5f), 5.5f, 0.0001f); + assert_float_equal(mathAbs(-100.25f), 100.25f, 0.0001f); +} + +int main(int argc, char **argv) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_mathNextPowTwo), + cmocka_unit_test(test_mathMax), + cmocka_unit_test(test_mathMin), + cmocka_unit_test(test_mathClamp), + cmocka_unit_test(test_mathAbs), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file diff --git a/test/util/test_memory.c b/test/util/test_memory.c new file mode 100644 index 0000000..b38661a --- /dev/null +++ b/test/util/test_memory.c @@ -0,0 +1,347 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "dusktest.h" +#include "util/memory.h" + +static void test_memoryAllocate(void **state) { + (void)state; + + size_t size = 128; + void *ptr = memoryAllocate(size); + assert_non_null(ptr); + memoryFree(ptr); + + // Should not be able to allocate 0 bytes + expect_assert_failure(memoryAllocate(0)); + + // Should not be able to allocate more memory than possible + expect_assert_failure(memoryAllocate(SIZE_MAX)); +} + +static void test_memoryFree(void **state) { + (void)state; + + // Create some memory + size_t size = 64; + void *ptr = memoryAllocate(size); + assert_non_null(ptr); + + // Free + memoryFree(ptr); + + // Expect unable to free NULL + expect_assert_failure(memoryFree(NULL)); +} + +static void test_memoryCopy(void **state) { + (void)state; + + // Create some memory + size_t size = 32; + void *src = memoryAllocate(size); + void *dest = memoryAllocate(size); + assert_non_null(src); + assert_non_null(dest); + + // Fill source with known pattern and copy + memorySet(src, 0xAB, size); + memoryCopy(dest, src, size); + assert_memory_equal(src, dest, size); + + // Should not be able to copy to NULL + expect_assert_failure(memoryCopy(NULL, src, size)); + + // Should not be able to copy from NULL + expect_assert_failure(memoryCopy(dest, NULL, size)); + + // Cannot copy 0 bytes + expect_assert_failure(memoryCopy(dest, src, 0)); + + // Cannot copy to itself + expect_assert_failure(memoryCopy(src, src, size)); + + memoryFree(src); + memoryFree(dest); +} + +static void test_memorySet(void **state) { + (void)state; + + // Allocate memory + size_t size = 16; + void *ptr = memoryAllocate(size); + assert_non_null(ptr); + + // Fill with pattern + memorySet(ptr, 0xCD, size); + uint8_t expected[16]; + for(size_t i = 0; i < size; i++) { + expected[i] = 0xCD; + } + assert_memory_equal(ptr, expected, size); + + // Cannot set to NULL + expect_assert_failure(memorySet(NULL, 0x00, size)); + + // Canot set 0 bytes + expect_assert_failure(memorySet(ptr, 0x00, 0)); + + memoryFree(ptr); +} + +static void test_memoryZero(void **state) { + (void)state; + + // Create some memory + size_t size = 20; + void *ptr = memoryAllocate(size); + assert_non_null(ptr); + + // Fill and then zero + memorySet(ptr, 0xEF, size); + memoryZero(ptr, size); + + // All memory should be zeroed + uint8_t expected[20] = {0}; + assert_memory_equal(ptr, expected, size); + + // Cannot zero to NULL pointer + expect_assert_failure(memoryZero(NULL, size)); + + // Cannot zero 0 bytes + expect_assert_failure(memoryZero(ptr, 0)); + + memoryFree(ptr); +} + +static void test_memoryCopyRangeSafe(void **state) { + (void)state; + + // Create some memory + size_t size = 10; + void *src = memoryAllocate(size); + void *dest = memoryAllocate(size); + assert_non_null(src); + assert_non_null(dest); + + // Initialize source with known pattern + for(size_t i = 0; i < size; i++) { + ((uint8_t*)src)[i] = (uint8_t)(i + 1); // 1, 2, ..., 10 + } + + // Copy a range from src[2] to src[7] (5 bytes) into dest + memoryCopyRangeSafe(dest, (uint8_t*)src + 2, (uint8_t*)src + 7, size); + uint8_t expected[10] = {3, 4, 5, 6, 7}; // Expected data in dest + assert_memory_equal(dest, expected, 5); + + // Cannot copy to NULL destination + expect_assert_failure(memoryCopyRangeSafe(NULL, src, (uint8_t*)src + 5, size)); + + // Cannot copy from NULL start + expect_assert_failure(memoryCopyRangeSafe(dest, NULL, (uint8_t*)src + 5, size)); + + // Cannot copy from NULL end + expect_assert_failure(memoryCopyRangeSafe(dest, (uint8_t*)src, NULL, size)); + + // Start and end are the same + expect_assert_failure(memoryCopyRangeSafe(dest, (uint8_t*)src, (uint8_t*)src, size)); + + // End is before start + expect_assert_failure(memoryCopyRangeSafe(dest, (uint8_t*)src + 5, (uint8_t*)src + 2, size)); + + // Size to copy exceeds maximum + expect_assert_failure(memoryCopyRangeSafe(dest, (uint8_t*)src, (uint8_t*)src + 10, 5)); + + memoryFree(src); + memoryFree(dest); +} + +static void test_memoryMove(void **state) { + (void)state; + + // Create some memory + size_t size = 15; + void *ptr = memoryAllocate(size + 5); // Extra space for overlap + assert_non_null(ptr); + + // Initialize memory with known pattern + for(size_t i = 0; i < size + 5; i++) { + ((uint8_t*)ptr)[i] = (uint8_t)(i + 1); // 1, 2, ..., size+5 + } + + // Move overlapping region: from ptr[0..14] to ptr[5..19] + memoryMove((uint8_t*)ptr + 5, ptr, size); + + // Expected pattern after move + uint8_t expected[20]; + for(size_t i = 0; i < 5; i++) { + expected[i] = (uint8_t)(i + 1); // Original first 5 bytes + } + for(size_t i = 5; i < 20; i++) { + expected[i] = (uint8_t)(i - 4); // Moved bytes + } + assert_memory_equal(ptr, expected, size + 5); + + // Cannot move to NULL + expect_assert_failure(memoryMove(NULL, ptr, size)); + + // Cannot move from NULL + expect_assert_failure(memoryMove(ptr, NULL, size)); + + // Cannot move 0 bytes + expect_assert_failure(memoryMove(ptr, ptr, 0)); + + // Cannot move to itself + expect_assert_failure(memoryMove(ptr, ptr, size)); + + memoryFree(ptr); +} + +static void test_memoryCompare(void **state) { + (void)state; + + // Create two memory blocks + size_t size = 8; + void *a = memoryAllocate(size); + void *b = memoryAllocate(size); + assert_non_null(a); + assert_non_null(b); + + // Initialize both with same data + for(size_t i = 0; i < size; i++) { + ((uint8_t*)a)[i] = (uint8_t)(i + 1); + ((uint8_t*)b)[i] = (uint8_t)(i + 1); + } + + // They should be equal + assert_int_equal(memoryCompare(a, b, size), 0); + + // Change b and compare again + ((uint8_t*)b)[size - 1] = 0xFF; + assert_true(memoryCompare(a, b, size) < 0); + + // Change a and compare again + ((uint8_t*)a)[size - 1] = 0xFF; + ((uint8_t*)a)[size - 2] = 0xFE; + assert_true(memoryCompare(a, b, size) > 0); + + // CAnnot compare with NULL a + expect_assert_failure(memoryCompare(NULL, b, size)); + + // Cannot compare with NULL b + expect_assert_failure(memoryCompare(a, NULL, size)); + + // Comparing with self always equal + assert_int_equal(memoryCompare(a, a, size), 0); + assert_int_equal(memoryCompare(b, b, size), 0); + + // Cannot compare 0 bytes + expect_assert_failure(memoryCompare(a, b, 0)); + + memoryFree(a); + memoryFree(b); +} + +static void test_memoryReallocate(void **state) { + (void)state; + + size_t initialSize = 16; + void *ptr = memoryAllocate(initialSize); + assert_non_null(ptr); + + // Reallocate to a larger size + size_t newSize = 32; + memoryReallocate(&ptr, newSize); + assert_non_null(ptr); + + // Reallocate to a smaller size + size_t smallerSize = 8; + memoryReallocate(&ptr, smallerSize); + assert_non_null(ptr); + + // Cannot realloc to size 0 + expect_assert_failure(memoryReallocate(&ptr, 0)); + + // Cannot realloc NULL pointer + expect_assert_failure(memoryReallocate(NULL, 16)); + + // Cannot reallocate more memory than possible + expect_assert_failure(memoryReallocate(&ptr, SIZE_MAX)); + + // All we really care about is that the pointer is valid after reallocations + memoryFree(ptr); +} + +static void test_memoryResize(void **state) { + (void)state; + + size_t initialSize = 32; + void *ptr = memoryAllocate(initialSize); + assert_non_null(ptr); + + // Initialize memory + for(size_t i = 0; i < initialSize; i++) { + ((uint8_t*)ptr)[i] = (uint8_t)(i + 1); + } + + // Equal size shouldn't touch the pointer + void *oldPtr = ptr; + memoryResize(&ptr, initialSize, initialSize); + assert_non_null(ptr); + assert_ptr_equal(ptr, oldPtr); + + // Reallocate to a larger size + size_t newSize = 64; + memoryResize(&ptr, initialSize, newSize); + assert_non_null(ptr); + + // Check that old data is preserved + for(size_t i = 0; i < initialSize; i++) { + assert_int_equal(((uint8_t*)ptr)[i], (uint8_t)(i + 1)); + } + + // Reallocate to a smaller size + size_t smallerSize = 16; + memoryResize(&ptr, newSize, smallerSize); + assert_non_null(ptr); + + // Check that data is still correct up to the smaller size + for(size_t i = 0; i < smallerSize; i++) { + assert_int_equal(((uint8_t*)ptr)[i], (uint8_t)(i + 1)); + } + + // Cannot realloc to size 0 + expect_assert_failure(memoryResize(&ptr, smallerSize, 0)); + + // Cannot take NULL + expect_assert_failure(memoryResize(NULL, smallerSize, 16)); + + // memoryResize with oldsize of 0 not possible + expect_assert_failure(memoryResize(&ptr, 0, 16)); + + // Cannot resize more memory than possible + expect_assert_failure(memoryResize(&ptr, smallerSize, SIZE_MAX)); + + memoryFree(ptr); +} + +int main(int argc, char **argv) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_memoryAllocate), + cmocka_unit_test(test_memoryFree), + cmocka_unit_test(test_memoryCopy), + cmocka_unit_test(test_memorySet), + cmocka_unit_test(test_memoryZero), + cmocka_unit_test(test_memoryCopyRangeSafe), + cmocka_unit_test(test_memoryMove), + cmocka_unit_test(test_memoryCompare), + cmocka_unit_test(test_memoryReallocate), + cmocka_unit_test(test_memoryResize), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file