4 Commits 6b5bd6968c ... 05281845c0

Author SHA1 Message Date
  Kajetan Johannes Hammerle 05281845c0 Various stuff 2 months ago
  Kajetan Johannes Hammerle 62fb3363c9 Test invalid index 2 months ago
  Kajetan Johannes Hammerle 95513339fd Correct performance log 2 months ago
  Kajetan Johannes Hammerle 6eff64c7ae Allow setting build profile, fix release build errors 2 months ago

+ 2 - 1
.gitignore

@@ -1,3 +1,4 @@
 .vscode
 build
-install
+install
+profile

+ 1 - 0
CMakeLists.txt

@@ -21,6 +21,7 @@ set(SRC
     "src/Frustum.cpp"
     "src/View.cpp"
     "src/Thread.cpp"
+    "src/Mutex.cpp"
     "src/FileReader.cpp"
     "src/ErrorSimulator.cpp"
 )

+ 6 - 5
include/core/data/BitArray.hpp

@@ -35,12 +35,13 @@ namespace Core {
         template<typename String>
         check_return Error toString(String& s) const {
             CORE_RETURN_ERROR(s.append("["));
-            for(int i = 0; i < length - 1; i++) {
-                CORE_RETURN_ERROR(s.append(get(i)));
-                CORE_RETURN_ERROR(s.append(", "));
-            }
             if(length > 0) {
-                CORE_RETURN_ERROR(s.append(get(length - 1)));
+                int end = length - 1;
+                for(int i = 0; i < end; i++) {
+                    CORE_RETURN_ERROR(s.append(get(i)));
+                    CORE_RETURN_ERROR(s.append(", "));
+                }
+                CORE_RETURN_ERROR(s.append(get(end)));
             }
             return s.append("]");
         }

+ 11 - 7
include/core/data/List.hpp

@@ -68,8 +68,8 @@ namespace Core {
             List copy;
             CORE_RETURN_ERROR(allocate(copy.data, n));
             copy.capacity = n;
-            for(int i = 0; i < length; i++) {
-                copy.unsafeAdd(Core::move(data[i]));
+            for(T& t : *this) {
+                copy.unsafeAdd(Core::move(t));
             }
             swap(copy);
             return Error::NONE;
@@ -92,7 +92,7 @@ namespace Core {
         check_return Error resize(int n, const T& t) {
             if(length < n) {
                 CORE_RETURN_ERROR(reserve(n));
-                for(int i = length; i < n; i++) {
+                for(int i = n - length; i != 0; i--) {
                     unsafeAdd(t);
                 }
             } else if(length > n) {
@@ -107,7 +107,7 @@ namespace Core {
         check_return Error resize(int n) {
             if(length < n) {
                 CORE_RETURN_ERROR(reserve(n));
-                for(int i = length; i < n; i++) {
+                for(int i = n - length; i != 0; i--) {
                     unsafeAdd(T());
                 }
             } else if(length > n) {
@@ -172,10 +172,14 @@ namespace Core {
                 return Error::INVALID_INDEX;
             }
             length--;
-            for(int i = index; i < length; i++) {
-                data[i] = Core::move(data[i + 1]);
+            T* currentT = begin() + index;
+            T* endT = end();
+            while(currentT != endT) {
+                T* nextT = currentT + 1;
+                *currentT = Core::move(*nextT);
+                currentT = nextT;
             }
-            data[length].~T();
+            endT->~T();
             return Error::NONE;
         }
 

+ 8 - 6
include/core/data/RingBuffer.hpp

@@ -103,12 +103,14 @@ namespace Core {
         template<typename String>
         check_return Error toString(String& s) const {
             CORE_RETURN_ERROR(s.append("["));
-            for(int i = 0; i < getLength() - 1; i++) {
-                CORE_RETURN_ERROR(s.append((*this)[i]));
-                CORE_RETURN_ERROR(s.append(", "));
-            }
-            if(getLength() > 0) {
-                CORE_RETURN_ERROR(s.append((*this)[getLength() - 1]));
+            int end = getLength();
+            if(end > 0) {
+                end--;
+                for(int i = 0; i < end; i++) {
+                    CORE_RETURN_ERROR(s.append((*this)[i]));
+                    CORE_RETURN_ERROR(s.append(", "));
+                }
+                CORE_RETURN_ERROR(s.append((*this)[end]));
             }
             return s.append("]");
         }

+ 5 - 1
include/core/data/Stack.hpp

@@ -21,7 +21,11 @@ namespace Core {
             }
 
             check_return Error pop() {
-                return data.removeBySwap(data.getLength() - 1);
+                int index = data.getLength();
+                if(index <= 0) {
+                    return Error::INVALID_STATE;
+                }
+                return data.removeBySwap(index - 1);
             }
 
             bool isEmpty() const {

+ 27 - 0
include/core/thread/Mutex.hpp

@@ -0,0 +1,27 @@
+#ifndef CORE_MUTEX_HPP
+#define CORE_MUTEX_HPP
+
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Error.hpp"
+
+namespace Core {
+    class Mutex final {
+        AlignedData<40, 8> mutex;
+
+    public:
+        Mutex();
+        Mutex(const Mutex& other) = delete;
+        Mutex(Mutex&& other) = delete;
+        ~Mutex();
+        Mutex& operator=(const Mutex& other) = delete;
+        Mutex& operator=(Mutex&& other) = delete;
+
+        check_return Error init();
+
+        check_return Error lock();
+        check_return Error unlock();
+    };
+}
+
+#endif

+ 2 - 2
include/core/utils/AlignedData.hpp

@@ -17,12 +17,12 @@ namespace Core {
 
     public:
         template<typename T>
-        T* as() {
+        constexpr T* as() {
             return reinterpret_cast<T*>(this);
         }
 
         template<typename T>
-        const T* as() const {
+        constexpr const T* as() const {
             return reinterpret_cast<T*>(this);
         }
 

+ 1 - 1
include/core/utils/ArrayString.hpp

@@ -261,7 +261,7 @@ namespace Core {
         template<int L>
         bool startsWidth(const ArrayString<L, CharType>& other,
                          int from = 0) const {
-            if(from + other.getLength() > length) {
+            if(from > length - other.getLength()) {
                 return false;
             }
             for(int i = 0; i < other.getLength(); i++) {

+ 1 - 1
include/core/utils/Clock.hpp

@@ -21,8 +21,8 @@ namespace Core {
         // the first invocation will always return 0 nanos
         check_return Error update(Nanos& n);
         float getUpdatesPerSecond() const;
-        check_return Error wait(Nanos nanos) const;
 
+        check_return static Error wait(Nanos nanos);
         check_return static Error getNanos(Nanos& n);
     };
 }

+ 1 - 0
include/core/utils/Error.hpp

@@ -16,6 +16,7 @@ namespace Core {
         TIME_NOT_AVAILABLE,
         SLEEP_INTERRUPTED,
         THREAD_ERROR,
+        MUTEX_ERROR,
         EXISTING_KEY,
         CANNOT_OPEN_FILE,
         END_OF_FILE

+ 1 - 1
include/core/utils/New.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_NEW_HPP
 #define CORE_NEW_HPP
 
-#include <stddef.h>
+using size_t = decltype(sizeof(0));
 
 void* operator new(size_t bytes) noexcept;
 void* operator new[](size_t bytes) noexcept;

+ 1 - 1
performance/Main.cpp

@@ -90,7 +90,7 @@ static void chaos(int n) {
     Core::ProbingHashMap<int, int> m2;
     fillChaos(m);
     fillChaos(m2);
-    CORE_LOG_INFO("Order Chaining | Order Probing");
+    CORE_LOG_INFO("Chaos Chaining | Chaos Probing");
     CORE_LOG_INFO("Search | # ms | # ms", average(m, testSearch, n),
                   average(m2, testSearch, n));
     CORE_LOG_INFO("EmptySearch | # ms | # ms", average(m, testEmptySearch, n),

+ 4 - 3
src/BitArray.cpp

@@ -1,6 +1,7 @@
 #include "core/data/BitArray.hpp"
 
 #include "core/math/Math.hpp"
+#include "core/utils/New.hpp"
 
 static int roundUpDivide(int a, int b) {
     if(a % b == 0) {
@@ -14,7 +15,7 @@ static constexpr int DIVIDE_BITS = Core::Math::roundUpLog2(INT_BITS);
 
 static int readBits(const int* data, int index, int bits) {
     int dataIndexA = (index * bits) >> DIVIDE_BITS;
-    int dataIndexB = ((index + 1) * bits) >> DIVIDE_BITS;
+    int dataIndexB = ((index * bits) + (bits - 1)) >> DIVIDE_BITS;
     int shifts = (index * bits) & (INT_BITS - 1);
     if(dataIndexA == dataIndexB) {
         return (data[dataIndexA] >> shifts) & ((1 << bits) - 1);
@@ -29,7 +30,7 @@ static void setBits(int* data, int index, int bits, int value) {
     int mask = (1 << bits) - 1;
     value &= mask;
     int dataIndexA = (index * bits) >> DIVIDE_BITS;
-    int dataIndexB = ((index + 1) * bits) >> DIVIDE_BITS;
+    int dataIndexB = ((index * bits) + (bits - 1)) >> DIVIDE_BITS;
     int shifts = (index * bits) & (INT_BITS - 1);
     data[dataIndexA] &= ~(mask << shifts);
     data[dataIndexA] |= (value << shifts);
@@ -41,7 +42,7 @@ static void setBits(int* data, int index, int bits, int value) {
 }
 
 static int getArrayLength(int length, int bits) {
-    return roundUpDivide(length * bits, sizeof(int) * 8);
+    return roundUpDivide(length * bits, INT_BITS);
 }
 
 Core::BitArray::BitArray() : length(0), bits(0), data(nullptr) {

+ 1 - 1
src/Clock.cpp

@@ -39,7 +39,7 @@ Core::Error Core::Clock::getNanos(Clock::Nanos& n) {
     return Error::NONE;
 }
 
-Core::Error Core::Clock::wait(Nanos nanos) const {
+Core::Error Core::Clock::wait(Nanos nanos) {
     timespec t;
     t.tv_nsec = nanos % 1'000'000'000;
     t.tv_sec = nanos / 1'000'000'000;

+ 20 - 16
src/Error.cpp

@@ -1,23 +1,27 @@
 #include "core/utils/Error.hpp"
 
+#define CASE_RETURN_ENUM_NAME(type)                                            \
+    case Error::type: return #type
+
 const char* Core::getErrorName(Error e) {
     switch(e) {
-        case Error::NONE: return "NONE";
-        case Error::NEGATIVE_ARGUMENT: return "NEGATIVE_ARGUMENT";
-        case Error::CAPACITY_REACHED: return "CAPACITY_REACHED";
-        case Error::BLOCKED_STDOUT: return "BLOCKED_STDOUT";
-        case Error::OUT_OF_MEMORY: return "OUT_OF_MEMORY";
-        case Error::INVALID_CHAR: return "INVALID_CHAR";
-        case Error::NOT_FOUND: return "NOT_FOUND";
-        case Error::INVALID_STATE: return "INVALID_STATE";
-        case Error::INVALID_INDEX: return "INVALID_INDEX";
-        case Error::INVALID_ARGUMENT: return "INVALID_ARGUMENT";
-        case Error::TIME_NOT_AVAILABLE: return "TIME_NOT_AVAILABLE";
-        case Error::SLEEP_INTERRUPTED: return "SLEEP_INTERRUPTED";
-        case Error::THREAD_ERROR: return "THREAD_ERROR";
-        case Error::EXISTING_KEY: return "EXISTING_KEY";
-        case Error::CANNOT_OPEN_FILE: return "CANNOT_OPEN_FILE";
-        case Error::END_OF_FILE: return "END_OF_FILE";
+        CASE_RETURN_ENUM_NAME(NONE);
+        CASE_RETURN_ENUM_NAME(NEGATIVE_ARGUMENT);
+        CASE_RETURN_ENUM_NAME(CAPACITY_REACHED);
+        CASE_RETURN_ENUM_NAME(BLOCKED_STDOUT);
+        CASE_RETURN_ENUM_NAME(OUT_OF_MEMORY);
+        CASE_RETURN_ENUM_NAME(INVALID_CHAR);
+        CASE_RETURN_ENUM_NAME(NOT_FOUND);
+        CASE_RETURN_ENUM_NAME(INVALID_STATE);
+        CASE_RETURN_ENUM_NAME(INVALID_INDEX);
+        CASE_RETURN_ENUM_NAME(INVALID_ARGUMENT);
+        CASE_RETURN_ENUM_NAME(TIME_NOT_AVAILABLE);
+        CASE_RETURN_ENUM_NAME(SLEEP_INTERRUPTED);
+        CASE_RETURN_ENUM_NAME(THREAD_ERROR);
+        CASE_RETURN_ENUM_NAME(MUTEX_ERROR);
+        CASE_RETURN_ENUM_NAME(EXISTING_KEY);
+        CASE_RETURN_ENUM_NAME(CANNOT_OPEN_FILE);
+        CASE_RETURN_ENUM_NAME(END_OF_FILE);
     }
     return "?";
 }

+ 9 - 1
src/ErrorSimulator.hpp

@@ -1,14 +1,22 @@
 #ifndef CORE_ERROR_SIMULATOR_HPP
 #define CORE_ERROR_SIMULATOR_HPP
 
+#include "core/utils/Utility.hpp"
+
 #ifdef ERROR_SIMULATOR
 namespace Core::Fail {
     extern bool realloc;
     extern bool fileClose;
     extern bool timeGet;
     extern int leftAllocations;
+
+    inline bool freeAndReturn(void* p) {
+        Core::free(p);
+        return true;
+    }
 }
-#define CORE_REALLOC_FAIL Core::Fail::realloc
+#define CORE_REALLOC_FAIL(pointer)                                             \
+    (Core::Fail::realloc && Core::Fail::freeAndReturn(pointer))
 #define CORE_FILE_CLOSE_FAIL Core::Fail::fileClose
 #define CORE_TIME_GET_FAIL Core::Fail::timeGet
 #else

+ 45 - 0
src/Mutex.cpp

@@ -0,0 +1,45 @@
+#include "core/thread/Mutex.hpp"
+
+#include <threads.h>
+
+#include "core/utils/Meta.hpp"
+#include "core/utils/Utility.hpp"
+
+static void reset(mtx_t* m) {
+    Core::memorySet(m, 0, sizeof(mtx_t));
+}
+
+Core::Mutex::Mutex() : mutex() {
+    CORE_ASSERT_ALIGNED_DATA(mutex, mtx_t);
+    reset(mutex.as<mtx_t>());
+}
+
+static bool doesExist(mtx_t* t) {
+    mtx_t zero{};
+    return !Core::memoryCompare(&zero, t, sizeof(mtx_t));
+}
+
+Core::Mutex::~Mutex() {
+    if(doesExist(mutex.as<mtx_t>())) {
+        mtx_destroy(mutex.as<mtx_t>());
+    }
+}
+
+check_return Core::Error Core::Mutex::init() {
+    if(doesExist(mutex.as<mtx_t>())) {
+        return Error::INVALID_STATE;
+    }
+    return mtx_init(mutex.as<mtx_t>(), mtx_plain) != thrd_success
+               ? Error::MUTEX_ERROR
+               : Error::NONE;
+}
+
+check_return Core::Error Core::Mutex::lock() {
+    return mtx_lock(mutex.as<mtx_t>()) != thrd_success ? Error::MUTEX_ERROR
+                                                       : Error::NONE;
+}
+
+check_return Core::Error Core::Mutex::unlock() {
+    return mtx_unlock(mutex.as<mtx_t>()) != thrd_success ? Error::MUTEX_ERROR
+                                                         : Error::NONE;
+}

+ 6 - 5
src/Thread.cpp

@@ -5,7 +5,13 @@
 #include "core/utils/Meta.hpp"
 #include "core/utils/Utility.hpp"
 
+static void reset(thrd_t* t) {
+    Core::memorySet(t, 0, sizeof(thrd_t));
+}
+
 Core::Thread::Thread() : thread() {
+    CORE_ASSERT_ALIGNED_DATA(thread, thrd_t);
+    reset(thread.as<thrd_t>());
 }
 
 Core::Thread::Thread(Thread&& other) : thread() {
@@ -17,10 +23,6 @@ static bool doesExist(thrd_t* t) {
     return !Core::memoryCompare(&zero, t, sizeof(thrd_t));
 }
 
-static void reset(thrd_t* t) {
-    Core::memorySet(t, 0, sizeof(thrd_t));
-}
-
 Core::Thread::~Thread() {
     if(doesExist(thread.as<thrd_t>())) {
         (void)join(nullptr);
@@ -39,7 +41,6 @@ Core::Thread& Core::Thread::operator=(Thread&& other) {
 }
 
 check_return Core::Error Core::Thread::start(Function f, void* p) {
-    CORE_ASSERT_ALIGNED_DATA(thread, thrd_t);
     return thrd_create(thread.as<thrd_t>(), f, p) != thrd_success
                ? Error::THREAD_ERROR
                : Error::NONE;

+ 1 - 1
src/Utility.cpp

@@ -77,7 +77,7 @@ Core::Error Core::reallocate(char*& p, int n) {
         return Error::NONE;
     }
     char* newP = static_cast<char*>(realloc(p, static_cast<unsigned int>(n)));
-    if(newP == nullptr || CORE_REALLOC_FAIL) {
+    if(newP == nullptr || CORE_REALLOC_FAIL(newP)) {
         return Error::OUT_OF_MEMORY;
     }
     p = newP;

+ 23 - 7
tasks

@@ -8,11 +8,12 @@ printHelpExit() {
     echo "$0 build       | build everything"
     echo "$0 install     | move build results into the install folder"
     echo "$0 test        | run the tests"
+    echo "$0 valgrind    | run the tests with valgrind"
     echo "$0 coverage    | generate code coverage"
     echo "$0 performance | run the performance tests"
     echo "$0 final       | find classes / structs which are not final"
     echo "$0 macro       | find macros without CORE" 
-    echo "$0 include     | find system includes in header files" 
+    echo "$0 include     | find system includes" 
     echo "$0 time        | check build time"
     exit 0
 }
@@ -27,10 +28,12 @@ fi
 # tasks
 build=false
 test=false
+valgrind=false
 performance=false
 time=false
 install=false
 coverage=false
+profile=$(cat profile)
 
 # parsing
 if [ "$task" = "clean" ]; then
@@ -48,6 +51,9 @@ elif [ "$task" = "coverage" ]; then
 elif [ "$task" = "test" ]; then
     build=true
     test=true
+elif [ "$task" = "valgrind" ]; then
+    build=true
+    valgrind=true
 elif [ "$task" = "performance" ]; then
     build=true
     performance=true
@@ -55,14 +61,18 @@ elif [ "$task" = "time" ]; then
     build=true
     time=true
 elif [ "$task" = "final" ]; then
-    grep -r " class" src | grep -v -E 'final|enum|.git' || true
-    grep -r " struct" src | grep -v -E 'final|enum|.git' || true
+    grep -r " class" src include | grep -v -E 'final|enum|.git' || true
+    grep -r " struct" src include | grep -v -E 'final|enum|.git' || true
     exit 0
 elif [ "$task" = "macro" ]; then
-    grep -r "#define" src | grep -v " CORE" || true
+    grep -r "#define" src include | grep -v " CORE" || true
     exit 0
 elif [ "$task" = "include" ]; then
-    grep -r "#include <" src | grep "\.hpp" || true
+    echo "System includes in header files:"
+    grep -r "#include <" src include | grep "\.hpp" || true
+    echo "-------------------------------------------------" 
+    echo "System includes in source files:"
+    grep -r "#include <" src include | grep "\.cpp" || true
     exit 0
 else
     echo "unknown task"
@@ -72,7 +82,8 @@ fi
 # task execution
 if $build; then
     if [ ! -e build ]; then 
-        cmake -B build -S . -G Ninja -DCMAKE_INSTALL_PREFIX=./install
+        cmake -B build -S . -G Ninja -DCMAKE_INSTALL_PREFIX=./install \
+            -DCMAKE_BUILD_TYPE=$profile
     fi
     ninja -C build
 fi
@@ -81,7 +92,12 @@ if $install; then
 fi
 if $test; then
     cd build
-    ./test || true
+    ./test light || true
+    cd ..
+fi
+if $valgrind; then
+    cd build
+    valgrind ./test light valgrind || true
     cd ..
 fi
 if $performance; then

+ 17 - 7
test/Main.cpp

@@ -1,3 +1,5 @@
+#include <string.h>
+
 #include "../src/ErrorSimulator.hpp"
 #include "Test.hpp"
 #include "Tests.hpp"
@@ -13,30 +15,38 @@ static void onExit(int code, void* data) {
     Core::Test::finalize();
 }
 
-int main(int argAmount, const char**) {
-    bool light = argAmount <= 1;
+int main(int argAmount, const char** args) {
+    bool light = false;
+    bool outOfMemoryTest = true;
+    for(int i = 0; i < argAmount; i++) {
+        if(strcmp(args[i], "light") == 0) {
+            light = true;
+        } else if(strcmp(args[i], "valgrind") == 0) {
+            outOfMemoryTest = false;
+        }
+    }
     Core::testArrayList(light);
     Core::testArrayString();
     Core::testArray();
-    Core::testBitArray();
+    Core::testBitArray(outOfMemoryTest);
     Core::testBox();
     Core::testBuffer(light);
     Core::testBufferedValue();
     Core::testClock(light);
     Core::testColor();
-    Core::testComponents();
+    Core::testComponents(outOfMemoryTest);
     Core::testError();
     Core::testFileReader();
     Core::testFrustum();
-    Core::testHashMap(light);
-    Core::testLinkedList(light);
+    Core::testHashMap(light, outOfMemoryTest);
+    Core::testLinkedList(light, outOfMemoryTest);
     Core::testList(light);
     Core::testMath();
     Core::testMatrixStack(light);
     Core::testMatrix();
     Core::testNew();
     Core::testPlane();
-    Core::testProbingHashMap(light);
+    Core::testProbingHashMap(light, outOfMemoryTest);
     Core::testQuaternion();
     Core::testRandom(light);
     Core::testRingBuffer();

+ 5 - 5
test/Tests.hpp

@@ -7,25 +7,25 @@ namespace Core {
     void testArrayList(bool light);
     void testArrayString();
     void testArray();
-    void testBitArray();
+    void testBitArray(bool outOfMemoryTest);
     void testBox();
     void testBuffer(bool light);
     void testBufferedValue();
     void testClock(bool light);
     void testColor();
-    void testComponents();
+    void testComponents(bool outOfMemoryTest);
     void testError();
     void testFileReader();
     void testFrustum();
-    void testHashMap(bool light);
-    void testLinkedList(bool light);
+    void testHashMap(bool light, bool outOfMemoryTest);
+    void testLinkedList(bool light, bool outOfMemoryTest);
     void testList(bool light);
     void testMath();
     void testMatrixStack(bool light);
     void testMatrix();
     void testNew();
     void testPlane();
-    void testProbingHashMap(bool light);
+    void testProbingHashMap(bool light, bool outOfMemoryTest);
     void testQuaternion();
     void testRandom(bool light);
     void testRingBuffer();

+ 6 - 0
test/modules/ArrayListTests.cpp

@@ -136,6 +136,12 @@ static void testRemove() {
     CORE_TEST_EQUAL(2, list[0]);
     CORE_TEST_EQUAL(3, list[1]);
     CORE_TEST_EQUAL(2, list.getLength());
+    CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, list.removeBySwap(4));
+    CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, list.removeBySwap(-1));
+    CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, list.removeBySwap(-5));
+    CORE_TEST_EQUAL(2, list[0]);
+    CORE_TEST_EQUAL(3, list[1]);
+    CORE_TEST_EQUAL(2, list.getLength());
     CORE_TEST_ERROR(list.removeBySwap(1));
     CORE_TEST_EQUAL(2, list[0]);
     CORE_TEST_EQUAL(1, list.getLength());

+ 4 - 2
test/modules/BitArrayTests.cpp

@@ -149,7 +149,7 @@ static void testOutOfMemory() {
     Core::Fail::leftAllocations = -1;
 }
 
-void Core::testBitArray() {
+void Core::testBitArray(bool outOfMemoryTest) {
     testSetRead();
     testOutOfBoundsSetRead();
     testBigSetRead();
@@ -162,5 +162,7 @@ void Core::testBitArray() {
     testResizeExact();
     testMoveAssignment();
     testNegativeArgument();
-    testOutOfMemory();
+    if(outOfMemoryTest) {
+        testOutOfMemory();
+    }
 }

+ 2 - 2
test/modules/ClockTests.cpp

@@ -31,10 +31,10 @@ static void testWait(Core::Clock::Nanos wait) {
     Core::Clock c;
     Core::Clock::Nanos n = 0;
     CORE_TEST_ERROR(c.update(n));
-    CORE_TEST_ERROR(c.wait(wait));
+    CORE_TEST_ERROR(Core::Clock::wait(wait));
     Core::Clock::Nanos n2 = 0;
     CORE_TEST_ERROR(c.update(n2));
-    CORE_TEST_TRUE(n2 >= wait && n2 <= wait * 12 / 10);
+    CORE_TEST_TRUE(n2 >= wait && n2 <= wait * 13 / 10);
 }
 
 static void testFail() {

+ 5 - 3
test/modules/ComponentsTests.cpp

@@ -198,7 +198,7 @@ static void testOutOfMemory() {
         }
     }
     Core::Fail::leftAllocations = -1;
-    CORE_TEST_TRUE(memFails > 0);
+    CORE_TEST_TRUE(memFails != 0);
 }
 
 static void testConstSearch() {
@@ -213,11 +213,13 @@ static void testConstSearch() {
     CORE_TEST_NULL(cc.search(2));
 }
 
-void Core::testComponents() {
+void Core::testComponents(bool outOfMemoryTest) {
     testAddForEach();
     testAddConstForEach();
     testAddComponentForEach();
     testRemove();
-    testOutOfMemory();
+    if(outOfMemoryTest) {
+        testOutOfMemory();
+    }
     testConstSearch();
 }

+ 1 - 0
test/modules/ErrorTests.cpp

@@ -19,6 +19,7 @@ void Core::testError() {
     test(Error::TIME_NOT_AVAILABLE, "TIME_NOT_AVAILABLE");
     test(Error::SLEEP_INTERRUPTED, "SLEEP_INTERRUPTED");
     test(Error::THREAD_ERROR, "THREAD_ERROR");
+    test(Error::MUTEX_ERROR, "MUTEX_ERROR");
     test(Error::EXISTING_KEY, "EXISTING_KEY");
     test(Error::CANNOT_OPEN_FILE, "CANNOT_OPEN_FILE");
     test(Error::END_OF_FILE, "END_OF_FILE");

+ 7 - 7
test/modules/HashMapTests.cpp

@@ -9,8 +9,7 @@ static void testAdd() {
     IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     int* value = map.search(5);
-    CORE_TEST_NOT_NULL(value);
-    if(value != nullptr) {
+    if(CORE_TEST_NOT_NULL(value)) {
         CORE_TEST_EQUAL(4, *value);
     }
 }
@@ -47,8 +46,7 @@ static void testAddReplace() {
     CORE_TEST_ERROR(map.add(5, 10));
     CORE_TEST_TRUE(map.contains(5));
     int* a = map.search(5);
-    CORE_TEST_NOT_NULL(a);
-    if(a != nullptr) {
+    if(CORE_TEST_NOT_NULL(a)) {
         CORE_TEST_EQUAL(10, *a);
     }
 }
@@ -329,10 +327,10 @@ static void testOutOfMemory() {
         CORE_TEST_EQUAL(1, *found);
     }
     Core::Fail::leftAllocations = -1;
-    CORE_TEST_TRUE(memFails > 0);
+    CORE_TEST_TRUE(memFails != 0);
 }
 
-void Core::testHashMap(bool light) {
+void Core::testHashMap(bool light, bool outOfMemoryTest) {
     testAdd();
     testMultipleAdd();
     testSearch();
@@ -351,5 +349,7 @@ void Core::testHashMap(bool light) {
     testKeyForEach();
     testValueForEach();
     testTypes();
-    testOutOfMemory();
+    if(outOfMemoryTest) {
+        testOutOfMemory();
+    }
 }

+ 5 - 3
test/modules/LinkedListTests.cpp

@@ -288,10 +288,10 @@ static void testOutOfMemory() {
         CORE_TEST_EQUAL(1, *list.begin());
     }
     Core::Fail::leftAllocations = -1;
-    CORE_TEST_TRUE(memFails > 0);
+    CORE_TEST_TRUE(memFails != 0);
 }
 
-void Core::testLinkedList(bool light) {
+void Core::testLinkedList(bool light, bool outOfMemoryTest) {
     testWithoutCopyOrMove();
     testAdd();
     testMultipleAdd();
@@ -306,5 +306,7 @@ void Core::testLinkedList(bool light) {
     testRemove();
     testRemoveFirst();
     testRemoveLast();
-    testOutOfMemory();
+    if(outOfMemoryTest) {
+        testOutOfMemory();
+    }
 }

+ 1 - 0
test/modules/ListTests.cpp

@@ -157,6 +157,7 @@ static void testRemove() {
     CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, list.remove(2));
     CORE_TEST_EQUAL(3, list[0]);
     CORE_TEST_EQUAL(2, list[1]);
+    CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, list.remove(2));
     CORE_TEST_EQUAL(2, list.getLength());
     CORE_TEST_ERROR(list.remove(1));
     CORE_TEST_EQUAL(3, list[0]);

+ 5 - 3
test/modules/ProbingHashMapTests.cpp

@@ -328,7 +328,7 @@ static void testOutOfMemory() {
         CORE_TEST_EQUAL(1, *found);
     }
     Core::Fail::leftAllocations = -1;
-    CORE_TEST_TRUE(memFails > 0);
+    CORE_TEST_TRUE(memFails != 0);
 }
 
 static void testInsertInvalid() {
@@ -347,7 +347,7 @@ static void testAddCollisions() {
     }
 }
 
-void Core::testProbingHashMap(bool light) {
+void Core::testProbingHashMap(bool light, bool outOfMemoryTest) {
     testAdd();
     testMultipleAdd();
     testSearch();
@@ -365,7 +365,9 @@ void Core::testProbingHashMap(bool light) {
     testKeyForEach();
     testValueForEach();
     testTypes();
-    testOutOfMemory();
+    if(outOfMemoryTest) {
+        testOutOfMemory();
+    }
     testInsertInvalid();
     testAddCollisions();
 }

+ 1 - 1
test/modules/StackTests.cpp

@@ -60,7 +60,7 @@ template<typename T>
 static void testPop(int amount) {
     T stack;
     for(int i = 0; i < amount; i++) {
-        CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, stack.pop());
+        CORE_TEST_EQUAL(Core::Error::INVALID_STATE, stack.pop());
     }
 }
 

+ 31 - 0
test/modules/ThreadTests.cpp

@@ -1,4 +1,7 @@
+#include <threads.h>
+
 #include "../Tests.hpp"
+#include "core/thread/Mutex.hpp"
 #include "core/thread/Thread.hpp"
 
 static int runDone = 0;
@@ -75,6 +78,33 @@ static void testDoubleJoin() {
     CORE_TEST_EQUAL(Core::Error::THREAD_ERROR, t.join(nullptr));
 }
 
+struct MutexCounter {
+    Core::Mutex m{};
+    int counter = 0;
+};
+
+static int incrementMutexCounter(void* p) {
+    MutexCounter* mcp = static_cast<MutexCounter*>(p);
+    for(int i = 0; i < 10000; i++) {
+        (void)mcp->m.lock();
+        mcp->counter++;
+        (void)mcp->m.unlock();
+    }
+    return 0;
+}
+
+static void testMutex() {
+    MutexCounter mc;
+    CORE_TEST_ERROR(mc.m.init());
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE, mc.m.init());
+    Core::Thread t[2];
+    CORE_TEST_ERROR(t[0].start(incrementMutexCounter, &mc));
+    CORE_TEST_ERROR(t[1].start(incrementMutexCounter, &mc));
+    CORE_TEST_ERROR(t[0].join(nullptr));
+    CORE_TEST_ERROR(t[1].join(nullptr));
+    CORE_TEST_EQUAL(20000, mc.counter);
+}
+
 void Core::testThread() {
     testStart();
     testLambda();
@@ -84,4 +114,5 @@ void Core::testThread() {
     testMoveAssignment();
     testMoveIntoActive();
     testDoubleJoin();
+    testMutex();
 }