Quellcode durchsuchen

Clang coverage, smart allocator

Kajetan Johannes Hammerle vor 10 Monaten
Ursprung
Commit
eb948e59e7
13 geänderte Dateien mit 468 neuen und 66 gelöschten Zeilen
  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);
+}