Browse Source

Clang coverage, smart allocator

Kajetan Johannes Hammerle 10 months ago
parent
commit
eb948e59e7
13 changed files with 468 additions and 66 deletions
  1. 13 14
      CMakeLists.txt
  2. 36 0
      include/core/utils/Error.h
  3. 16 0
      include/core/utils/Types.h
  4. 34 0
      include/core/utils/Utility.h
  5. 8 0
      src/ErrorSimulator.c
  6. 14 0
      src/ErrorSimulator.h
  7. 199 0
      src/Utility.c
  8. 25 5
      tasks
  9. 23 39
      test/Main.c
  10. 1 0
      test/Test.c
  11. 10 8
      test/Test.h
  12. 6 0
      test/Tests.h
  13. 83 0
      test/modules/UtilityTests.c

+ 13 - 14
CMakeLists.txt

@@ -5,9 +5,8 @@ set(CMAKE_C_STANDARD 23)
 
 set(SRC
     "src/Logger.c"
-    #"src/Utility.cpp"
+    "src/Utility.c"
     #"src/Error.cpp"
-    #"src/New.cpp"
     #"src/Buffer.cpp"
     #"src/Clock.cpp"
     #"src/Random.cpp"
@@ -23,7 +22,6 @@ set(SRC
     #"src/Mutex.cpp"
     #"src/SpinLock.cpp"
     #"src/FileReader.cpp"
-    #"src/ErrorSimulator.cpp"
     #"src/ArrayString.cpp"
 )
 
@@ -32,7 +30,7 @@ set(SRC_TESTS
     "test/Test.c"
     #"test/modules/ArrayTests.cpp"
     #"test/modules/ArrayStringTests.cpp"
-    #"test/modules/UtilityTests.cpp"
+    "test/modules/UtilityTests.c"
     #"test/modules/ArrayListTests.cpp"
     #"test/modules/BitArrayTests.cpp"
     #"test/modules/MathTests.cpp"
@@ -59,7 +57,6 @@ set(SRC_TESTS
     #"test/modules/ThreadTests.cpp"
     #"test/modules/FileReaderTests.cpp"
     #"test/modules/ErrorTests.cpp"
-    #"test/modules/NewTests.cpp"
     #"test/modules/HashedStringTests.cpp"
 )
 
@@ -72,14 +69,18 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
     set(COMPILE_OPTIONS -flto)
     set(LINK_OPTIONS -flto)
     set(LOG_LEVEL 2)
-    set(ERROR_SIMULATOR "")
+    set(DEFINITIONS "")
 else()
     if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
         set(COMPILE_OPTIONS --coverage)
+        set(LINK_OPTIONS gcov)
+    else()
+        set(COMPILE_OPTIONS -fprofile-instr-generate -fcoverage-mapping)
+        set(LINK_OPTIONS ${COMPILE_OPTIONS})
     endif()
-    set(LINK_OPTIONS gcov)
     set(LOG_LEVEL 4)
-    set(ERROR_SIMULATOR "ERROR_SIMULATOR")
+    set(DEFINITIONS ERROR_SIMULATOR CORE_CHECK_MEMORY)
+    list(APPEND SRC "src/ErrorSimulator.c")
 endif()
 
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
@@ -161,10 +162,9 @@ target_compile_options(core PUBLIC
 )
 target_compile_definitions(core 
     PUBLIC CORE_LOG_LEVEL=${LOG_LEVEL}
-    PRIVATE ${ERROR_SIMULATOR}
+    PRIVATE ${DEFINITIONS}
 )
 target_link_libraries(core 
-    PUBLIC -nodefaultlibs c m
     PRIVATE ${LINK_OPTIONS}
 )
 target_sources(core PUBLIC 
@@ -183,7 +183,6 @@ target_sources(core PUBLIC
 #        ./include/core/data/RingBuffer.hpp
 #        ./include/core/thread/Thread.hpp
 #        ./include/core/utils/HashCode.hpp
-#        ./include/core/utils/New.hpp
         ./include/core/utils/Check.h
 #        ./include/core/utils/Buffer.hpp
 #        ./include/core/utils/Random.hpp
@@ -192,11 +191,11 @@ target_sources(core PUBLIC
 #        ./include/core/utils/Color.hpp
         ./include/core/utils/Logger.h
 #        ./include/core/utils/ArrayString.hpp
-#        ./include/core/utils/Utility.hpp
+        ./include/core/utils/Utility.h
 #        ./include/core/utils/Meta.hpp
 #        ./include/core/utils/AlignedData.hpp
 #        ./include/core/utils/Clock.hpp
-#        ./include/core/utils/Error.hpp
+        ./include/core/utils/Error.h
 #        ./include/core/math/Quaternion.hpp
 #        ./include/core/math/Box.hpp
 #        ./include/core/math/Frustum.hpp
@@ -214,7 +213,7 @@ install(TARGETS core FILE_SET HEADERS)
 
 add_executable(test ${SRC_TESTS})
 target_link_libraries(test PRIVATE core)
-target_compile_definitions(test PRIVATE ${ERROR_SIMULATOR})
+target_compile_definitions(test PRIVATE ${DEFINITIONS})
 
 #add_executable(performance ${SRC_PERFORMANCE})
 #target_link_libraries(performance PRIVATE core)

+ 36 - 0
include/core/utils/Error.h

@@ -0,0 +1,36 @@
+#ifndef CORE_ERROR_H
+#define CORE_ERROR_H
+
+#include "core/utils/Check.h"
+
+typedef int CoreError;
+#define CORE_ERROR_CODE(n) ((CoreError)(1 << n))
+
+#define CORE_ERROR_NONE 0
+#define CORE_ERROR_ERROR CORE_ERROR_CODE(0)
+#define CORE_ERROR_NEGATIVE_ARGUMENT CORE_ERROR_CODE(1)
+#define CORE_ERROR_CAPACITY_REACHED CORE_ERROR_CODE(2)
+#define CORE_ERROR_BLOCKED_STDOUT CORE_ERROR_CODE(3)
+#define CORE_ERROR_OUT_OF_MEMORY CORE_ERROR_CODE(4)
+#define CORE_ERROR_INVALID_CHAR CORE_ERROR_CODE(5)
+#define CORE_ERROR_NOT_FOUND CORE_ERROR_CODE(6)
+#define CORE_ERROR_INVALID_STATE CORE_ERROR_CODE(7)
+#define CORE_ERROR_INVALID_INDEX CORE_ERROR_CODE(8)
+#define CORE_ERROR_INVALID_ARGUMENT CORE_ERROR_CODE(9)
+#define CORE_ERROR_TIME_NOT_AVAILABLE CORE_ERROR_CODE(10)
+#define CORE_ERROR_SLEEP_INTERRUPTED CORE_ERROR_CODE(11)
+#define CORE_ERROR_THREAD_ERROR CORE_ERROR_CODE(12)
+#define CORE_ERROR_MUTEX_ERROR CORE_ERROR_CODE(13)
+#define CORE_ERROR_EXISTING_KEY CORE_ERROR_CODE(14)
+#define CORE_ERROR_CANNOT_OPEN_FILE CORE_ERROR_CODE(15)
+#define CORE_ERROR_END_OF_FILE CORE_ERROR_CODE(16)
+
+#define CORE_RETURN_ERROR(checked)                                             \
+    {                                                                          \
+        CoreError error = checked;                                             \
+        if(error != CORE_ERROR_NONE) [[unlikely]] {                            \
+            return error;                                                      \
+        }                                                                      \
+    }
+
+#endif

+ 16 - 0
include/core/utils/Types.h

@@ -0,0 +1,16 @@
+#ifndef CORE_TYPES_HPP
+#define CORE_TYPES_HPP
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef int64_t i64;
+typedef int32_t i32;
+typedef int16_t i16;
+typedef int8_t i8;
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+#endif

+ 34 - 0
include/core/utils/Utility.h

@@ -0,0 +1,34 @@
+#ifndef CORE_UTILITY_H
+#define CORE_UTILITY_H
+
+#include "core/utils/Check.h"
+#include "core/utils/Types.h"
+
+size_t corePopCount(u64 u);
+
+typedef void (*CoreExitHandler)(int, void*);
+void coreExitWithHandler(const char* file, int line, int value);
+void coreSetExitHandler(CoreExitHandler h, void* data);
+#define CORE_EXIT(exitValue) coreExitWithHandler(__FILE__, __LINE__, exitValue)
+
+typedef void (*CoreOutOfMemoryHandler)(void*);
+void coreSetOutOfMemoryHandler(CoreOutOfMemoryHandler h, void* data);
+
+#ifdef CORE_CHECK_MEMORY
+void* coreDebugAllocate(const char* file, int line, size_t n);
+void* coreDebugReallocate(const char* file, int line, void* p, size_t n);
+void coreFreeDebug(const char* file, int line, void* p);
+void corePrintMemoryReport();
+
+#define coreAllocate(n) coreDebugAllocate(__FILE__, __LINE__, n)
+#define coreReallocate(p, n) coreDebugReallocate(__FILE__, __LINE__, p, n)
+#define coreFree(p) coreFreeDebug(__FILE__, __LINE__, p)
+
+#else
+void* coreAllocate(size_t n);
+void* coreReallocate(void* p, size_t n);
+void coreFree(void* p);
+#define corePrintMemoryReport()
+#endif
+
+#endif

+ 8 - 0
src/ErrorSimulator.c

@@ -0,0 +1,8 @@
+#ifdef ERROR_SIMULATOR
+
+#include "ErrorSimulator.h"
+
+bool coreFailFileClose = false;
+bool coreFailTimeGet = false;
+
+#endif

+ 14 - 0
src/ErrorSimulator.h

@@ -0,0 +1,14 @@
+#ifndef CORE_ERROR_SIMULATOR_H
+#define CORE_ERROR_SIMULATOR_H
+
+#ifdef ERROR_SIMULATOR
+extern bool coreFailFileClose;
+extern bool coreFailTimeGet;
+#define CORE_FILE_CLOSE_FAIL coreFailFile
+#define CORE_TIME_GET_FAIL coreFailTimeGet
+#else
+#define CORE_FILE_CLOSE_FAIL false
+#define CORE_TIME_GET_FAIL false
+#endif
+
+#endif

+ 199 - 0
src/Utility.c

@@ -0,0 +1,199 @@
+#include "core/utils/Utility.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ErrorSimulator.h"
+#include "core/utils/Error.h"
+#include "core/utils/Logger.h"
+
+static CoreExitHandler exitHandler = nullptr;
+static void* exitData = nullptr;
+static CoreOutOfMemoryHandler outOfMemoryHandler = nullptr;
+static void* outOfMemoryData = nullptr;
+
+size_t corePopCount(u64 u) {
+    static const u64 map[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+    size_t sum = 0;
+    for(size_t i = 0; i < sizeof(u64) * 8; i += 4) {
+        sum += map[(u >> i) & 0xF];
+    }
+    return sum;
+}
+
+void coreExitWithHandler(const char* file, int line, int value) {
+    if(value != 0) {
+        file = coreGetShortFileName(file);
+        CORE_LOG_ERROR("Exit from %s:%d with value %d", file, line, value);
+    }
+    if(exitHandler != nullptr) {
+        exitHandler(value, exitData);
+    }
+    exit(value);
+}
+
+void coreSetExitHandler(CoreExitHandler eh, void* data) {
+    exitHandler = eh;
+    exitData = data;
+}
+
+void coreSetOutOfMemoryHandler(CoreOutOfMemoryHandler h, void* data) {
+    outOfMemoryHandler = h;
+    outOfMemoryData = data;
+}
+
+static void* exitOnNull(void* p, size_t n) {
+    if(p == nullptr) {
+        CORE_LOG_ERROR("Out of memory, requested '%zd' bytes", n)
+        CORE_EXIT(1);
+    }
+    return p;
+}
+
+static void* coreRealAllocate(size_t n) {
+    void* p = malloc(n);
+    while(p == nullptr && outOfMemoryHandler != nullptr) {
+        outOfMemoryHandler(outOfMemoryData);
+        p = malloc(n);
+    }
+    return exitOnNull(p, n);
+}
+
+static void* coreRealReallocate(void* oldP, size_t n) {
+    if(n <= 0) {
+        free(oldP);
+        return nullptr;
+    }
+    void* p = realloc(oldP, n);
+    while(p == nullptr && outOfMemoryHandler != nullptr) {
+        outOfMemoryHandler(outOfMemoryData);
+        p = realloc(oldP, n);
+    }
+    return exitOnNull(p, n);
+}
+
+static void coreRealFree(void* p) {
+    free(p);
+}
+
+#ifdef CORE_CHECK_MEMORY
+static const u8 CANARY[16] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
+                              0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF};
+
+struct CoreMemoryInfo;
+typedef struct CoreMemoryInfo CoreMemoryInfo;
+struct CoreMemoryInfo {
+    CoreMemoryInfo* next;
+    CoreMemoryInfo* previous;
+    size_t size;
+    int line;
+    char buffer[64 - 2 * sizeof(void*) - sizeof(size_t) - sizeof(int)];
+    char canary[sizeof(CANARY)];
+};
+static_assert(sizeof(CoreMemoryInfo) == 80, "memory info has invalid size");
+static CoreMemoryInfo* headMemoryInfo = nullptr;
+
+static void addMemoryInfo(CoreMemoryInfo* info, const char* file, int line,
+                          size_t n) {
+    info->next = nullptr;
+    info->previous = nullptr;
+    info->size = n;
+    info->line = line;
+    strncpy(info->buffer, coreGetShortFileName(file), sizeof(info->buffer));
+    memcpy(info->canary, CANARY, sizeof(CANARY));
+    memcpy((char*)info + n - sizeof(CANARY), CANARY, sizeof(CANARY));
+    if(headMemoryInfo == nullptr) {
+        headMemoryInfo = info;
+    } else {
+        headMemoryInfo->previous = info;
+        info->next = headMemoryInfo;
+        headMemoryInfo = info;
+    }
+}
+
+static void removeMemoryInfo(CoreMemoryInfo* info) {
+    if(info->previous == nullptr) {
+        if(info->next == nullptr) {
+            headMemoryInfo = nullptr;
+        } else {
+            headMemoryInfo = info->next;
+            info->next->previous = nullptr;
+        }
+    } else {
+        if(info->next == nullptr) {
+            info->previous->next = nullptr;
+        } else {
+            info->previous->next = info->next;
+            info->next->previous = info->previous;
+        }
+    }
+}
+
+void* coreDebugAllocate(const char* file, int line, size_t n) {
+    n += sizeof(CoreMemoryInfo) + sizeof(CANARY);
+    void* p = coreRealAllocate(n + sizeof(CANARY));
+    addMemoryInfo(p, file, line, n);
+    return (char*)p + sizeof(CoreMemoryInfo);
+}
+
+void* coreDebugReallocate(const char* file, int line, void* p, size_t n) {
+    if(n > 0) {
+        n += sizeof(CoreMemoryInfo) + sizeof(CANARY);
+    }
+    void* rp = (void*)p;
+    if(rp != nullptr) {
+        rp = (char*)rp - sizeof(CoreMemoryInfo);
+        removeMemoryInfo(rp);
+    }
+    void* np = coreRealReallocate(rp, n);
+    if(np == nullptr) {
+        return nullptr;
+    }
+    addMemoryInfo(np, file, line, n);
+    return (char*)np + sizeof(CoreMemoryInfo);
+}
+
+static bool checkCanary(void* p) {
+    return memcmp(p, CANARY, sizeof(CANARY)) != 0;
+}
+
+void coreFreeDebug(const char* file, int line, void* p) {
+    if(p == nullptr) {
+        return;
+    }
+    CoreMemoryInfo* rp = (CoreMemoryInfo*)((char*)p - sizeof(CoreMemoryInfo));
+    if(checkCanary(rp->canary)) {
+        file = coreGetShortFileName(file);
+        CORE_LOG_ERROR("Free at %s:%d violated pre canary", file, line);
+        CORE_EXIT(1);
+    } else if(checkCanary((char*)rp + rp->size - sizeof(CANARY))) {
+        file = coreGetShortFileName(file);
+        CORE_LOG_ERROR("Free at %s:%d violated post canary", file, line);
+        CORE_EXIT(1);
+    }
+    removeMemoryInfo(rp);
+    coreRealFree(rp);
+}
+
+void corePrintMemoryReport() {
+    for(CoreMemoryInfo* i = headMemoryInfo; i != nullptr; i = i->next) {
+        CORE_LOG_ERROR("%s:%d was not freed", i->buffer, i->line);
+    }
+}
+
+#else
+
+void* coreAllocate(size_t n) {
+    return coreRealAllocate(n);
+}
+
+void* coreReallocate(void* p, size_t n) {
+    return coreRealReallocate(p, n);
+}
+
+void coreFree(void* p) {
+    coreRealFree(p);
+}
+
+#endif

+ 25 - 5
tasks

@@ -133,14 +133,28 @@ fi
 if $install; then
     ninja -C build_release install
 fi
+
+function runTests() {
+    echo "--------------Invalid alloc exit----------------"
+    LLVM_PROFILE_FILE="alloc.profraw" $valgrind ./test alloc $valgrind || true
+    echo "--------------Invalid realloc exit--------------"
+    LLVM_PROFILE_FILE="realloc.profraw" $valgrind ./test realloc $valgrind || true
+    echo "--------------Pre canary detection--------------"
+    LLVM_PROFILE_FILE="pre_canary.profraw" $valgrind ./test pre_canary $valgrind || true
+    echo "--------------Post canary detection-------------"
+    LLVM_PROFILE_FILE="post_canary.profraw" $valgrind ./test post_canary $valgrind || true
+    echo "--------------Default run-----------------------"
+    LLVM_PROFILE_FILE="default.profraw" $valgrind ./test light $valgrind || true
+}
+
 if $test_debug; then
     cd build_debug
-    $valgrind ./test light $valgrind || true
+    runTests
     cd ..
 fi
 if $test_release; then
     cd build_release
-    $valgrind ./test light $valgrind || true
+    runTests
     cd ..
 fi
 if $performance; then
@@ -171,6 +185,12 @@ if $time; then
     printf "$output" | sort -n
 fi
 if $coverage; then
-    gcovr -r . build_debug -e test -e performance \
-        --exclude-lines-by-pattern ".*CoverageIgnore.*"
-fi
+    if [ $compiler = "gcc" ]; then
+        gcovr -r . build_debug -e test -e performance \
+            --exclude-lines-by-pattern ".*CoverageIgnore.*"
+    else
+        files=$(find build_debug -name *.profraw)
+        llvm-profdata-16 merge -sparse $files -o build_debug/default.profdata
+        llvm-cov-16 show ./build_debug/test -instr-profile=build_debug/default.profdata --ignore-filename-regex="test/" -line-coverage-lt=100
+    fi
+fi 

+ 23 - 39
test/Main.c

@@ -1,21 +1,18 @@
 #include <locale.h>
+#include <stdio.h>
 #include <string.h>
-// #include "../src/ErrorSimulator.hpp"
+
 #include "Test.h"
 #include "Tests.h"
 #include "core/utils/Logger.h"
-// #include "core/utils/ArrayString.hpp"
-// #include "core/utils/Utility.hpp"
+#include "core/utils/Utility.h"
 
-/*static void onExit(int code, void* data) {
-    unsigned int i = *static_cast<unsigned int*>(data);
-    Core::ArrayString<1024> s;
-    s.append("Hello from exit #: #");
-    s.format(code, i);
-    s.printLine();
-    Core::print('A');
-    Core::Test::finalize();
-}*/
+static void onExit(int code, void* data) {
+    unsigned int i = *(unsigned int*)(data);
+    CORE_LOG_WARNING("Hello from exit %d: %u", code, i);
+    coreFinalizeTests();
+    corePrintMemoryReport();
+}
 
 int main(int argAmount, const char** args) {
     setlocale(LC_ALL, "en_US.utf8");
@@ -23,30 +20,17 @@ int main(int argAmount, const char** args) {
     for(int i = 0; i < argAmount; i++) {
         if(strcmp(args[i], "light") == 0) {
             light = true;
+        } else if(strcmp(args[i], "alloc") == 0) {
+            coreTestInvalidAllocate();
+        } else if(strcmp(args[i], "realloc") == 0) {
+            coreTestInvalidReallocate();
+        } else if(strcmp(args[i], "pre_canary") == 0) {
+            coreTestPreCanary();
+        } else if(strcmp(args[i], "post_canary") == 0) {
+            coreTestPostCanary();
         }
     }
     (void)light;
-
-    CORE_TEST_INT(20, 30);
-    CORE_TEST_INT(20, 20);
-    CORE_TEST_INT(20, 40);
-    CORE_TEST_INT(20, 50);
-    CORE_TEST_BOOL(true, false);
-    CORE_TEST_TRUE(true);
-    CORE_TEST_FALSE(false);
-    CORE_TEST_TRUE(false);
-    CORE_TEST_NULL(nullptr);
-    CORE_TEST_NOT_NULL(nullptr);
-    CORE_TEST_FLOAT(0, 3, 1);
-    CORE_TEST_STRING("wusi", "GUSI");
-    CORE_TEST_STRING("wusi", "wusi");
-    coreFinalizeTests();
-
-    CORE_LOG_DEBUG("This is a test %d", 6);
-    CORE_LOG_INFO("This is a test");
-    CORE_LOG_WARNING("This is a test");
-    CORE_LOG_ERROR("This is a test");
-
     /*
     Core::testArrayList(light);
     Core::testArrayString();
@@ -75,18 +59,18 @@ int main(int argAmount, const char** args) {
     Core::testRingBuffer();
     Core::testStack(light);
     Core::testThread();
-    Core::testUniquePointer();
-    Core::testUtility();
-    Core::testVector();
+    Core::testUniquePointer();*/
+    coreTestUtility();
+    /*Core::testVector();
     Core::testView();*/
 
     coreLogLevel = CORE_LOG_WARNING;
     CORE_LOG_DEBUG("You won't see this!");
     coreLogLevel = CORE_LOG_DEBUG;
 
-    // unsigned int data = 123456789;
-    // Core::setExitHandler(onExit, &data);
+    unsigned int data = 123456789;
+    coreSetExitHandler(onExit, &data);
 
-    // CORE_EXIT(1);
+    CORE_EXIT(1);
     return 0;
 }

+ 1 - 0
test/Test.c

@@ -82,6 +82,7 @@ static bool addToResult(const char* file, bool comparison) {
     }
 
 CORE_TEST_NAMED_COMPARE(Int, int, "%d")
+CORE_TEST_NAMED_COMPARE(U64, u64, "%lu")
 CORE_TEST_NAMED_COMPARE(Bool, bool, "%d")
 
 bool coreTestString(CORE_TEST_ARGS, const char* wanted, const char* actual) {

+ 10 - 8
test/Test.h

@@ -1,6 +1,8 @@
 #ifndef CORE_TEST_HPP
 #define CORE_TEST_HPP
 
+#include "core/utils/Types.h"
+
 void coreFinalizeTests(void);
 
 #define CORE_TEST_ARGS const char *file, int line
@@ -8,6 +10,7 @@ void coreFinalizeTests(void);
     bool coreTest##name(CORE_TEST_ARGS, type wanted, type actual)
 
 CORE_TEST_FUNCTION(Int, int);
+CORE_TEST_FUNCTION(U64, u64);
 CORE_TEST_FUNCTION(Bool, bool);
 CORE_TEST_FUNCTION(String, const char*);
 
@@ -16,19 +19,18 @@ bool coreTestFloat(CORE_TEST_ARGS, float wanted, float actual, float error);
 bool coreTestNull(CORE_TEST_ARGS, const void* p);
 bool coreTestNotNull(CORE_TEST_ARGS, const void* p);
 
-#define CORE_TEST(wanted, actual, name, type)                                  \
+#define CORE_TEST(wanted, actual, name)                                        \
     coreTest##name(__FILE__, __LINE__, wanted, actual)
 
 #define CORE_TEST_FLOAT(wanted, actual, error)                                 \
     coreTestFloat(__FILE__, __LINE__, wanted, actual, error)
 
-#define CORE_TEST_BOOL(wanted, actual) CORE_TEST(wanted, actual, Bool, bool)
-#define CORE_TEST_INT(wanted, actual) CORE_TEST(wanted, actual, Int, int)
-#define CORE_TEST_STRING(wanted, actual)                                       \
-    CORE_TEST(wanted, actual, String, string)
-
-#define CORE_TEST_FALSE(actual) CORE_TEST(false, actual, Bool, bool)
-#define CORE_TEST_TRUE(actual) CORE_TEST(true, actual, Bool, bool)
+#define CORE_TEST_BOOL(wanted, actual) CORE_TEST(wanted, actual, Bool)
+#define CORE_TEST_INT(wanted, actual) CORE_TEST(wanted, actual, Int)
+#define CORE_TEST_U64(wanted, actual) CORE_TEST(wanted, actual, U64)
+#define CORE_TEST_STRING(wanted, actual) CORE_TEST(wanted, actual, String)
+#define CORE_TEST_FALSE(actual) CORE_TEST(false, actual, Bool)
+#define CORE_TEST_TRUE(actual) CORE_TEST(true, actual, Bool)
 #define CORE_TEST_NULL(actual) coreTestNull(__FILE__, __LINE__, actual)
 #define CORE_TEST_NOT_NULL(actual) coreTestNotNull(__FILE__, __LINE__, actual)
 

+ 6 - 0
test/Tests.h

@@ -1,6 +1,8 @@
 #ifndef CORE_TESTS_HPP
 #define CORE_TESTS_HPP
 
+#include "Test.h"
+
 void coreTestArrayList(bool light);
 void coreTestArrayString(void);
 void coreTestArray(void);
@@ -30,6 +32,10 @@ void coreTestStack(bool light);
 void coreTestThread(void);
 void coreTestUniquePointer(void);
 void coreTestUtility(void);
+void coreTestInvalidAllocate(void);
+void coreTestInvalidReallocate(void);
+void coreTestPreCanary(void);
+void coreTestPostCanary(void);
 void coreTestVector(void);
 void coreTestView(void);
 

+ 83 - 0
test/modules/UtilityTests.c

@@ -0,0 +1,83 @@
+#include "../../src/ErrorSimulator.h"
+#include "../Tests.h"
+#include "core/utils/Utility.h"
+
+static void testPopCount() {
+    CORE_TEST_U64(4, corePopCount(0xF));
+    CORE_TEST_U64(0, corePopCount(0x0));
+    CORE_TEST_U64(2, corePopCount(0x6));
+    CORE_TEST_U64(7, corePopCount(0x7F));
+    CORE_TEST_U64(3, corePopCount(0x2A));
+    CORE_TEST_U64(32, corePopCount(0xFFFFFFFF));
+    CORE_TEST_U64(64, corePopCount(0xFFFFFFFFFFFFFFFF));
+    CORE_TEST_U64(44, corePopCount(0xFFFF0FFFFFFF));
+}
+
+static void testZeroRellocate() {
+    void* buffer = coreReallocate(nullptr, 16);
+    CORE_TEST_NOT_NULL(buffer);
+    buffer = coreReallocate(buffer, 0);
+    CORE_TEST_NULL(buffer);
+}
+
+static void testMemoryInfoList() {
+    void* a = coreAllocate(8);
+    void* b = coreAllocate(8);
+    void* c = coreAllocate(8);
+    void* d = coreAllocate(8);
+    coreFree(b); // remove middle element
+    coreFree(a); // remove first
+    coreFree(d); // remove last
+    coreFree(c); // remove single
+    coreFree(nullptr);
+}
+
+void coreTestUtility() {
+    testPopCount();
+    testZeroRellocate();
+    testMemoryInfoList();
+}
+
+static void outOfMemory(void*) {
+    coreSetOutOfMemoryHandler(nullptr, nullptr);
+}
+
+void coreTestInvalidAllocate(void) {
+    coreSetOutOfMemoryHandler(outOfMemory, nullptr);
+    coreAllocate(0xFFFFFFFFFFF);
+    CORE_TEST_TRUE(false);
+    coreFinalizeTests();
+    CORE_EXIT(0);
+}
+
+void coreTestInvalidReallocate(void) {
+    coreSetOutOfMemoryHandler(outOfMemory, nullptr);
+    void* p = coreAllocate(0xFF);
+    corePrintMemoryReport();
+    coreReallocate(p, 0xFFFFFFFFFFF);
+    CORE_TEST_TRUE(false);
+    coreFinalizeTests();
+    CORE_EXIT(0);
+}
+
+void coreTestPreCanary(void) {
+#ifdef CORE_CHECK_MEMORY
+    char* p = coreAllocate(16);
+    p[-1] = 0;
+    coreFree(p);
+    CORE_TEST_TRUE(false);
+#endif
+    coreFinalizeTests();
+    CORE_EXIT(0);
+}
+
+void coreTestPostCanary(void) {
+#ifdef CORE_CHECK_MEMORY
+    char* p = coreAllocate(16);
+    p[17] = 0;
+    coreFree(p);
+    CORE_TEST_TRUE(false);
+#endif
+    coreFinalizeTests();
+    CORE_EXIT(0);
+}