Add some tests
This commit is contained in:
@@ -25,6 +25,7 @@ set(DUSK_CACHE_TARGET "dusk-target")
|
|||||||
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
|
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
|
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
|
||||||
set(DUSK_SOURCES_DIR "${DUSK_ROOT_DIR}/src")
|
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_TEMP_DIR "${DUSK_BUILD_DIR}/temp")
|
||||||
set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools")
|
set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools")
|
||||||
set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data")
|
set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data")
|
||||||
@@ -75,7 +76,7 @@ add_subdirectory(assets)
|
|||||||
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
|
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
|
||||||
find_package(SDL2 REQUIRED)
|
find_package(SDL2 REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||||
SDL2
|
SDL2
|
||||||
OpenGL::GL
|
OpenGL::GL
|
||||||
GL
|
GL
|
||||||
|
|||||||
14
cmake/modules/Findcmocka.cmake
Normal file
14
cmake/modules/Findcmocka.cmake
Normal file
@@ -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)
|
||||||
32
cmake/modules/dusktest.cmake
Normal file
32
cmake/modules/dusktest.cmake
Normal file
@@ -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()
|
||||||
@@ -9,6 +9,21 @@
|
|||||||
#include "debug/debug.h"
|
#include "debug/debug.h"
|
||||||
|
|
||||||
#ifndef ASSERTIONS_FAKED
|
#ifndef ASSERTIONS_FAKED
|
||||||
|
#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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#else
|
||||||
void assertTrueImpl(
|
void assertTrueImpl(
|
||||||
const char *file,
|
const char *file,
|
||||||
const int32_t line,
|
const int32_t line,
|
||||||
@@ -26,6 +41,7 @@
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void assertFalseImpl(
|
void assertFalseImpl(
|
||||||
const char *file,
|
const char *file,
|
||||||
|
|||||||
@@ -8,6 +8,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#ifdef DUSK_TEST_ASSERT
|
||||||
|
#include <cmocka.h>
|
||||||
|
|
||||||
|
#ifdef ASSERTIONS_FAKED
|
||||||
|
#error "Cannot fake assertions when DUSK_TEST_ASSERT is set."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ASSERTIONS_FAKED
|
#ifndef ASSERTIONS_FAKED
|
||||||
/**
|
/**
|
||||||
* Assert a given value to be true.
|
* Assert a given value to be true.
|
||||||
@@ -105,27 +113,77 @@
|
|||||||
const char *message
|
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) \
|
#define assertTrue(x, message) \
|
||||||
assertTrueImpl(__FILE__, __LINE__, 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) \
|
#define assertFalse(x, message) \
|
||||||
assertFalseImpl(__FILE__, __LINE__, 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) \
|
#define assertUnreachable(message) \
|
||||||
assertUnreachableImpl(__FILE__, __LINE__, 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) \
|
#define assertNotNull(pointer, message) \
|
||||||
assertNotNullImpl(__FILE__, __LINE__, 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) \
|
#define assertNull(pointer, message) \
|
||||||
assertNullImpl(__FILE__, __LINE__, 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) \
|
#define assertDeprecated(message) \
|
||||||
assertDeprecatedImpl(__FILE__, __LINE__, 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) \
|
#define assertStrLenMax(str, len, message) \
|
||||||
assertTrue(strlen(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) \
|
#define assertStrLenMin(str, len, message) \
|
||||||
assertTrue(strlen(str) >= len, message)
|
assertTrue(strlen(str) >= len, message)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
uint32_t mathNextPowTwo(uint32_t value) {
|
uint32_t mathNextPowTwo(uint32_t value) {
|
||||||
if(value == 0) return 1; // Handle zero case
|
if(value == 0) return 1; // Handle zero case
|
||||||
|
if(value >= 0x80000000) return 1; // Handle overflow case
|
||||||
|
|
||||||
value--;
|
value--;
|
||||||
value |= value >> 1;
|
value |= value >> 1;
|
||||||
value |= value >> 2;
|
value |= value >> 2;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include "util/math.h"
|
||||||
|
|
||||||
void * memoryAllocate(const size_t size) {
|
void * memoryAllocate(const size_t size) {
|
||||||
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
|
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) {
|
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize) {
|
||||||
assertNotNull(ptr, "Cannot resize NULL pointer.");
|
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(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);
|
void *newPointer = memoryAllocate(newSize);
|
||||||
assertNotNull(newPointer, "Memory resizing failed.");
|
assertNotNull(newPointer, "Memory resizing failed.");
|
||||||
memoryCopy(newPointer, *ptr, oldSize);
|
memoryCopy(newPointer, *ptr, mathMin(oldSize, newSize));
|
||||||
memoryFree(*ptr);
|
memoryFree(*ptr);
|
||||||
*ptr = newPointer;
|
*ptr = newPointer;
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
# Copyright (c) 2026 Dominic Masters
|
||||||
#
|
#
|
||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Tests
|
add_subdirectory(util)
|
||||||
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)
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
#pragma once
|
||||||
// return 1;
|
#include "dusk.h"
|
||||||
return 0;
|
#include "assert/assert.h"// Includes cmocka when DUSK_TEST_ASSERT is set
|
||||||
}
|
|
||||||
10
test/util/CMakeLists.txt
Normal file
10
test/util/CMakeLists.txt
Normal file
@@ -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)
|
||||||
206
test/util/test_math.c
Normal file
206
test/util/test_math.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
347
test/util/test_memory.c
Normal file
347
test/util/test_memory.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user