18 Commits fb6ac9b462 ... 6b5bd6968c

Author SHA1 Message Date
  Kajetan Johannes Hammerle 6b5bd6968c More tests for coverage 2 months ago
  Kajetan Johannes Hammerle 54f4ee0d09 More tests for code coverage 2 months ago
  Kajetan Johannes Hammerle 85c099b58f Explicit test template instantiation for coverage 2 months ago
  Kajetan Johannes Hammerle 69435fff82 Reset logger formatting correctly, shorten file name in tests 2 months ago
  Kajetan Johannes Hammerle 9d798ff19f Generate test coverage, light test mode 2 months ago
  Kajetan Johannes Hammerle 2bb6afd961 Public swaps 2 months ago
  Kajetan Johannes Hammerle 424bf8008e Thread auto join / move 2 months ago
  Kajetan Johannes Hammerle 6fa6bc34d7 Improve Array constexpr behavior 2 months ago
  Kajetan Johannes Hammerle d6cb5d5a77 Make src folder flat 2 months ago
  Kajetan Johannes Hammerle d38c945a2c Move performance 2 months ago
  Kajetan Johannes Hammerle bf8dd240d7 Shared test header 2 months ago
  Kajetan Johannes Hammerle 3715139e0f Move tests into own folder 2 months ago
  Kajetan Johannes Hammerle 44a697dbe8 Move public headers into include folder 2 months ago
  Kajetan Johannes Hammerle 51059cde93 Move CMakeLists 2 months ago
  Kajetan Johannes Hammerle 15f7d33510 Move src into folder 2 months ago
  Kajetan Johannes Hammerle 8dfbb9394b Rename macro 2 months ago
  Kajetan Johannes Hammerle ce18e12f82 New compiler warnings 2 months ago
  Kajetan Johannes Hammerle c8910020f1 Rename test classes to prevent clashes, use cmake 2 months ago
100 changed files with 1815 additions and 562 deletions
  1. 3 0
      .gitignore
  2. 226 0
      CMakeLists.txt
  3. 11 11
      include/core/data/Array.hpp
  4. 2 2
      include/core/data/ArrayList.hpp
  5. 1 2
      include/core/data/BitArray.hpp
  6. 4 4
      include/core/data/Components.hpp
  7. 5 5
      include/core/data/HashMap.hpp
  8. 1 2
      include/core/data/LinkedList.hpp
  9. 10 10
      include/core/data/List.hpp
  10. 25 24
      include/core/data/ProbingHashMap.hpp
  11. 2 2
      include/core/data/RingBuffer.hpp
  12. 3 3
      include/core/data/Stack.hpp
  13. 1 1
      include/core/io/File.hpp
  14. 1 3
      include/core/io/FileReader.hpp
  15. 2 2
      include/core/math/Box.hpp
  16. 1 1
      include/core/math/BufferedValue.hpp
  17. 3 3
      include/core/math/Frustum.hpp
  18. 1 1
      include/core/math/Math.hpp
  19. 2 2
      include/core/math/Matrix.hpp
  20. 3 3
      include/core/math/MatrixStack.hpp
  21. 2 2
      include/core/math/Plane.hpp
  22. 2 2
      include/core/math/Quaternion.hpp
  23. 8 4
      include/core/math/Vector.hpp
  24. 5 5
      include/core/math/View.hpp
  25. 29 0
      include/core/thread/Thread.hpp
  26. 2 2
      include/core/utils/AlignedData.hpp
  27. 6 7
      include/core/utils/ArrayString.hpp
  28. 2 3
      include/core/utils/Buffer.hpp
  29. 0 0
      include/core/utils/Check.hpp
  30. 4 6
      include/core/utils/Clock.hpp
  31. 0 0
      include/core/utils/Color.hpp
  32. 0 0
      include/core/utils/Error.hpp
  33. 1 1
      include/core/utils/HashCode.hpp
  34. 2 2
      include/core/utils/Logger.hpp
  35. 0 0
      include/core/utils/Meta.hpp
  36. 0 1
      include/core/utils/New.hpp
  37. 3 5
      include/core/utils/Random.hpp
  38. 0 0
      include/core/utils/Types.hpp
  39. 1 1
      include/core/utils/UniquePointer.hpp
  40. 2 2
      include/core/utils/Utility.hpp
  41. 0 119
      meson.build
  42. 6 6
      performance/Main.cpp
  43. 2 2
      src/BitArray.cpp
  44. 2 2
      src/Box.cpp
  45. 3 3
      src/Buffer.cpp
  46. 6 4
      src/Clock.cpp
  47. 1 1
      src/Error.cpp
  48. 10 0
      src/ErrorSimulator.cpp
  49. 20 0
      src/ErrorSimulator.hpp
  50. 5 4
      src/FileReader.cpp
  51. 3 2
      src/Frustum.cpp
  52. 1 1
      src/Logger.cpp
  53. 1 1
      src/Math.cpp
  54. 2 2
      src/Matrix.cpp
  55. 18 7
      src/New.cpp
  56. 2 2
      src/Plane.cpp
  57. 4 3
      src/Quaternion.cpp
  58. 12 7
      src/Random.cpp
  59. 59 0
      src/Thread.cpp
  60. 4 2
      src/Utility.cpp
  61. 1 1
      src/Vector.cpp
  62. 1 1
      src/View.cpp
  63. 115 0
      tasks
  64. 45 63
      test/Main.cpp
  65. 1 1
      test/Test.cpp
  66. 4 3
      test/Test.hpp
  67. 40 0
      test/Tests.hpp
  68. 22 8
      test/modules/ArrayListTests.cpp
  69. 121 5
      test/modules/ArrayStringTests.cpp
  70. 49 0
      test/modules/ArrayTests.cpp
  71. 51 5
      test/modules/BitArrayTests.cpp
  72. 3 5
      test/modules/BoxTests.cpp
  73. 8 9
      test/modules/BufferTests.cpp
  74. 17 12
      test/modules/BufferedValueTests.cpp
  75. 15 8
      test/modules/ClockTests.cpp
  76. 11 6
      test/modules/ColorTests.cpp
  77. 69 4
      test/modules/ComponentsTests.cpp
  78. 26 0
      test/modules/ErrorTests.cpp
  79. 52 6
      test/modules/FileReaderTests.cpp
  80. 13 5
      test/modules/FrustumTests.cpp
  81. 45 24
      test/modules/HashMapTests.cpp
  82. 29 10
      test/modules/LinkedListTests.cpp
  83. 39 9
      test/modules/ListTests.cpp
  84. 3 5
      test/modules/MathTests.cpp
  85. 24 8
      test/modules/MatrixStackTests.cpp
  86. 3 5
      test/modules/MatrixTests.cpp
  87. 26 0
      test/modules/NewTests.cpp
  88. 3 5
      test/modules/PlaneTests.cpp
  89. 70 27
      test/modules/ProbingHashMapTests.cpp
  90. 3 5
      test/modules/QuaternionTests.cpp
  91. 90 0
      test/modules/RandomTests.cpp
  92. 10 4
      test/modules/RingBufferTests.cpp
  93. 23 8
      test/modules/StackTests.cpp
  94. 87 0
      test/modules/ThreadTests.cpp
  95. 75 0
      test/modules/UniquePointerTests.cpp
  96. 54 0
      test/modules/UtilityTests.cpp
  97. 17 5
      test/modules/VectorTests.cpp
  98. 13 5
      test/modules/ViewTests.cpp
  99. 0 0
      test/modules/resources/test
  100. 0 8
      tests/ArrayListTests.hpp

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.vscode
+build
+install

+ 226 - 0
CMakeLists.txt

@@ -0,0 +1,226 @@
+cmake_minimum_required(VERSION 3.25)
+project(core)
+
+set(CMAKE_CXX_STANDARD 20)
+
+set(SRC
+    "src/Logger.cpp"
+    "src/Utility.cpp"
+    "src/Error.cpp"
+    "src/New.cpp"
+    "src/Buffer.cpp"
+    "src/Clock.cpp"
+    "src/Random.cpp"
+    "src/BitArray.cpp"
+    "src/Math.cpp"
+    "src/Vector.cpp"
+    "src/Quaternion.cpp"
+    "src/Matrix.cpp"
+    "src/Box.cpp"
+    "src/Plane.cpp"
+    "src/Frustum.cpp"
+    "src/View.cpp"
+    "src/Thread.cpp"
+    "src/FileReader.cpp"
+    "src/ErrorSimulator.cpp"
+)
+
+set(SRC_TESTS
+    "test/Main.cpp"
+    "test/Test.cpp"
+    "test/modules/ArrayTests.cpp"
+    "test/modules/ArrayStringTests.cpp"
+    "test/modules/UtilityTests.cpp"
+    "test/modules/ArrayListTests.cpp"
+    "test/modules/BitArrayTests.cpp"
+    "test/modules/MathTests.cpp"
+    "test/modules/ListTests.cpp"
+    "test/modules/LinkedListTests.cpp"
+    "test/modules/UniquePointerTests.cpp"
+    "test/modules/HashMapTests.cpp"
+    "test/modules/ProbingHashMapTests.cpp"
+    "test/modules/StackTests.cpp"
+    "test/modules/RingBufferTests.cpp"
+    "test/modules/ComponentsTests.cpp"
+    "test/modules/VectorTests.cpp"
+    "test/modules/QuaternionTests.cpp"
+    "test/modules/MatrixTests.cpp"
+    "test/modules/BoxTests.cpp"
+    "test/modules/BufferedValueTests.cpp"
+    "test/modules/PlaneTests.cpp"
+    "test/modules/FrustumTests.cpp"
+    "test/modules/ViewTests.cpp"
+    "test/modules/MatrixStackTests.cpp"
+    "test/modules/ColorTests.cpp"
+    "test/modules/BufferTests.cpp"
+    "test/modules/ClockTests.cpp"
+    "test/modules/RandomTests.cpp"
+    "test/modules/ThreadTests.cpp"
+    "test/modules/FileReaderTests.cpp"
+    "test/modules/ErrorTests.cpp"
+    "test/modules/NewTests.cpp"
+)
+
+set(SRC_PERFORMANCE
+    "performance/Main.cpp"
+    "test/Test.cpp"
+)
+
+add_library(core STATIC ${SRC})
+target_compile_options(core PUBLIC
+    --coverage
+    -fdiagnostics-color=always
+    -fno-exceptions
+    -fno-rtti
+    -fno-threadsafe-statics
+    -nostdinc++
+    -pedantic
+    -pedantic-errors
+    -Waligned-new=all
+    -Wall
+    -Walloca
+    -Walloc-zero
+    -Wanalyzer-too-complex
+    -Warith-conversion
+    -Warray-bounds=2
+    -Warray-parameter
+    -Wattribute-alias=2
+    -Wbidi-chars=any
+    -Wcast-align=strict
+    -Wcast-qual
+    -Wcatch-value=3
+    -Wcomma-subscript
+    -Wconditionally-supported
+    -Wconversion
+    -Wctad-maybe-unsupported
+    -Wctor-dtor-privacy
+    -Wdate-time
+    -Wdeprecated-copy-dtor
+    -Wdeprecated-enum-enum-conversion
+    -Wdeprecated-enum-float-conversion
+    -Wdisabled-optimization
+    -Wdouble-promotion
+    -Wduplicated-branches
+    -Wduplicated-cond
+    -Weffc++
+    -Wenum-compare
+    -Wenum-conversion
+    -Werror
+    -Wextra
+    -Wextra-semi
+    -Wfloat-equal
+    -Wformat=2
+    -Wformat-overflow=2
+    -Wformat-signedness
+    -Wformat-truncation=2
+    -Wframe-larger-than=8388608
+    -Wimplicit-fallthrough=5
+    -Winfinite-recursion
+    -Winit-self
+    -Winvalid-imported-macros
+    -Winvalid-pch
+    -Wlarger-than=1073741824
+    -Wlogical-op
+    -Wmismatched-tags
+    -Wmissing-braces
+    -Wmissing-declarations
+    -Wmissing-include-dirs
+    -Wmultichar
+    -Wmultiple-inheritance
+    -Wnoexcept
+    -Wnon-virtual-dtor
+    -Wnormalized=nfkc
+    -Wnull-dereference
+    -Wold-style-cast
+    -Woverlength-strings
+    -Woverloaded-virtual
+    -Wplacement-new=2
+    -Wredundant-decls
+    -Wredundant-tags
+    -Wregister
+    -Wshadow
+    -Wshift-overflow=2
+    -Wsign-conversion
+    -Wsign-promo
+    -Wstack-protector
+    -Wstack-usage=8388608
+    -Wstrict-null-sentinel
+    -Wstrict-overflow=5
+    -Wstringop-overflow=4
+    -Wsuggest-final-methods
+    -Wsuggest-final-types
+    -Wsuggest-override
+    -Wswitch-enum
+    -Wsynth
+    -Wtrampolines
+    -Wtrivial-auto-var-init
+    -Wundef
+    -Wunreachable-code
+    -Wunused-const-variable=2
+    -Wunused-macros
+    -Wuse-after-free=3
+    -Wvirtual-inheritance
+    -Wvla
+    -Wvolatile
+    -Wwrite-strings
+    -Wzero-as-null-pointer-constant
+)
+target_compile_definitions(core 
+    PUBLIC CORE_LOG_LEVEL=4
+    PRIVATE ERROR_SIMULATOR=true
+)
+target_link_libraries(core 
+    PUBLIC -nodefaultlibs c m
+    PRIVATE gcov
+)
+target_sources(core PUBLIC 
+    FILE_SET HEADERS
+    BASE_DIRS include
+    FILES 
+        ./include/core/data/Stack.hpp
+        ./include/core/data/HashMap.hpp
+        ./include/core/data/Components.hpp
+        ./include/core/data/ArrayList.hpp
+        ./include/core/data/ProbingHashMap.hpp
+        ./include/core/data/List.hpp
+        ./include/core/data/LinkedList.hpp
+        ./include/core/data/BitArray.hpp
+        ./include/core/data/Array.hpp
+        ./include/core/data/RingBuffer.hpp
+        ./include/core/thread/Thread.hpp
+        ./include/core/utils/HashCode.hpp
+        ./include/core/utils/New.hpp
+        ./include/core/utils/Check.hpp
+        ./include/core/utils/Buffer.hpp
+        ./include/core/utils/Random.hpp
+        ./include/core/utils/UniquePointer.hpp
+        ./include/core/utils/Types.hpp
+        ./include/core/utils/Color.hpp
+        ./include/core/utils/Logger.hpp
+        ./include/core/utils/ArrayString.hpp
+        ./include/core/utils/Utility.hpp
+        ./include/core/utils/Meta.hpp
+        ./include/core/utils/AlignedData.hpp
+        ./include/core/utils/Clock.hpp
+        ./include/core/utils/Error.hpp
+        ./include/core/math/Quaternion.hpp
+        ./include/core/math/Box.hpp
+        ./include/core/math/Frustum.hpp
+        ./include/core/math/Vector.hpp
+        ./include/core/math/Matrix.hpp
+        ./include/core/math/View.hpp
+        ./include/core/math/BufferedValue.hpp
+        ./include/core/math/Plane.hpp
+        ./include/core/math/MatrixStack.hpp
+        ./include/core/math/Math.hpp
+        ./include/core/io/File.hpp
+        ./include/core/io/FileReader.hpp
+)
+install(TARGETS core FILE_SET HEADERS)
+
+add_executable(test ${SRC_TESTS})
+target_link_libraries(test PRIVATE core)
+target_compile_definitions(test PRIVATE ERROR_SIMULATOR=true)
+
+add_executable(performance ${SRC_PERFORMANCE})
+target_link_libraries(performance PRIVATE core)

+ 11 - 11
data/Array.hpp → include/core/data/Array.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_ARRAY_HPP
 #define CORE_ARRAY_HPP
 
-#include "utils/ArrayString.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     template<typename T, int N>
@@ -10,43 +10,43 @@ namespace Core {
         T data[static_cast<unsigned int>(N)];
 
     public:
-        Array() = default;
+        constexpr Array() = default;
 
-        Array(const T& t) {
+        constexpr Array(const T& t) {
             fill(t);
         }
 
-        void fill(const T& t) {
+        constexpr void fill(const T& t) {
             for(int i = 0; i < N; i++) {
                 data[i] = t;
             }
         }
 
-        T& operator[](int index) {
+        constexpr T& operator[](int index) {
             return data[index];
         }
 
-        const T& operator[](int index) const {
+        constexpr const T& operator[](int index) const {
             return data[index];
         }
 
-        T* begin() {
+        constexpr T* begin() {
             return data;
         }
 
-        T* end() {
+        constexpr T* end() {
             return data + N;
         }
 
-        const T* begin() const {
+        constexpr const T* begin() const {
             return data;
         }
 
-        const T* end() const {
+        constexpr const T* end() const {
             return data + N;
         }
 
-        constexpr int getLength() const {
+        static consteval int getLength() {
             return N;
         }
 

+ 2 - 2
data/ArrayList.hpp → include/core/data/ArrayList.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_ARRAYLIST_HPP
 #define CORE_ARRAYLIST_HPP
 
-#include "utils/AlignedData.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     template<typename T, int N>

+ 1 - 2
data/BitArray.hpp → include/core/data/BitArray.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_BIT_ARRAY_HPP
 #define CORE_BIT_ARRAY_HPP
 
-#include "utils/ArrayString.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     class BitArray final {
@@ -45,7 +45,6 @@ namespace Core {
             return s.append("]");
         }
 
-    private:
         void swap(BitArray& other);
     };
 }

+ 4 - 4
data/Components.hpp → include/core/data/Components.hpp

@@ -1,16 +1,16 @@
 #ifndef CORE_COMPONENTS_HPP
 #define CORE_COMPONENTS_HPP
 
-#include "data/HashMap.hpp"
+#include "core/data/HashMap.hpp"
 
 namespace Core {
     using Entity = int;
 
     template<typename T>
     class Components final {
-        HashMap<Entity, int> entityToIndex;
-        List<Entity> indexToEntity;
-        List<T> components;
+        HashMap<Entity, int> entityToIndex{};
+        List<Entity> indexToEntity{};
+        List<T> components{};
 
     public:
         template<typename R>

+ 5 - 5
data/HashMap.hpp → include/core/data/HashMap.hpp

@@ -1,9 +1,9 @@
 #ifndef CORE_HPPASHMAP_HPP
 #define CORE_HPPASHMAP_HPP
 
-#include "data/LinkedList.hpp"
-#include "data/List.hpp"
-#include "utils/HashCode.hpp"
+#include "core/data/LinkedList.hpp"
+#include "core/data/List.hpp"
+#include "core/utils/HashCode.hpp"
 
 namespace Core {
     template<typename K, typename V>
@@ -113,8 +113,8 @@ namespace Core {
             IteratorAdapter<const HashMap, ConstKeyIterator>;
 
     private:
-        LinkedList<Node> nodes;
-        List<NodePointerList> nodePointers;
+        LinkedList<Node> nodes{};
+        List<NodePointerList> nodePointers{};
 
     public:
         check_return Error copyFrom(const HashMap& other) {

+ 1 - 2
data/LinkedList.hpp → include/core/data/LinkedList.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_LINKED_LIST_HPP
 #define CORE_LINKED_LIST_HPP
 
-#include "utils/ArrayString.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     template<typename T>
@@ -177,7 +177,6 @@ namespace Core {
             remove(n);
         }
 
-    private:
         void swap(LinkedList& other) {
             Core::swap(first, other.first);
             Core::swap(last, other.last);

+ 10 - 10
data/List.hpp → include/core/data/List.hpp

@@ -1,9 +1,9 @@
 #ifndef CORE_LIST_HPP
 #define CORE_LIST_HPP
 
-#include "utils/AlignedData.hpp"
-#include "utils/ArrayString.hpp"
-#include "utils/New.hpp"
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/ArrayString.hpp"
+#include "core/utils/New.hpp"
 
 namespace Core {
     template<typename T>
@@ -188,22 +188,22 @@ namespace Core {
             return Core::toString(s, *this);
         }
 
+        void swap(List& other) {
+            Core::swap(length, other.length);
+            Core::swap(capacity, other.capacity);
+            Core::swap(data, other.data);
+        }
+
     private:
         static Error allocate(T*& t, int n) {
             if(n <= 0) {
-                return Error::NEGATIVE_ARGUMENT;
+                return Error::NONE;
             }
             t = reinterpret_cast<T*>(
                 new AlignedType<T>[static_cast<size_t>(n)]);
             return t == nullptr ? Error::OUT_OF_MEMORY : Error::NONE;
         }
 
-        void swap(List& other) {
-            Core::swap(length, other.length);
-            Core::swap(capacity, other.capacity);
-            Core::swap(data, other.data);
-        }
-
         check_return Error ensureCapacity() {
             return length >= capacity
                        ? reserve(capacity + Core::Math::max(4, capacity / 4))

+ 25 - 24
data/ProbingHashMap.hpp → include/core/data/ProbingHashMap.hpp

@@ -1,11 +1,11 @@
 #ifndef CORE_PROBING_HPPASHMAP_HPP
 #define CORE_PROBING_HPPASHMAP_HPP
 
-#include "data/List.hpp"
-#include "utils/AlignedData.hpp"
-#include "utils/ArrayString.hpp"
-#include "utils/HashCode.hpp"
-#include "utils/Logger.hpp"
+#include "core/data/List.hpp"
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/ArrayString.hpp"
+#include "core/utils/HashCode.hpp"
+#include "core/utils/Logger.hpp"
 
 namespace Core {
     template<typename K, typename V>
@@ -125,7 +125,7 @@ namespace Core {
         int entries;
 
     public:
-        ProbingHashMap() : values(nullptr), entries(0) {
+        ProbingHashMap() : keys(), values(nullptr), entries(0) {
         }
 
         ProbingHashMap(const ProbingHashMap& other) = delete;
@@ -184,11 +184,9 @@ namespace Core {
             if(key == emptyValue<K>()) {
                 return Error::INVALID_ARGUMENT;
             }
-            CORE_RETURN_ERROR(rehash(entries * 2 + 1));
-            int index = searchSlot(key);
-            if(index < 0) {
-                return Error::CAPACITY_REACHED;
-            } else if(keys[index] == key) {
+            int index = 0;
+            CORE_RETURN_ERROR(searchSlot(index, key));
+            if(keys[index] == key) {
                 return Error::EXISTING_KEY;
             }
             keys[index] = key;
@@ -202,11 +200,8 @@ namespace Core {
             if(key == emptyValue<K>()) {
                 return Error::INVALID_ARGUMENT;
             }
-            CORE_RETURN_ERROR(rehash(entries * 2 + 1));
-            int index = searchSlot(key);
-            if(index < 0) {
-                return Error::CAPACITY_REACHED;
-            }
+            int index = 0;
+            CORE_RETURN_ERROR(searchSlot(index, key));
             if(keys[index] == key) {
                 values[index] = Core::forward<VA>(value);
             } else {
@@ -282,16 +277,22 @@ namespace Core {
         }
 
     private:
-        int searchSlot(const K& key) const {
-            int baseHash = static_cast<int>(hashCode(key) * 514685581u);
-            int end = keys.getLength() - 1;
-            for(int i = 0; i <= end; i++) {
-                int hash = (baseHash + i) & end;
-                if(keys[hash] == emptyValue<K>() || keys[hash] == key) {
-                    return hash;
+        check_return Error searchSlot(int& slot, const K& key) {
+            int rehashFactor = 2;
+            while(true) {
+                CORE_RETURN_ERROR(rehash(entries * rehashFactor + 1));
+                int baseHash = static_cast<int>(hashCode(key) * 514685581u);
+                int end = keys.getLength() - 1;
+                // rehash on bad clustering
+                for(int i = 0; i <= 5; i++) {
+                    int hash = (baseHash + i) & end;
+                    if(keys[hash] == emptyValue<K>() || keys[hash] == key) {
+                        slot = hash;
+                        return Core::Error::NONE;
+                    }
                 }
+                rehashFactor *= 2;
             }
-            return -1;
         }
 
         template<typename Value>

+ 2 - 2
data/RingBuffer.hpp → include/core/data/RingBuffer.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_RINGBUFFER_HPP
 #define CORE_RINGBUFFER_HPP
 
-#include "utils/AlignedData.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     template<typename T, int N>

+ 3 - 3
data/Stack.hpp → include/core/data/Stack.hpp

@@ -1,14 +1,14 @@
 #ifndef CORE_STACK_HPP
 #define CORE_STACK_HPP
 
-#include "data/ArrayList.hpp"
-#include "data/List.hpp"
+#include "core/data/ArrayList.hpp"
+#include "core/data/List.hpp"
 
 namespace Core {
     namespace Internal {
         template<typename T, typename S>
         class BaseStack final {
-            S data;
+            S data{};
 
         public:
             template<typename... Args>

+ 1 - 1
io/File.hpp → include/core/io/File.hpp

@@ -3,7 +3,7 @@
 
 #include <limits.h>
 
-#include "utils/ArrayString.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     using Path = String8<PATH_MAX>;

+ 1 - 3
io/FileReader.hpp → include/core/io/FileReader.hpp

@@ -1,11 +1,9 @@
 #ifndef CORE_FILE_READER_HPP
 #define CORE_FILE_READER_HPP
 
-#include "io/File.hpp"
+#include "core/io/File.hpp"
 
 namespace Core {
-    using Path = String8<PATH_MAX>;
-
     class FileReader final {
         void* file;
         Path path;

+ 2 - 2
math/Box.hpp → include/core/math/Box.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_BOX_HPP
 #define CORE_BOX_HPP
 
-#include "math/Vector.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/math/Vector.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     class Box final {

+ 1 - 1
math/BufferedValue.hpp → include/core/math/BufferedValue.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_BUFFERED_VALUE_HPP
 #define CORE_BUFFERED_VALUE_HPP
 
-#include "math/Math.hpp"
+#include "core/math/Math.hpp"
 
 namespace Core {
     template<typename T>

+ 3 - 3
math/Frustum.hpp → include/core/math/Frustum.hpp

@@ -1,9 +1,9 @@
 #ifndef CORE_FRUSTUM_HPP
 #define CORE_FRUSTUM_HPP
 
-#include "data/Array.hpp"
-#include "math/Matrix.hpp"
-#include "math/Plane.hpp"
+#include "core/data/Array.hpp"
+#include "core/math/Matrix.hpp"
+#include "core/math/Plane.hpp"
 
 namespace Core {
     class Frustum final {

+ 1 - 1
math/Math.hpp → include/core/math/Math.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_MATH_HPP
 #define CORE_MATH_HPP
 
-#include "utils/Meta.hpp"
+#include "core/utils/Meta.hpp"
 
 namespace Core::Math {
     template<typename T>

+ 2 - 2
math/Matrix.hpp → include/core/math/Matrix.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_MATRIX_HPP
 #define CORE_MATRIX_HPP
 
-#include "math/Quaternion.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/math/Quaternion.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     class Matrix final {

+ 3 - 3
math/MatrixStack.hpp → include/core/math/MatrixStack.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_MATRIX_STACK_HPP
 #define CORE_MATRIX_STACK_HPP
 
-#include "data/ArrayList.hpp"
-#include "math/Matrix.hpp"
+#include "core/data/ArrayList.hpp"
+#include "core/math/Matrix.hpp"
 
 namespace Core {
     template<int N>
@@ -10,7 +10,7 @@ namespace Core {
         ArrayList<Matrix, N> stack;
 
     public:
-        MatrixStack() {
+        MatrixStack() : stack() {
             (void)stack.add(Matrix());
         }
 

+ 2 - 2
math/Plane.hpp → include/core/math/Plane.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_PLANE_HPP
 #define CORE_PLANE_HPP
 
-#include "math/Vector.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/math/Vector.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     class Plane final {

+ 2 - 2
math/Quaternion.hpp → include/core/math/Quaternion.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_QUATERNION_HPP
 #define CORE_QUATERNION_HPP
 
-#include "math/Vector.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/math/Vector.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     class Quaternion final {

+ 8 - 4
math/Vector.hpp → include/core/math/Vector.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_VECTOR_HPP
 #define CORE_VECTOR_HPP
 
-#include "math/Math.hpp"
-#include "utils/ArrayString.hpp"
+#include "core/math/Math.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core {
     template<int N, typename T>
@@ -137,11 +137,15 @@ namespace Core {
         }
 
         float length() const {
-            return Math::squareRoot(squareLength());
+            return Math::squareRoot(static_cast<float>(squareLength()));
         }
 
         Vector& normalize() {
-            *this *= 1.0f / length();
+            if constexpr(Core::IsSame<float, T> || Core::IsSame<double, T>) {
+                *this *= 1.0f / length();
+            } else {
+                *this /= static_cast<T>(length());
+            }
             return *this;
         }
 

+ 5 - 5
math/View.hpp → include/core/math/View.hpp

@@ -1,14 +1,14 @@
 #ifndef CORE_VIEW_HPP
 #define CORE_VIEW_HPP
 
-#include "math/Matrix.hpp"
+#include "core/math/Matrix.hpp"
 
 namespace Core {
     class View final {
-        Matrix view;
-        Vector3 right;
-        Vector3 up;
-        Vector3 back;
+        Matrix view{};
+        Vector3 right{};
+        Vector3 up{};
+        Vector3 back{};
 
     public:
         void updateDirections(float lengthAngle, float widthAngle);

+ 29 - 0
include/core/thread/Thread.hpp

@@ -0,0 +1,29 @@
+#ifndef CORE_THREAD_HPP
+#define CORE_THREAD_HPP
+
+#include "core/utils/AlignedData.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Error.hpp"
+
+namespace Core {
+    class Thread final {
+        AlignedData<8, 8> thread;
+
+    public:
+        Thread();
+        Thread(const Thread& other) = delete;
+        Thread(Thread&& other);
+        ~Thread();
+        Thread& operator=(const Thread& other) = delete;
+        Thread& operator=(Thread&& other);
+
+        void swap(Thread& other);
+
+        using Function = int (*)(void*);
+
+        check_return Error start(Function f, void* p);
+        check_return Error join(int* returnValue = nullptr);
+    };
+}
+
+#endif

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

@@ -3,7 +3,7 @@
 
 #define alignmentof(type) __alignof__(type)
 
-#define ASSERT_ALIGNED_DATA(var, type)                                         \
+#define CORE_ASSERT_ALIGNED_DATA(var, type)                                    \
     static_assert(sizeof(var) == sizeof(type), "aligned data size missmatch"); \
     static_assert(var.getSize() >= var.getAlignment(), "size >= alignment");   \
     static_assert(alignmentof(var) == alignmentof(type),                       \
@@ -11,7 +11,7 @@
 
 namespace Core {
     template<int SIZE, int ALIGNMENT>
-    class alignas(ALIGNMENT) AlignedData {
+    class alignas(ALIGNMENT) AlignedData final {
         static_assert(SIZE > 0, "size must be positive");
         char buffer[static_cast<unsigned int>(SIZE)] = {};
 

+ 6 - 7
utils/ArrayString.hpp → include/core/utils/ArrayString.hpp

@@ -1,10 +1,10 @@
 #ifndef CORE_ARRAY_STRING_HPP
 #define CORE_ARRAY_STRING_HPP
 
-#include "math/Math.hpp"
-#include "utils/Check.hpp"
-#include "utils/Types.hpp"
-#include "utils/Utility.hpp"
+#include "core/math/Math.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Types.hpp"
+#include "core/utils/Utility.hpp"
 
 namespace Core {
     template<typename T>
@@ -166,7 +166,7 @@ namespace Core {
         }
 
         check_return Error append(float f) {
-            return convertAppend(static_cast<double>(f));
+            return convertAppend(f);
         }
 
         check_return Error append(double d) {
@@ -361,6 +361,7 @@ namespace Core {
         template<unsigned int L>
         void unicodeToChar(c32 c, char (&buffer)[L]) {
             static_assert(L >= 5, "to small char buffer");
+            buffer[0] = '\0';
             if(c < (1 << 7)) {
                 buffer[0] = static_cast<char>(((c >> 0) & 0x7F) | 0x0);
                 buffer[1] = '\0';
@@ -379,8 +380,6 @@ namespace Core {
                 buffer[2] = static_cast<char>(((c >> 6) & 0x3F) | 0x80);
                 buffer[3] = static_cast<char>(((c >> 0) & 0x3F) | 0x80);
                 buffer[4] = '\0';
-            } else {
-                buffer[0] = '\0';
             }
         }
 

+ 2 - 3
utils/Buffer.hpp → include/core/utils/Buffer.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_BUFFER_HPP
 #define CORE_BUFFER_HPP
 
-#include "utils/Check.hpp"
-#include "utils/Error.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Error.hpp"
 
 namespace Core {
     class Buffer final {
@@ -33,7 +33,6 @@ namespace Core {
 
         void clear();
 
-    private:
         void swap(Buffer& other);
     };
 }

+ 0 - 0
utils/Check.hpp → include/core/utils/Check.hpp


+ 4 - 6
utils/Clock.hpp → include/core/utils/Clock.hpp

@@ -1,21 +1,19 @@
 #ifndef CORE_CLOCK_HPP
 #define CORE_CLOCK_HPP
 
-#include "data/Array.hpp"
-#include "utils/Check.hpp"
-#include "utils/Types.hpp"
+#include "core/data/Array.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Types.hpp"
 
 namespace Core {
     struct Clock final {
         using Nanos = i64;
 
     private:
-        static constexpr int BITS = 7;
-        static constexpr int LENGTH = 1 << BITS;
         int index;
         Nanos last;
         Nanos sum;
-        Array<Nanos, LENGTH> time;
+        Array<Nanos, 1 << 7> time;
 
     public:
         Clock();

+ 0 - 0
utils/Color.hpp → include/core/utils/Color.hpp


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


+ 1 - 1
utils/HashCode.hpp → include/core/utils/HashCode.hpp

@@ -3,7 +3,7 @@
 
 #include <limits.h>
 
-#include "utils/Types.hpp"
+#include "core/utils/Types.hpp"
 
 namespace Core {
     template<typename H>

+ 2 - 2
utils/Logger.hpp → include/core/utils/Logger.hpp

@@ -1,7 +1,7 @@
 #ifndef CORE_LOGGER_HPP
 #define CORE_LOGGER_HPP
 
-#include "utils/ArrayString.hpp"
+#include "core/utils/ArrayString.hpp"
 
 namespace Core::Logger {
     enum class Level { ERROR, WARNING, INFO, DEBUG };
@@ -25,7 +25,7 @@ namespace Core::Logger {
         if(filterError(s.append(start)) || filterError(s.append("#:# | ")) ||
            filterError(s.format(file, line)) || filterError(s.append(format)) ||
            filterError(s.format(Core::forward<Args>(args)...)) ||
-           filterError(s.append("\33[39;49m\n")) || filterError(s.print())) {
+           filterError(s.append("\33[0m\n")) || filterError(s.print())) {
             CORE_EXIT(1);
         }
     }

+ 0 - 0
utils/Meta.hpp → include/core/utils/Meta.hpp


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

@@ -10,6 +10,5 @@ void operator delete[](void* p) noexcept;
 void operator delete(void* p, size_t bytes) noexcept;
 void operator delete[](void* p, size_t bytes) noexcept;
 void* operator new(size_t bytes, void* p) noexcept;
-// void* operator new[](size_t bytes, void* p) noexcept;
 
 #endif

+ 3 - 5
utils/Random.hpp → include/core/utils/Random.hpp

@@ -1,17 +1,15 @@
 #ifndef CORE_RANDOM_HPP
 #define CORE_RANDOM_HPP
 
-#include "utils/Types.hpp"
+#include "core/data/Array.hpp"
+#include "core/utils/Types.hpp"
 
 namespace Core {
     struct Random final {
         using Seed = u32;
 
     private:
-        constexpr static int N = 25;
-        constexpr static int M = 7;
-
-        Seed data[N];
+        Array<Seed, 25> data;
         int index;
 
     public:

+ 0 - 0
utils/Types.hpp → include/core/utils/Types.hpp


+ 1 - 1
utils/UniquePointer.hpp → include/core/utils/UniquePointer.hpp

@@ -31,7 +31,7 @@ namespace Core {
                 other.t = nullptr;
             }
             return *this;
-        };
+        }
 
         T* operator->() {
             return t;

+ 2 - 2
utils/Utility.hpp → include/core/utils/Utility.hpp

@@ -1,8 +1,8 @@
 #ifndef CORE_UTILITY_HPP
 #define CORE_UTILITY_HPP
 
-#include "utils/Check.hpp"
-#include "utils/Error.hpp"
+#include "core/utils/Check.hpp"
+#include "core/utils/Error.hpp"
 
 #define CORE_SIZE(t) static_cast<int>(sizeof(t))
 

+ 0 - 119
meson.build

@@ -1,119 +0,0 @@
-project('core', 'cpp', default_options : ['cpp_std=c++2a'])
-
-src = [
-    'utils/Logger.cpp',
-    'utils/Utility.cpp',
-    'utils/Error.cpp',
-    'utils/New.cpp',
-    'utils/Buffer.cpp',
-    'utils/Clock.cpp',
-    'utils/Random.cpp',
-    'data/BitArray.cpp',
-    'math/Math.cpp',
-    'math/Vector.cpp',
-    'math/Quaternion.cpp',
-    'math/Matrix.cpp',
-    'math/Box.cpp',
-    'math/Plane.cpp',
-    'math/Frustum.cpp',
-    'math/View.cpp',
-    'thread/Thread.cpp',
-    'io/FileReader.cpp',
-]
-
-src_tests = [
-    'test/Main.cpp',
-    'test/Test.cpp',
-    'tests/ArrayTests.cpp',
-    'tests/ArrayStringTests.cpp',
-    'tests/UtilityTests.cpp',
-    'tests/ArrayListTests.cpp',
-    'tests/BitArrayTests.cpp',
-    'tests/MathTests.cpp',
-    'tests/ListTests.cpp',
-    'tests/LinkedListTests.cpp',
-    'tests/UniquePointerTests.cpp',
-    'tests/HashMapTests.cpp',
-    'tests/ProbingHashMapTests.cpp',
-    'tests/StackTests.cpp',
-    'tests/RingBufferTests.cpp',
-    'tests/ComponentsTests.cpp',
-    'tests/VectorTests.cpp',
-    'tests/QuaternionTests.cpp',
-    'tests/MatrixTests.cpp',
-    'tests/BoxTests.cpp',
-    'tests/BufferedValueTests.cpp',
-    'tests/PlaneTests.cpp',
-    'tests/FrustumTests.cpp',
-    'tests/ViewTests.cpp',
-    'tests/MatrixStackTests.cpp',
-    'tests/ColorTests.cpp',
-    'tests/BufferTests.cpp',
-    'tests/ClockTests.cpp',
-    'tests/RandomTests.cpp',
-    'tests/ThreadTests.cpp',
-    'tests/FileReaderTests.cpp',
-]
-
-src_performance = [
-    'performance/Main.cpp',
-    'test/Test.cpp',
-]
-
-compiler = meson.get_compiler('cpp')
-error_args = compiler.get_supported_arguments([
-    '-Wdeprecated-enum-float-conversion', '-Wctad-maybe-unsupported', '-Werror',
-    '-Wdeprecated-enum-enum-conversion', '-Winvalid-imported-macros', '-Wextra',
-    '-Wzero-as-null-pointer-constant', '-Wframe-larger-than=8388608', '-Wundef',
-    '-Wunused-const-variable=2', '-Wconditionally-supported', '-Wwrite-strings',
-    '-Wlarger-than=1073741824', '-Wimplicit-fallthrough=5', '-Wduplicated-cond',
-    '-Wdisabled-optimization', '-Wsuggest-final-methods', '-Wformat-signedness',
-    '-Wtrivial-auto-var-init', '-Wmissing-include-dirs', '-Winfinite-recursion',
-    '-Wdeprecated-copy-dtor', '-Wanalyzer-too-complex', '-Wduplicated-branches',
-    '-Wstrict-null-sentinel', '-Wmissing-declarations', '-Wformat-truncation=2',
-    '-Wmultiple-inheritance', '-Wstack-usage=8388608', '-Winit-self', '-Wsynth',
-    '-Wvirtual-inheritance', '-Wstringop-overflow=4', '-Wcast-qual', '-Wshadow',
-    '-Wsuggest-final-types', '-Woverloaded-virtual', '-Wconversion', '-Walloca',
-    '-Woverlength-strings', '-Wctor-dtor-privacy', '-Wswitch-enum', '-pedantic',
-    '-Wstrict-overflow=2', '-Wcast-align=strict', '-Wfloat-equal', '-Wformat=2',
-    '-Wattribute-alias=2', '-Wformat-overflow=2', '-Winvalid-pch', '-Wvolatile',
-    '-Wshift-overflow=2', '-Warith-conversion', '-Wcatch-value=3', '-Wnoexcept',
-    '-Wuse-after-free=3', '-Wdouble-promotion', '-Wunused-macros', '-Wregister',
-    '-Wsuggest-override', '-Wnull-dereference', '-Wtrampolines', '-Wlogical-op',
-    '-Wnon-virtual-dtor', '-pedantic-errors', '-Wbidi-chars=any', '-Wdate-time',
-    '-Warray-parameter', '-Waligned-new=all', '-Wold-style-cast', '-Wmultichar',
-    '-Wstack-protector', '-Wmissing-braces', '-Warray-bounds=2', '-Walloc-zero',
-    '-Wplacement-new=2', '-Wmismatched-tags', '-Wcomma-subscript', '-Wall',
-    '-Wbidi-chars', '-Wredundant-tags', '-Wenum-conversion', '-Wall',
-    '-Wredundant-decls', '-Wsign-conversion'
-])
-compile_args = compiler.get_supported_arguments([
-    '-nostdinc++', '-fno-exceptions', '-fno-rtti', '-fno-threadsafe-statics'
-])
-link_args = compiler.get_supported_arguments([
-    '-nodefaultlibs', '-lc', '-lm', '-lpthread'
-])
-
-
-core_include = [include_directories('.')]
-core = static_library('core', 
-    sources: src,
-    include_directories: core_include,
-    cpp_args: error_args + compile_args + ['-DCORE_LOG_LEVEL=4'],
-    link_args: link_args)
-
-core_dep = declare_dependency(
-    include_directories: core_include,
-    link_with: core)
-
-executable('tests', 
-    sources: src_tests,
-    dependencies: core_dep,
-    cpp_args: error_args + compile_args + ['-DCORE_LOG_LEVEL=4'],
-    link_args: link_args)
-
-executable('performance', 
-    sources: src_performance,
-    dependencies: core_dep,
-    cpp_args: error_args + compile_args + ['-DCORE_LOG_LEVEL=4'],
-    link_args: link_args)

+ 6 - 6
performance/Main.cpp

@@ -1,15 +1,15 @@
-#include "data/HashMap.hpp"
-#include "data/ProbingHashMap.hpp"
-#include "test/Test.hpp"
-#include "utils/Clock.hpp"
-#include "utils/Random.hpp"
+#include "../test/Test.hpp"
+#include "core/data/HashMap.hpp"
+#include "core/data/ProbingHashMap.hpp"
+#include "core/utils/Clock.hpp"
+#include "core/utils/Random.hpp"
 
 using Millis = Core::Clock::Nanos;
 
 struct Timer {
     Core::Clock::Nanos nanos;
 
-    Timer() {
+    Timer() : nanos(0) {
         CORE_TEST_ERROR(Core::Clock::getNanos(nanos));
     }
 

+ 2 - 2
data/BitArray.cpp → src/BitArray.cpp

@@ -1,6 +1,6 @@
-#include "data/BitArray.hpp"
+#include "core/data/BitArray.hpp"
 
-#include "math/Math.hpp"
+#include "core/math/Math.hpp"
 
 static int roundUpDivide(int a, int b) {
     if(a % b == 0) {

+ 2 - 2
math/Box.cpp → src/Box.cpp

@@ -1,10 +1,10 @@
-#include "math/Box.hpp"
+#include "core/math/Box.hpp"
 
 Core::Box::Box(const Vector3& min_, const Vector3& max_)
     : min(min_), max(max_) {
 }
 
-Core::Box::Box(const Vector3& size) {
+Core::Box::Box(const Vector3& size) : min(), max() {
     for(int i = 0; i < 3; i++) {
         if(size[i] < 0.0f) {
             min[i] = size[i];

+ 3 - 3
utils/Buffer.cpp → src/Buffer.cpp

@@ -1,7 +1,7 @@
-#include "utils/Buffer.hpp"
+#include "core/utils/Buffer.hpp"
 
-#include "math/Math.hpp"
-#include "utils/Utility.hpp"
+#include "core/math/Math.hpp"
+#include "core/utils/Utility.hpp"
 
 Core::Buffer::Buffer(int initialSize)
     : length(0), capacity(initialSize <= 0 ? 1 : initialSize), buffer(nullptr) {

+ 6 - 4
utils/Clock.cpp → src/Clock.cpp

@@ -1,8 +1,10 @@
-#include "utils/Clock.hpp"
+#include "core/utils/Clock.hpp"
 
 #include <threads.h>
 #include <time.h>
 
+#include "ErrorSimulator.hpp"
+
 Core::Clock::Clock() : index(0), last(0), sum(0), time(0) {
 }
 
@@ -14,7 +16,7 @@ Core::Error Core::Clock::update(Clock::Nanos& n) {
         n = 0;
         return Error::NONE;
     }
-    index = (index + 1) & (LENGTH - 1);
+    index = (index + 1) & (time.getLength() - 1);
     sum -= time[index];
     time[index] = current - last;
     sum += time[index];
@@ -24,12 +26,12 @@ Core::Error Core::Clock::update(Clock::Nanos& n) {
 }
 
 float Core::Clock::getUpdatesPerSecond() const {
-    return (LENGTH * 1000000000.0f) / static_cast<float>(sum);
+    return (time.getLength() * 1000000000.0f) / static_cast<float>(sum);
 }
 
 Core::Error Core::Clock::getNanos(Clock::Nanos& n) {
     timespec ts;
-    if(timespec_get(&ts, TIME_UTC) == 0) {
+    if(timespec_get(&ts, TIME_UTC) == 0 || CORE_TIME_GET_FAIL) {
         return Error::TIME_NOT_AVAILABLE;
     }
     n = static_cast<Clock::Nanos>(ts.tv_sec) * 1'000'000'000L +

+ 1 - 1
utils/Error.cpp → src/Error.cpp

@@ -1,4 +1,4 @@
-#include "utils/Error.hpp"
+#include "core/utils/Error.hpp"
 
 const char* Core::getErrorName(Error e) {
     switch(e) {

+ 10 - 0
src/ErrorSimulator.cpp

@@ -0,0 +1,10 @@
+#include "ErrorSimulator.hpp"
+
+#ifdef ERROR_SIMULATOR
+
+bool Core::Fail::realloc = false;
+bool Core::Fail::fileClose = false;
+bool Core::Fail::timeGet = false;
+int Core::Fail::leftAllocations = -1;
+
+#endif

+ 20 - 0
src/ErrorSimulator.hpp

@@ -0,0 +1,20 @@
+#ifndef CORE_ERROR_SIMULATOR_HPP
+#define CORE_ERROR_SIMULATOR_HPP
+
+#ifdef ERROR_SIMULATOR
+namespace Core::Fail {
+    extern bool realloc;
+    extern bool fileClose;
+    extern bool timeGet;
+    extern int leftAllocations;
+}
+#define CORE_REALLOC_FAIL Core::Fail::realloc
+#define CORE_FILE_CLOSE_FAIL Core::Fail::fileClose
+#define CORE_TIME_GET_FAIL Core::Fail::timeGet
+#else
+#define CORE_REALLOC_FAIL false
+#define CORE_FILE_CLOSE_FAIL false
+#define CORE_TIME_GET_FAIL false
+#endif
+
+#endif

+ 5 - 4
io/FileReader.cpp → src/FileReader.cpp

@@ -1,10 +1,11 @@
-#include "io/FileReader.hpp"
+#include "core/io/FileReader.hpp"
 
 #include <stdio.h>
 
-#include "utils/Logger.hpp"
+#include "ErrorSimulator.hpp"
+#include "core/utils/Logger.hpp"
 
-Core::FileReader::FileReader() : file(nullptr) {
+Core::FileReader::FileReader() : file(nullptr), path() {
 }
 
 Core::FileReader::FileReader(FileReader&& other) : FileReader() {
@@ -14,7 +15,7 @@ Core::FileReader::FileReader(FileReader&& other) : FileReader() {
 Core::FileReader::~FileReader() {
     if(file != nullptr) {
         int r = fclose(static_cast<FILE*>(file));
-        if(r != 0) {
+        if(r != 0 || CORE_FILE_CLOSE_FAIL) {
             CORE_LOG_WARNING("Cannot close file #: #", path, r);
         }
         file = nullptr;

+ 3 - 2
math/Frustum.cpp → src/Frustum.cpp

@@ -1,7 +1,8 @@
-#include "math/Frustum.hpp"
+#include "core/math/Frustum.hpp"
 
 Core::Frustum::Frustum(float fieldOfView, float nearClip_, float farClip_)
-    : tan(Core::Math::tan(Core::Math::degreeToRadian(fieldOfView) * 0.5f)),
+    : projection(), planes(),
+      tan(Core::Math::tan(Core::Math::degreeToRadian(fieldOfView) * 0.5f)),
       nearClip(nearClip_), farClip(farClip_) {
     float diff = 1.0f / (nearClip - farClip);
     projection.set(1, Vector4(0.0f, 1.0f / tan, 0.0f, 0.0f));

+ 1 - 1
utils/Logger.cpp → src/Logger.cpp

@@ -1,4 +1,4 @@
-#include "utils/Logger.hpp"
+#include "core/utils/Logger.hpp"
 
 Core::Logger::Level Core::Logger::level = Core::Logger::Level::DEBUG;
 

+ 1 - 1
math/Math.cpp → src/Math.cpp

@@ -1,4 +1,4 @@
-#include "math/Math.hpp"
+#include "core/math/Math.hpp"
 
 #include <math.h>
 

+ 2 - 2
math/Matrix.cpp → src/Matrix.cpp

@@ -1,6 +1,6 @@
-#include "math/Matrix.hpp"
+#include "core/math/Matrix.hpp"
 
-#include "math/Math.hpp"
+#include "core/math/Math.hpp"
 
 Core::Matrix::Matrix() {
     unit();

+ 18 - 7
utils/New.cpp → src/New.cpp

@@ -1,12 +1,28 @@
-#include "utils/New.hpp"
+#include "core/utils/New.hpp"
 
 #include <stdlib.h>
 
+#include "ErrorSimulator.hpp"
+
 void* operator new(size_t bytes) noexcept {
+#ifdef ERROR_SIMULATOR
+    if(Core::Fail::leftAllocations > 0) {
+        Core::Fail::leftAllocations--;
+    } else if(Core::Fail::leftAllocations == 0) {
+        return nullptr;
+    }
+#endif
     return malloc(bytes);
 }
 
 void* operator new[](size_t bytes) noexcept {
+#ifdef ERROR_SIMULATOR
+    if(Core::Fail::leftAllocations > 0) {
+        Core::Fail::leftAllocations--;
+    } else if(Core::Fail::leftAllocations == 0) {
+        return nullptr;
+    }
+#endif
     return malloc(bytes);
 }
 
@@ -20,7 +36,7 @@ void operator delete[](void* p) noexcept {
 
 void operator delete(void* p, size_t bytes) noexcept {
     (void)bytes;
-    free(p);
+    operator delete(p);
 }
 
 void operator delete[](void* p, size_t bytes) noexcept {
@@ -32,8 +48,3 @@ void* operator new(size_t bytes, void* p) noexcept {
     (void)bytes;
     return p;
 }
-
-/*void* operator new[](size_t bytes, void* p) noexcept {
-    (void)bytes;
-    return p;
-}*/

+ 2 - 2
math/Plane.cpp → src/Plane.cpp

@@ -1,6 +1,6 @@
-#include "math/Plane.hpp"
+#include "core/math/Plane.hpp"
 
-Core::Plane::Plane() : d(0) {
+Core::Plane::Plane() : abc(), d(0) {
 }
 
 Core::Plane::Plane(const Vector3& a, const Vector3& b, const Vector3& c)

+ 4 - 3
math/Quaternion.cpp → src/Quaternion.cpp

@@ -1,9 +1,10 @@
-#include "math/Quaternion.hpp"
+#include "core/math/Quaternion.hpp"
 
-Core::Quaternion::Quaternion() : w(1.0f) {
+Core::Quaternion::Quaternion() : xyz(), w(1.0f) {
 }
 
-Core::Quaternion::Quaternion(const Vector3& axis, float angle) : xyz(axis) {
+Core::Quaternion::Quaternion(const Vector3& axis, float angle)
+    : xyz(axis), w(1.0f) {
     xyz.normalize();
     float factor = 0.0f;
     Core::Math::sinCos(Core::Math::degreeToRadian(angle) * 0.5f, factor, w);

+ 12 - 7
utils/Random.cpp → src/Random.cpp

@@ -1,7 +1,11 @@
-#include "utils/Random.hpp"
+#include "core/utils/Random.hpp"
 
-Core::Random::Random(Seed seed) : index(0) {
-    for(int i = 0; i < N; i++) {
+#include <stdio.h>
+
+constexpr static int M = 7;
+
+Core::Random::Random(Seed seed) : data(), index(0) {
+    for(int i = 0; i < data.getLength(); i++) {
         data[i] = seed;
         seed = seed * 7 + 31;
     }
@@ -9,17 +13,18 @@ Core::Random::Random(Seed seed) : index(0) {
 
 void Core::Random::update() {
     static const Seed map[2] = {0, 0x8EBFD028};
-    for(int i = 0; i < N - M; i++) {
+    for(int i = 0; i < data.getLength() - M; i++) {
         data[i] = data[i + M] ^ (data[i] >> 1) ^ map[data[i] & 1];
     }
-    for(int i = N - M; i < N; i++) {
-        data[i] = data[i + (M - N)] ^ (data[i] >> 1) ^ map[data[i] & 1];
+    for(int i = data.getLength() - M; i < data.getLength(); i++) {
+        data[i] = data[i + (M - data.getLength())] ^ (data[i] >> 1) ^
+                  map[data[i] & 1];
     }
     index = 0;
 }
 
 int Core::Random::next() {
-    if(index >= N) {
+    if(index >= data.getLength()) {
         update();
     }
     Seed r = data[index++];

+ 59 - 0
src/Thread.cpp

@@ -0,0 +1,59 @@
+#include "core/thread/Thread.hpp"
+
+#include <threads.h>
+
+#include "core/utils/Meta.hpp"
+#include "core/utils/Utility.hpp"
+
+Core::Thread::Thread() : thread() {
+}
+
+Core::Thread::Thread(Thread&& other) : thread() {
+    swap(other);
+}
+
+static bool doesExist(thrd_t* t) {
+    thrd_t zero{};
+    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);
+    }
+}
+
+Core::Thread& Core::Thread::operator=(Thread&& other) {
+    if(this != &other) {
+        if(doesExist(thread.as<thrd_t>())) {
+            (void)join(nullptr);
+        }
+        reset(thread.as<thrd_t>());
+        swap(other);
+    }
+    return *this;
+}
+
+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;
+}
+
+check_return Core::Error Core::Thread::join(int* returnValue) {
+    int e = thrd_join(*thread.as<thrd_t>(), returnValue);
+    reset(thread.as<thrd_t>());
+    return e != thrd_success ? Error::THREAD_ERROR : Error::NONE;
+}
+
+void Core::Thread::swap(Thread& other) {
+    thrd_t tmp;
+    memoryCopy(&tmp, thread.as<thrd_t>(), sizeof(thrd_t));
+    memoryCopy(thread.as<thrd_t>(), other.thread.as<thrd_t>(), sizeof(thrd_t));
+    memoryCopy(other.thread.as<thrd_t>(), &tmp, sizeof(thrd_t));
+}

+ 4 - 2
utils/Utility.cpp → src/Utility.cpp

@@ -1,9 +1,11 @@
-#include "utils/Utility.hpp"
+#include "core/utils/Utility.hpp"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "ErrorSimulator.hpp"
+
 static Core::ExitHandler exitHandler = nullptr;
 static void* exitData = nullptr;
 
@@ -75,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) {
+    if(newP == nullptr || CORE_REALLOC_FAIL) {
         return Error::OUT_OF_MEMORY;
     }
     p = newP;

+ 1 - 1
math/Vector.cpp → src/Vector.cpp

@@ -1,4 +1,4 @@
-#include "math/Vector.hpp"
+#include "core/math/Vector.hpp"
 
 template<>
 Core::Vector3& Core::Vector3::setAngles(float lengthAngle, float widthAngle) {

+ 1 - 1
math/View.cpp → src/View.cpp

@@ -1,4 +1,4 @@
-#include "math/View.hpp"
+#include "core/math/View.hpp"
 
 void Core::View::updateDirections(float lengthAngle, float widthAngle) {
     back.setAngles(lengthAngle, widthAngle);

+ 115 - 0
tasks

@@ -0,0 +1,115 @@
+#!/bin/bash
+set -e
+clear
+cd $(dirname $0)
+
+printHelpExit() {
+    echo "$0 clean       | remove build results"
+    echo "$0 build       | build everything"
+    echo "$0 install     | move build results into the install folder"
+    echo "$0 test        | run the tests"
+    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 time        | check build time"
+    exit 0
+}
+
+# TODO gcovr https://gcovr.com/en/5.0/guide.html
+
+task=$1
+if [ -z "$task" ]; then
+    printHelpExit
+fi
+
+# tasks
+build=false
+test=false
+performance=false
+time=false
+install=false
+coverage=false
+
+# parsing
+if [ "$task" = "clean" ]; then
+    rm -rf build install
+    exit 0
+elif [ "$task" = "build" ]; then
+    build=true
+elif [ "$task" = "install" ]; then
+    build=true
+    install=true
+elif [ "$task" = "coverage" ]; then
+    build=true
+    coverage=true
+    test=true
+elif [ "$task" = "test" ]; then
+    build=true
+    test=true
+elif [ "$task" = "performance" ]; then
+    build=true
+    performance=true
+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
+    exit 0
+elif [ "$task" = "macro" ]; then
+    grep -r "#define" src | grep -v " CORE" || true
+    exit 0
+elif [ "$task" = "include" ]; then
+    grep -r "#include <" src | grep "\.hpp" || true
+    exit 0
+else
+    echo "unknown task"
+    printHelpExit
+fi
+
+# task execution
+if $build; then
+    if [ ! -e build ]; then 
+        cmake -B build -S . -G Ninja -DCMAKE_INSTALL_PREFIX=./install
+    fi
+    ninja -C build
+fi
+if $install; then
+    ninja -C build install
+fi
+if $test; then
+    cd build
+    ./test || true
+    cd ..
+fi
+if $performance; then
+    cd build
+    ./performance
+fi
+if $time; then
+    lines=$(cat build/.ninja_log | grep "^[0-9]")
+
+    startMillis=0
+    endMillis=0
+    name=""
+    i=0
+    output=""
+    for arg in $lines; do
+        if [ $i == 0 ]; then
+            startMillis=$arg
+        elif [ $i == 1 ]; then
+            endMillis=$arg
+        elif [ $i == 3 ]; then
+            name=$arg
+            diff=$(expr $endMillis - $startMillis)
+            output="${output}\n$diff $name"
+        fi
+        i=$(expr $(expr $i + 1) % 5) && true
+    done
+    printf "$output" | sort -n
+fi
+if $coverage; then
+    gcovr -r . build -e test -e performance
+fi

+ 45 - 63
test/Main.cpp

@@ -1,35 +1,8 @@
-#include "test/Test.hpp"
-#include "tests/ArrayListTests.hpp"
-#include "tests/ArrayStringTests.hpp"
-#include "tests/ArrayTests.hpp"
-#include "tests/BitArrayTests.hpp"
-#include "tests/BoxTests.hpp"
-#include "tests/BufferTests.hpp"
-#include "tests/BufferedValueTests.hpp"
-#include "tests/ClockTests.hpp"
-#include "tests/ColorTests.hpp"
-#include "tests/ComponentsTests.hpp"
-#include "tests/FileReaderTests.hpp"
-#include "tests/FrustumTests.hpp"
-#include "tests/HashMapTests.hpp"
-#include "tests/LinkedListTests.hpp"
-#include "tests/ListTests.hpp"
-#include "tests/MathTests.hpp"
-#include "tests/MatrixStackTests.hpp"
-#include "tests/MatrixTests.hpp"
-#include "tests/PlaneTests.hpp"
-#include "tests/ProbingHashMapTests.hpp"
-#include "tests/QuaternionTests.hpp"
-#include "tests/RandomTests.hpp"
-#include "tests/RingBufferTests.hpp"
-#include "tests/StackTests.hpp"
-#include "tests/ThreadTests.hpp"
-#include "tests/UniquePointerTests.hpp"
-#include "tests/UtilityTests.hpp"
-#include "tests/VectorTests.hpp"
-#include "tests/ViewTests.hpp"
-#include "utils/ArrayString.hpp"
-#include "utils/Utility.hpp"
+#include "../src/ErrorSimulator.hpp"
+#include "Test.hpp"
+#include "Tests.hpp"
+#include "core/utils/ArrayString.hpp"
+#include "core/utils/Utility.hpp"
 
 static void onExit(int code, void* data) {
     unsigned int i = *static_cast<unsigned int*>(data);
@@ -40,40 +13,49 @@ static void onExit(int code, void* data) {
     Core::Test::finalize();
 }
 
-int main() {
-    Core::ArrayListTests::test();
-    Core::ArrayStringTests::test();
-    Core::ArrayTests::test();
-    Core::BitArrayTests::test();
-    Core::BoxTests::test();
-    Core::BufferTests::test();
-    Core::BufferedValueTests::test();
-    Core::ClockTests::test();
-    Core::ColorTests::test();
-    Core::ComponentsTests::test();
-    Core::FileReaderTests::test();
-    Core::FrustumTests::test();
-    Core::HashMapTests::test();
-    Core::LinkedListTests::test();
-    Core::ListTests::test();
-    Core::MathTests::test();
-    Core::MatrixStackTests::test();
-    Core::MatrixTests::test();
-    Core::PlaneTests::test();
-    Core::ProbingHashMapTests::test();
-    Core::QuaternionTests::test();
-    Core::RandomTests::test();
-    Core::RingBufferTests::test();
-    Core::StackTests::test();
-    Core::ThreadTests::test();
-    Core::UniquePointerTests::test();
-    Core::UtilityTests::test();
-    Core::VectorTests::test();
-    Core::ViewTests::test();
+int main(int argAmount, const char**) {
+    bool light = argAmount <= 1;
+    Core::testArrayList(light);
+    Core::testArrayString();
+    Core::testArray();
+    Core::testBitArray();
+    Core::testBox();
+    Core::testBuffer(light);
+    Core::testBufferedValue();
+    Core::testClock(light);
+    Core::testColor();
+    Core::testComponents();
+    Core::testError();
+    Core::testFileReader();
+    Core::testFrustum();
+    Core::testHashMap(light);
+    Core::testLinkedList(light);
+    Core::testList(light);
+    Core::testMath();
+    Core::testMatrixStack(light);
+    Core::testMatrix();
+    Core::testNew();
+    Core::testPlane();
+    Core::testProbingHashMap(light);
+    Core::testQuaternion();
+    Core::testRandom(light);
+    Core::testRingBuffer();
+    Core::testStack(light);
+    Core::testThread();
+    Core::testUniquePointer();
+    Core::testUtility();
+    Core::testVector();
+    Core::testView();
+
+    Core::Logger::level = Core::Logger::Level::WARNING;
+    CORE_LOG_DEBUG("You won't see this!");
+    Core::Logger::level = Core::Logger::Level::DEBUG;
 
     unsigned int data = 123456789;
     Core::setExitHandler(onExit, &data);
-    CORE_EXIT(1);
+
+    char buffer[] = {-100, 0};
+    CORE_LOG_DEBUG(buffer);
 
     return 0;
 }

+ 1 - 1
test/Test.cpp

@@ -1,4 +1,4 @@
-#include "test/Test.hpp"
+#include "Test.hpp"
 
 Core::HashMap<Core::Test::Internal::FileName, Core::Test::Internal::Result>
     Core::Test::Internal::results;

+ 4 - 3
test/Test.hpp

@@ -1,9 +1,9 @@
 #ifndef CORE_TEST_HPP
 #define CORE_TEST_HPP
 
-#include "data/HashMap.hpp"
-#include "math/Vector.hpp"
-#include "utils/Logger.hpp"
+#include "core/data/HashMap.hpp"
+#include "core/math/Vector.hpp"
+#include "core/utils/Logger.hpp"
 
 namespace Core::Test {
     namespace Internal {
@@ -19,6 +19,7 @@ namespace Core::Test {
         template<typename T>
         bool check(const char* file, int line, const T& wanted, const T& actual,
                    bool c) {
+            file = Logger::getFileName(file);
             Error e = Error::NONE;
             FileName fileName;
             if(checkError(e, fileName.append(file))) {

+ 40 - 0
test/Tests.hpp

@@ -0,0 +1,40 @@
+#ifndef CORE_TESTS_HPP
+#define CORE_TESTS_HPP
+
+#include "Test.hpp"
+
+namespace Core {
+    void testArrayList(bool light);
+    void testArrayString();
+    void testArray();
+    void testBitArray();
+    void testBox();
+    void testBuffer(bool light);
+    void testBufferedValue();
+    void testClock(bool light);
+    void testColor();
+    void testComponents();
+    void testError();
+    void testFileReader();
+    void testFrustum();
+    void testHashMap(bool light);
+    void testLinkedList(bool light);
+    void testList(bool light);
+    void testMath();
+    void testMatrixStack(bool light);
+    void testMatrix();
+    void testNew();
+    void testPlane();
+    void testProbingHashMap(bool light);
+    void testQuaternion();
+    void testRandom(bool light);
+    void testRingBuffer();
+    void testStack(bool light);
+    void testThread();
+    void testUniquePointer();
+    void testUtility();
+    void testVector();
+    void testView();
+}
+
+#endif

+ 22 - 8
tests/ArrayListTests.cpp → test/modules/ArrayListTests.cpp

@@ -1,8 +1,7 @@
-#include "tests/ArrayListTests.hpp"
-
-#include "data/ArrayList.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/data/ArrayList.hpp"
 
+template class Core::ArrayList<int, 20>;
 using IntList = Core::ArrayList<int, 20>;
 
 static void testAdd() {
@@ -39,12 +38,13 @@ static void testClear() {
     CORE_TEST_EQUAL(0, list.getLength());
 }
 
-static void testOverflow() {
+static void testOverflow(bool light) {
     IntList list;
     for(int i = 0; i < 20; i++) {
         CORE_TEST_ERROR(list.add(i));
     }
-    for(int i = 0; i < 100000; i++) {
+    int limit = light ? 1000 : 100000;
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, list.add(i));
     }
     for(int i = 0; i < list.getLength(); i++) {
@@ -143,12 +143,25 @@ static void testRemove() {
     CORE_TEST_EQUAL(0, list.getLength());
 }
 
-void Core::ArrayListTests::test() {
+static void testForRange() {
+    IntList list;
+    CORE_TEST_ERROR(list.add(1));
+    CORE_TEST_ERROR(list.add(2));
+    CORE_TEST_ERROR(list.add(3));
+    for(int& i : list) {
+        i++;
+    }
+    for(int i = 0; i < list.getLength(); i++) {
+        CORE_TEST_EQUAL(i + 2, list[i]);
+    }
+}
+
+void Core::testArrayList(bool light) {
     testAdd();
     testMultipleAdd();
     testAddReplace();
     testClear();
-    testOverflow();
+    testOverflow(light);
     testCopy();
     testCopyAssignment();
     testMove();
@@ -157,4 +170,5 @@ void Core::ArrayListTests::test() {
     testToString2();
     testToString3();
     testRemove();
+    testForRange();
 }

+ 121 - 5
tests/ArrayStringTests.cpp → test/modules/ArrayStringTests.cpp

@@ -1,8 +1,9 @@
-#include "tests/ArrayStringTests.hpp"
+#include "../Tests.hpp"
+#include "core/data/HashMap.hpp"
+#include "core/utils/ArrayString.hpp"
 
-#include "data/HashMap.hpp"
-#include "test/Test.hpp"
-#include "utils/ArrayString.hpp"
+template class Core::ArrayString<128, char>;
+template class Core::ArrayString<128, char32_t>;
 
 using String8 = Core::String8<128>;
 using String32 = Core::String32<128>;
@@ -436,6 +437,49 @@ static void testCastAppendSelf8() {
     CORE_TEST_STRING("abcabcabcabc", s);
 }
 
+static void testCompareWithShorter8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append("abc"));
+    CORE_TEST_FALSE(s == "ab");
+}
+
+static void testAppendSignedChar8() {
+    const signed char buffer[] = {'a', 'b', 'c', '\0'};
+    String8 s;
+    CORE_TEST_ERROR(s.append(buffer));
+    CORE_TEST_TRUE(s == "abc");
+}
+
+static void testAppendUnsignedChar8() {
+    const unsigned char buffer[] = {'a', 'b', 'c', '\0'};
+    String8 s;
+    CORE_TEST_ERROR(s.append(buffer));
+    CORE_TEST_TRUE(s == "abc");
+}
+
+static void testAppendError8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append(Core::Error::NONE));
+    CORE_TEST_TRUE(s == "NONE");
+}
+
+static void testPrint8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append('\u0040'));
+    CORE_TEST_ERROR(s.append(L'\u0400'));
+    CORE_TEST_ERROR(s.append(L'\u8000'));
+    CORE_TEST_ERROR(s.append(U'\U00100000'));
+    CORE_TEST_EQUAL(build("\u0040\u0400\u8000\U00100000"), s);
+    CORE_TEST_ERROR(s.print());
+}
+
+static void testKeepHash8() {
+    String8 s;
+    CORE_TEST_ERROR(s.append("a ## test #### #####"));
+    CORE_TEST_ERROR(s.format(1, 2, 3, 4, 5, 6, 7, 8, 9));
+    CORE_TEST_STRING("a # test ## ##123456789", s);
+}
+
 static String32 build(const c32* cs) {
     String32 s;
     CORE_TEST_ERROR(s.append(cs));
@@ -874,6 +918,65 @@ static void testCastAppendSelf32() {
     CORE_TEST_STRING("abcabcabcabc", s);
 }
 
+static void testCompareWithShorter32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append("abc"));
+    CORE_TEST_FALSE(s == U"ab");
+}
+
+static void testAppendSignedChar32() {
+    const signed char buffer[] = {'a', 'b', 'c', '\0'};
+    String32 s;
+    CORE_TEST_ERROR(s.append(buffer));
+    CORE_TEST_TRUE(s == U"abc");
+}
+
+static void testAppendUnsignedChar32() {
+    const unsigned char buffer[] = {'a', 'b', 'c', '\0'};
+    String32 s;
+    CORE_TEST_ERROR(s.append(buffer));
+    CORE_TEST_TRUE(s == U"abc");
+}
+
+static void testAppendError32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append(Core::Error::NONE));
+    CORE_TEST_TRUE(s == U"NONE");
+}
+
+static void testPrint32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append('\u0040'));
+    CORE_TEST_ERROR(s.append(L'\u0400'));
+    CORE_TEST_ERROR(s.append(L'\u8000'));
+    CORE_TEST_ERROR(s.append(U'\U00100000'));
+    CORE_TEST_EQUAL(build(U"\u0040\u0400\u8000\U00100000"), s);
+    CORE_TEST_ERROR(s.print());
+}
+
+static void testVariousUnicode32() {
+    const unsigned char buffer[] = {0xC0, 0};
+    const unsigned char buffer2[] = {0xE0, 0};
+    const unsigned char buffer3[] = {0xE0, 1, 2, 0};
+    const unsigned char buffer4[] = {0xF0, 0};
+    const unsigned char buffer5[] = {0xF0, 1, 2, 3, 0};
+    const unsigned char buffer6[] = {0xFF, 0};
+    String32 s;
+    CORE_TEST_EQUAL(Core::Error::INVALID_CHAR, s.append(buffer));
+    CORE_TEST_EQUAL(Core::Error::INVALID_CHAR, s.append(buffer2));
+    CORE_TEST_EQUAL(Core::Error::NONE, s.append(buffer3));
+    CORE_TEST_EQUAL(Core::Error::INVALID_CHAR, s.append(buffer4));
+    CORE_TEST_EQUAL(Core::Error::NONE, s.append(buffer5));
+    CORE_TEST_EQUAL(Core::Error::INVALID_CHAR, s.append(buffer6));
+}
+
+static void testKeepHash32() {
+    String32 s;
+    CORE_TEST_ERROR(s.append("a ## test #### #####"));
+    CORE_TEST_ERROR(s.format(1, 2, 3, 4, 5, 6, 7, 8, 9));
+    CORE_TEST_STRING("a # test ## ##123456789", s);
+}
+
 static void testConversion() {
     const c32* a = U"öüewfde_§$§%$ädsf";
     const char* b = "öüewfde_§$§%$ädsf";
@@ -892,7 +995,7 @@ static void testConversion() {
     CORE_TEST_STRING(b, sb2);
 }
 
-void Core::ArrayStringTests::test() {
+void Core::testArrayString() {
     testEquality8();
     testUnicodeEquality8();
     testInequality8();
@@ -930,6 +1033,12 @@ void Core::ArrayStringTests::test() {
     testReplace8();
     testReplaceChar8();
     testCastAppendSelf8();
+    testCompareWithShorter8();
+    testAppendSignedChar8();
+    testAppendUnsignedChar8();
+    testAppendError8();
+    testPrint8();
+    testKeepHash8();
 
     testEquality32();
     testUnicodeEquality32();
@@ -968,6 +1077,13 @@ void Core::ArrayStringTests::test() {
     testReplace32();
     testReplaceChar32();
     testCastAppendSelf32();
+    testCompareWithShorter32();
+    testAppendSignedChar32();
+    testAppendUnsignedChar32();
+    testAppendError32();
+    testPrint32();
+    testVariousUnicode32();
+    testKeepHash32();
 
     testConversion();
 }

+ 49 - 0
test/modules/ArrayTests.cpp

@@ -0,0 +1,49 @@
+#include "../Tests.hpp"
+#include "core/data/Array.hpp"
+
+template class Core::Array<int, 3>;
+
+static void testToString1() {
+    Core::Array<int, 3> a;
+    a[0] = 1;
+    a[1] = 243;
+    a[2] = -423;
+    CORE_TEST_STRING("[1, 243, -423]", a);
+}
+
+static void testToString2() {
+    Core::Array<int, 1> a;
+    a[0] = 1;
+    CORE_TEST_STRING("[1]", a);
+}
+
+static void testReadConst() {
+    Core::Array<int, 3> a;
+    for(int i = 0; i < a.getLength(); i++) {
+        a[i] = i;
+    }
+    const Core::Array<int, 3>& c = a;
+    for(int i = 0; i < c.getLength(); i++) {
+        CORE_TEST_EQUAL(i, c[i]);
+    }
+}
+
+static void testRangeFor() {
+    Core::Array<int, 3> a;
+    for(int i = 0; i < a.getLength(); i++) {
+        a[i] = i;
+    }
+    for(int& i : a) {
+        i++;
+    }
+    for(int i = 0; i < a.getLength(); i++) {
+        CORE_TEST_EQUAL(i + 1, a[i]);
+    }
+}
+
+void Core::testArray() {
+    testToString1();
+    testToString2();
+    testReadConst();
+    testRangeFor();
+}

+ 51 - 5
tests/BitArrayTests.cpp → test/modules/BitArrayTests.cpp

@@ -1,7 +1,6 @@
-#include "tests/BitArrayTests.hpp"
-
-#include "data/BitArray.hpp"
-#include "test/Test.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/BitArray.hpp"
 
 static void testSetRead() {
     Core::BitArray bits;
@@ -107,7 +106,50 @@ static void testToString3() {
     CORE_TEST_STRING("[]", bits);
 }
 
-void Core::BitArrayTests::test() {
+static void testResizeExact() {
+    Core::BitArray bits;
+    CORE_TEST_EQUAL(0, bits.getInternalByteSize());
+    // the size in bytes matches the internal storage type
+    int elements = CORE_SIZE(int);
+    CORE_TEST_ERROR(bits.resize(elements, 8));
+    for(int i = 0; i < elements; i++) {
+        bits.set(i, i);
+    }
+    for(int i = 0; i < elements; i++) {
+        CORE_TEST_EQUAL(i, bits.get(i));
+    }
+    CORE_TEST_EQUAL(CORE_SIZE(int), bits.getInternalByteSize());
+}
+
+static void testMoveAssignment() {
+    Core::BitArray bits;
+    CORE_TEST_ERROR(bits.resize(8, 8));
+    for(int i = 0; i < bits.getLength(); i++) {
+        bits.set(i, i);
+    }
+    Core::BitArray m;
+    m = Core::move(bits);
+    CORE_TEST_EQUAL(8, m.getLength());
+    for(int i = 0; i < m.getLength(); i++) {
+        CORE_TEST_EQUAL(i, m.get(i));
+    }
+}
+
+static void testNegativeArgument() {
+    Core::BitArray bits;
+    CORE_TEST_EQUAL(Core::Error::NEGATIVE_ARGUMENT, bits.resize(-1, 5));
+    CORE_TEST_EQUAL(Core::Error::NEGATIVE_ARGUMENT, bits.resize(5, -1));
+    CORE_TEST_EQUAL(Core::Error::NEGATIVE_ARGUMENT, bits.resize(-1, -1));
+}
+
+static void testOutOfMemory() {
+    Core::BitArray bits;
+    Core::Fail::leftAllocations = 0;
+    CORE_TEST_EQUAL(Core::Error::OUT_OF_MEMORY, bits.resize(8, 4));
+    Core::Fail::leftAllocations = -1;
+}
+
+void Core::testBitArray() {
     testSetRead();
     testOutOfBoundsSetRead();
     testBigSetRead();
@@ -117,4 +159,8 @@ void Core::BitArrayTests::test() {
     testToString1();
     testToString2();
     testToString3();
+    testResizeExact();
+    testMoveAssignment();
+    testNegativeArgument();
+    testOutOfMemory();
 }

+ 3 - 5
tests/BoxTests.cpp → test/modules/BoxTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/BoxTests.hpp"
-
-#include "math/Box.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Box.hpp"
 
 static void testInit() {
     Core::Box box(Core::Vector3(1.0f, 2.0f, 3.0f));
@@ -53,7 +51,7 @@ static void testGrow() {
                      box.grow(Core::Vector3(-0.1f, -4.0f, -1.0f)));
 }
 
-void Core::BoxTests::test() {
+void Core::testBox() {
     testInit();
     testOffset();
     testCollidesWith();

+ 8 - 9
tests/BufferTests.cpp → test/modules/BufferTests.cpp

@@ -1,20 +1,19 @@
-#include "tests/BufferTests.hpp"
-
-#include "test/Test.hpp"
-#include "utils/Buffer.hpp"
+#include "../Tests.hpp"
+#include "core/utils/Buffer.hpp"
 
 static constexpr int SIZE_TYPES =
     sizeof(int) + sizeof(long) + sizeof(float) + sizeof(double);
 
-static void testAdd() {
+static void testAdd(bool light) {
     Core::Buffer buffer(10);
-    for(int i = 0; i < 100000; i++) {
+    int limit = light ? 1000 : 100000;
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_ERROR(buffer.add(5));
         CORE_TEST_ERROR(buffer.add(5L));
         CORE_TEST_ERROR(buffer.add(5.0f));
         CORE_TEST_ERROR(buffer.add(5.0));
     }
-    CORE_TEST_EQUAL(SIZE_TYPES * 100000, buffer.getLength());
+    CORE_TEST_EQUAL(SIZE_TYPES * limit, buffer.getLength());
 }
 
 static void testCopy() {
@@ -62,8 +61,8 @@ static void testMove() {
     CORE_TEST_EQUAL(SIZE_TYPES * 10, buffer2.getLength());
 }
 
-void Core::BufferTests::test() {
-    testAdd();
+void Core::testBuffer(bool light) {
+    testAdd(light);
     testCopy();
     testMoveConstruct();
     testMove();

+ 17 - 12
tests/BufferedValueTests.cpp → test/modules/BufferedValueTests.cpp

@@ -1,21 +1,24 @@
-#include "tests/BufferedValueTests.hpp"
+#include "../Tests.hpp"
+#include "core/math/BufferedValue.hpp"
+#include "core/math/Vector.hpp"
 
-#include "math/BufferedValue.hpp"
-#include "math/Vector.hpp"
-#include "test/Test.hpp"
+template class Core::BufferedValue<Core::Vector2>;
+
+using BufferedFloat = Core::BufferedValue<float>;
 
 const float eps = 0.0001f;
 
 static void testInit() {
-    Core::BufferedValue<float> b = 5.0f;
+    BufferedFloat b = 5.0f;
     CORE_TEST_FLOAT(5.0f, b.get(0.0f), eps);
     CORE_TEST_FLOAT(5.0f, b.get(0.5f), eps);
     CORE_TEST_FLOAT(5.0f, b.get(1.0f), eps);
     CORE_TEST_FLOAT(5.0f, b, eps);
+    CORE_TEST_FLOAT(5.0f, static_cast<const BufferedFloat&>(b), eps);
 }
 
 static void testInterpolate() {
-    Core::BufferedValue<float> b = 5.0f;
+    BufferedFloat b = 5.0f;
     b = 7.0f;
     CORE_TEST_FLOAT(5.0f, b.get(0.0f), eps);
     CORE_TEST_FLOAT(6.0f, b.get(0.5f), eps);
@@ -24,7 +27,7 @@ static void testInterpolate() {
 }
 
 static void testUpdate() {
-    Core::BufferedValue<float> b = 5.0f;
+    BufferedFloat b = 5.0f;
     b = 7.0f;
     b.update();
     CORE_TEST_FLOAT(7.0f, b.get(0.0f), eps);
@@ -34,7 +37,7 @@ static void testUpdate() {
 }
 
 static void testCalculate() {
-    Core::BufferedValue<float> b = 5.0f;
+    BufferedFloat b = 5.0f;
     b = 7.0f;
     b += 3.0f;
     CORE_TEST_FLOAT(5.0f, b.get(0.0f), eps);
@@ -44,9 +47,10 @@ static void testCalculate() {
     CORE_TEST_FLOAT(12.0f, b + 2.0f, eps);
 }
 
-static void testVector() {
+static void testVector2() {
     Core::Vector2 base(5.0f, 6.0f);
-    Core::BufferedValue<Core::Vector2> b = base;
+    using BufferedVector2 = Core::BufferedValue<Core::Vector2>;
+    BufferedVector2 b = base;
 
     CORE_TEST_VECTOR(base, b.get(1.0f));
     b = Core::Vector2(7.0f, 5.0);
@@ -79,12 +83,13 @@ static void testVector() {
     CORE_TEST_VECTOR(Core::Vector2(1.0f, 1.0f), b.get(1.0f));
     b[0] += 3;
     CORE_TEST_VECTOR(Core::Vector2(4.0f, 1.0f), b.get(1.0f));
+    CORE_TEST_FLOAT(4.0f, static_cast<const BufferedVector2&>(b)[0], eps);
 }
 
-void Core::BufferedValueTests::test() {
+void Core::testBufferedValue() {
     testInit();
     testInterpolate();
     testUpdate();
     testCalculate();
-    testVector();
+    testVector2();
 }

+ 15 - 8
tests/ClockTests.cpp → test/modules/ClockTests.cpp

@@ -1,7 +1,6 @@
-#include "tests/ClockTests.hpp"
-
-#include "test/Test.hpp"
-#include "utils/Clock.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/utils/Clock.hpp"
 
 static void testUpdate() {
     Core::Clock c;
@@ -35,12 +34,20 @@ static void testWait(Core::Clock::Nanos wait) {
     CORE_TEST_ERROR(c.wait(wait));
     Core::Clock::Nanos n2 = 0;
     CORE_TEST_ERROR(c.update(n2));
-    CORE_TEST_TRUE(n2 >= wait && n2 <= wait * 11 / 10);
+    CORE_TEST_TRUE(n2 >= wait && n2 <= wait * 12 / 10);
+}
+
+static void testFail() {
+    Core::Clock::Nanos n = 0;
+    Core::Fail::timeGet = true;
+    CORE_TEST_EQUAL(Core::Error::TIME_NOT_AVAILABLE, Core::Clock::getNanos(n));
+    Core::Fail::timeGet = false;
 }
 
-void Core::ClockTests::test() {
+void Core::testClock(bool light) {
     testUpdate();
     testUpdatesPerSecond();
-    testWait(50'000'000);
-    testWait(1'300'000'000);
+    testWait(light ? 5'000'000 : 50'000'000);
+    testWait(light ? 50'000'000 : 1'300'000'000);
+    testFail();
 }

+ 11 - 6
tests/ColorTests.cpp → test/modules/ColorTests.cpp

@@ -1,7 +1,10 @@
-#include "tests/ColorTests.hpp"
+#include "../Tests.hpp"
+#include "core/utils/Color.hpp"
 
-#include "test/Test.hpp"
-#include "utils/Color.hpp"
+template class Core::Color<1>;
+template class Core::Color<2>;
+template class Core::Color<3>;
+template class Core::Color<4>;
 
 const float eps = 0.0001f;
 
@@ -29,8 +32,9 @@ static void testColor3() {
     CORE_TEST_FLOAT(200.0f / 255.0f, c.asFloat(2), eps);
 }
 
+template<typename T>
 static void testColor4() {
-    Core::Color4 c(36, 100, 200, 142);
+    T c(36, 100, 200, 142);
     CORE_TEST_EQUAL(36, c[0]);
     CORE_TEST_EQUAL(100, c[1]);
     CORE_TEST_EQUAL(200, c[2]);
@@ -49,10 +53,11 @@ static void testColor4Empty() {
     CORE_TEST_EQUAL(0, c[3]);
 }
 
-void Core::ColorTests::test() {
+void Core::testColor() {
     testColor1();
     testColor2();
     testColor3();
-    testColor4();
+    testColor4<Core::Color4>();
+    testColor4<const Core::Color4>();
     testColor4Empty();
 }

+ 69 - 4
tests/ComponentsTests.cpp → test/modules/ComponentsTests.cpp

@@ -1,7 +1,8 @@
-#include "tests/ComponentsTests.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/Components.hpp"
 
-#include "data/Components.hpp"
-#include "test/Test.hpp"
+template class Core::Components<int>;
 
 using IntComponent = Core::Components<int>;
 
@@ -48,6 +49,40 @@ static void testAddForEach() {
     CORE_TEST_FALSE(pos != end);
 }
 
+static void testAddConstForEach() {
+    IntComponent c;
+    int* i1 = nullptr;
+    int* i2 = nullptr;
+    int* i3 = nullptr;
+    CORE_TEST_ERROR(c.put(i1, 1, 10));
+    CORE_TEST_ERROR(c.put(i2, 5, 20));
+    CORE_TEST_ERROR(c.put(i3, 10, 30));
+
+    auto iter = static_cast<const IntComponent&>(c).entities();
+    auto pos = iter.begin();
+    auto end = iter.end();
+
+    CORE_TEST_EQUAL(1, (*pos).entity);
+    CORE_TEST_EQUAL(10, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_EQUAL(5, (*pos).entity);
+    CORE_TEST_EQUAL(20, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_EQUAL(10, (*pos).entity);
+    CORE_TEST_EQUAL(30, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_FALSE(pos != end);
+}
+
 static void testAddComponentForEach() {
     IntComponent c;
     int* i1 = nullptr;
@@ -151,8 +186,38 @@ static void testRemove() {
     CORE_TEST_NULL(c.search(10));
 }
 
-void Core::ComponentsTests::test() {
+static void testOutOfMemory() {
+    IntComponent c;
+    int* i1 = nullptr;
+    int memFails = 0;
+    for(int i = 0; i < 40; i++) {
+        Core::Fail::leftAllocations = 2;
+        Core::Error e = c.put(i1, 1, 10);
+        if(e == Core::Error::OUT_OF_MEMORY) {
+            memFails++;
+        }
+    }
+    Core::Fail::leftAllocations = -1;
+    CORE_TEST_TRUE(memFails > 0);
+}
+
+static void testConstSearch() {
+    IntComponent c;
+    int* i = nullptr;
+    CORE_TEST_ERROR(c.put(i, 1, 10));
+    const IntComponent& cc = c;
+    const int* component = cc.search(1);
+    if(CORE_TEST_TRUE(component != nullptr)) {
+        CORE_TEST_EQUAL(10, *component);
+    }
+    CORE_TEST_NULL(cc.search(2));
+}
+
+void Core::testComponents() {
     testAddForEach();
+    testAddConstForEach();
     testAddComponentForEach();
     testRemove();
+    testOutOfMemory();
+    testConstSearch();
 }

+ 26 - 0
test/modules/ErrorTests.cpp

@@ -0,0 +1,26 @@
+#include "../Tests.hpp"
+#include "core/utils/Error.hpp"
+
+static void test(Core::Error e, const char* s) {
+    CORE_TEST_STRING(getErrorName(e), s);
+}
+
+void Core::testError() {
+    test(Error::NONE, "NONE");
+    test(Error::NEGATIVE_ARGUMENT, "NEGATIVE_ARGUMENT");
+    test(Error::CAPACITY_REACHED, "CAPACITY_REACHED");
+    test(Error::BLOCKED_STDOUT, "BLOCKED_STDOUT");
+    test(Error::OUT_OF_MEMORY, "OUT_OF_MEMORY");
+    test(Error::INVALID_CHAR, "INVALID_CHAR");
+    test(Error::NOT_FOUND, "NOT_FOUND");
+    test(Error::INVALID_STATE, "INVALID_STATE");
+    test(Error::INVALID_INDEX, "INVALID_INDEX");
+    test(Error::INVALID_ARGUMENT, "INVALID_ARGUMENT");
+    test(Error::TIME_NOT_AVAILABLE, "TIME_NOT_AVAILABLE");
+    test(Error::SLEEP_INTERRUPTED, "SLEEP_INTERRUPTED");
+    test(Error::THREAD_ERROR, "THREAD_ERROR");
+    test(Error::EXISTING_KEY, "EXISTING_KEY");
+    test(Error::CANNOT_OPEN_FILE, "CANNOT_OPEN_FILE");
+    test(Error::END_OF_FILE, "END_OF_FILE");
+    test(static_cast<Error>(200), "?");
+}

+ 52 - 6
tests/FileReaderTests.cpp → test/modules/FileReaderTests.cpp

@@ -1,9 +1,8 @@
-#include "tests/FileReaderTests.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/io/FileReader.hpp"
 
-#include "io/FileReader.hpp"
-#include "test/Test.hpp"
-
-static constexpr const char* TEST_FILE = "../src/tests/resources/test";
+static constexpr const char* TEST_FILE = "../test/modules/resources/test";
 
 static void testReadChar() {
     Core::FileReader r;
@@ -19,6 +18,22 @@ static void testReadChar() {
     CORE_TEST_STRING("abc\nBaum\n", s);
 }
 
+static void testReadCharPath() {
+    Core::FileReader r;
+    Core::Path path;
+    CORE_TEST_ERROR(path.append(TEST_FILE));
+    CORE_TEST_ERROR(r.open(path));
+    Core::String8<128> s;
+    while(true) {
+        int c = 0;
+        if(r.readChar(c) != Core::Error::NONE) {
+            break;
+        }
+        CORE_TEST_ERROR(s.append(static_cast<c32>(c)));
+    }
+    CORE_TEST_STRING("abc\nBaum\n", s);
+}
+
 static void testReadChars() {
     Core::FileReader r;
     CORE_TEST_ERROR(r.open(TEST_FILE));
@@ -63,10 +78,41 @@ static void testMoveAssignment() {
     CORE_TEST_EQUAL('a', c);
 }
 
-void Core::FileReaderTests::test() {
+static void testInvalidAccess() {
+    char buffer[4];
+    Core::FileReader r;
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE,
+                    r.readChars(buffer, sizeof(buffer)));
+    CORE_TEST_ERROR(r.open(TEST_FILE));
+    CORE_TEST_EQUAL(Core::Error::INVALID_ARGUMENT, r.readChars(buffer, -1));
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE, r.open(TEST_FILE));
+}
+
+static void testInvalidAccessPath() {
+    Core::FileReader r;
+    Core::Path path;
+    CORE_TEST_ERROR(path.append(TEST_FILE));
+    CORE_TEST_ERROR(r.open(path));
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE, r.open(path));
+}
+
+static void testCloseFail() {
+    Core::Fail::fileClose = true;
+    {
+        Core::FileReader r;
+        CORE_TEST_ERROR(r.open(TEST_FILE));
+    }
+    Core::Fail::fileClose = false;
+}
+
+void Core::testFileReader() {
     testReadChar();
+    testReadCharPath();
     testReadChars();
     testReadCharsOverflow();
     testMoveConstruct();
     testMoveAssignment();
+    testInvalidAccess();
+    testInvalidAccessPath();
+    testCloseFail();
 }

+ 13 - 5
tests/FrustumTests.cpp → test/modules/FrustumTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/FrustumTests.hpp"
-
-#include "math/Frustum.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Frustum.hpp"
 
 using V3 = Core::Vector3;
 
@@ -48,8 +46,18 @@ static void testSphereIsInside() {
     CORE_TEST_TRUE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 50.0f));
 }
 
-void Core::FrustumTests::test() {
+static void testUpdateProjection() {
+    Core::Frustum f(60.0f, 0.1f, 1000.0f);
+    CORE_TEST_STRING("[[1.30, 0.00, 0.00, 0.00], "
+                     "[0.00, 1.73, 0.00, 0.00], "
+                     "[0.00, 0.00, -1.00, -0.20], "
+                     "[0.00, 0.00, -1.00, 0.00]]",
+                     f.updateProjection(Core::IntVector2(400, 300)));
+}
+
+void Core::testFrustum() {
     testToString();
     testPointIsInside();
     testSphereIsInside();
+    testUpdateProjection();
 }

+ 45 - 24
tests/HashMapTests.cpp → test/modules/HashMapTests.cpp

@@ -1,8 +1,8 @@
-#include "tests/HashMapTests.hpp"
-
-#include "data/HashMap.hpp"
-#include "test/Test.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/HashMap.hpp"
 
+template class Core::HashMap<int, int>;
 using IntMap = Core::HashMap<int, int>;
 
 static void testAdd() {
@@ -62,30 +62,31 @@ static void testClear() {
     CORE_TEST_FALSE(map.contains(4));
 }
 
-static void testOverflow() {
+static void testOverflow(bool light) {
     IntMap map;
-    for(int i = 0; i < 100000; i++) {
+    int limit = light ? 10000 : 100000;
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_ERROR(map.add(i, i));
     }
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_TRUE(map.contains(i));
     }
 }
 
-struct A final {
+struct HashMapTestStruct final {
     int a;
     int b;
 
-    A(int a_, int b_) : a(a_), b(b_) {
+    HashMapTestStruct(int a_, int b_) : a(a_), b(b_) {
     }
 
     // none of these should be needed for the hashmap
-    A(const A&) = delete;
-    A(A&&) = delete;
-    A& operator=(const A&) = delete;
-    A& operator=(A&&) = delete;
+    HashMapTestStruct(const HashMapTestStruct&) = delete;
+    HashMapTestStruct(HashMapTestStruct&&) = delete;
+    HashMapTestStruct& operator=(const HashMapTestStruct&) = delete;
+    HashMapTestStruct& operator=(HashMapTestStruct&&) = delete;
 
-    bool operator==(const A& other) const {
+    bool operator==(const HashMapTestStruct& other) const {
         return a == other.a && b == other.b;
     }
 
@@ -101,27 +102,27 @@ struct A final {
 };
 
 static void testEmplace() {
-    Core::HashMap<int, A> map;
+    Core::HashMap<int, HashMapTestStruct> map;
 
-    A* ar = nullptr;
+    HashMapTestStruct* ar = nullptr;
     CORE_TEST_ERROR(map.tryEmplace(ar, 0, 3, 4));
     CORE_TEST_ERROR(map.tryEmplace(ar, 3, 4, 5));
     CORE_TEST_ERROR(map.tryEmplace(ar, 20, 5, 6));
     CORE_TEST_EQUAL(Core::Error::EXISTING_KEY, map.tryEmplace(ar, 3, 6, 7));
     CORE_TEST_EQUAL(Core::Error::EXISTING_KEY, map.tryEmplace(ar, 20, 7, 8));
 
-    A* a = map.search(0);
-    A* b = map.search(3);
-    A* c = map.search(20);
+    HashMapTestStruct* a = map.search(0);
+    HashMapTestStruct* b = map.search(3);
+    HashMapTestStruct* c = map.search(20);
 
     CORE_TEST_NOT_NULL(a);
     CORE_TEST_NOT_NULL(b);
     CORE_TEST_NOT_NULL(c);
 
     if(a != nullptr && b != nullptr && c != nullptr) {
-        CORE_TEST_EQUAL(A(3, 4), *a);
-        CORE_TEST_EQUAL(A(4, 5), *b);
-        CORE_TEST_EQUAL(A(5, 6), *c);
+        CORE_TEST_EQUAL(HashMapTestStruct(3, 4), *a);
+        CORE_TEST_EQUAL(HashMapTestStruct(4, 5), *b);
+        CORE_TEST_EQUAL(HashMapTestStruct(5, 6), *c);
     }
 }
 
@@ -312,13 +313,32 @@ static void testTypes() {
     testType<unsigned long long>();
 }
 
-void Core::HashMapTests::test() {
+static void testOutOfMemory() {
+    IntMap map;
+    int memFails = 0;
+    for(int i = 0; i < 40; i++) {
+        Core::Fail::leftAllocations = 2;
+        int* v = nullptr;
+        Core::Error e = map.put(v, 1, 1);
+        if(e == Core::Error::OUT_OF_MEMORY) {
+            memFails++;
+        }
+    }
+    int* found = map.search(1);
+    if(CORE_TEST_NOT_NULL(found)) {
+        CORE_TEST_EQUAL(1, *found);
+    }
+    Core::Fail::leftAllocations = -1;
+    CORE_TEST_TRUE(memFails > 0);
+}
+
+void Core::testHashMap(bool light) {
     testAdd();
     testMultipleAdd();
     testSearch();
     testAddReplace();
     testClear();
-    testOverflow();
+    testOverflow(light);
     testEmplace();
     testToString1();
     testToString2();
@@ -331,4 +351,5 @@ void Core::HashMapTests::test() {
     testKeyForEach();
     testValueForEach();
     testTypes();
+    testOutOfMemory();
 }

+ 29 - 10
tests/LinkedListTests.cpp → test/modules/LinkedListTests.cpp

@@ -1,7 +1,9 @@
-#include "tests/LinkedListTests.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/LinkedList.hpp"
 
-#include "data/LinkedList.hpp"
-#include "test/Test.hpp"
+template class Core::LinkedList<int>;
+using IntList = Core::LinkedList<int>;
 
 struct LinkedListTester final {
     int a;
@@ -13,8 +15,6 @@ struct LinkedListTester final {
     LinkedListTester& operator=(LinkedListTester&&) = delete;
 };
 
-using IntList = Core::LinkedList<int>;
-
 static void testWithoutCopyOrMove() {
     Core::LinkedList<LinkedListTester> list;
     CORE_TEST_ERROR(list.add(3));
@@ -51,9 +51,10 @@ static void testClear() {
     CORE_TEST_FALSE(list.begin() != list.end());
 }
 
-static void testBigAdd() {
+static void testBigAdd(bool light) {
+    int limit = light ? 10000 : 100000;
     IntList list;
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_ERROR(list.add(i));
     }
     auto iter = list.begin();
@@ -61,7 +62,7 @@ static void testBigAdd() {
         CORE_TEST_EQUAL(i, *iter);
         ++iter;
     }
-    CORE_TEST_EQUAL(100000, list.getLength());
+    CORE_TEST_EQUAL(limit, list.getLength());
 }
 
 static void testCopy() {
@@ -273,12 +274,29 @@ static void testRemoveLast() {
     CORE_TEST_EQUAL(0, list.getLength());
 }
 
-void Core::LinkedListTests::test() {
+static void testOutOfMemory() {
+    IntList list;
+    int memFails = 0;
+    for(int i = 0; i < 40; i++) {
+        Core::Fail::leftAllocations = i;
+        Core::Error e = list.add(1);
+        if(e == Core::Error::OUT_OF_MEMORY) {
+            memFails++;
+        }
+    }
+    if(CORE_TEST_TRUE(list.getLength() > 0)) {
+        CORE_TEST_EQUAL(1, *list.begin());
+    }
+    Core::Fail::leftAllocations = -1;
+    CORE_TEST_TRUE(memFails > 0);
+}
+
+void Core::testLinkedList(bool light) {
     testWithoutCopyOrMove();
     testAdd();
     testMultipleAdd();
     testClear();
-    testBigAdd();
+    testBigAdd(light);
     testCopy();
     testMove();
     testMoveAssignment();
@@ -288,4 +306,5 @@ void Core::LinkedListTests::test() {
     testRemove();
     testRemoveFirst();
     testRemoveLast();
+    testOutOfMemory();
 }

+ 39 - 9
tests/ListTests.cpp → test/modules/ListTests.cpp

@@ -1,8 +1,8 @@
-#include "tests/ListTests.hpp"
-
-#include "data/List.hpp"
-#include "test/Test.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/List.hpp"
 
+template class Core::List<int>;
 using IntList = Core::List<int>;
 
 static void testAdd() {
@@ -54,15 +54,16 @@ static void testShrink() {
     CORE_TEST_EQUAL(3, list[2]);
 }
 
-static void testBigAdd() {
+static void testBigAdd(bool light) {
+    int limit = light ? 10000 : 100000;
     IntList list;
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_ERROR(list.add(i));
     }
     for(int i = 0; i < list.getLength(); i++) {
         CORE_TEST_EQUAL(i, list[i]);
     }
-    CORE_TEST_EQUAL(100000, list.getLength());
+    CORE_TEST_EQUAL(limit, list.getLength());
 }
 
 static void testCopy() {
@@ -183,13 +184,38 @@ static void testDefaultResize() {
     }
 }
 
-void Core::ListTests::test() {
+static void testInvalidReserve() {
+    IntList list;
+    CORE_TEST_ERROR(list.reserve(0));
+    CORE_TEST_ERROR(list.reserve(-5));
+}
+
+static void testShrinkExact() {
+    IntList list;
+    CORE_TEST_ERROR(list.resize(50));
+    CORE_TEST_ERROR(list.shrink());
+}
+
+static void testShrinkResize() {
+    IntList list;
+    CORE_TEST_ERROR(list.resize(50));
+    CORE_TEST_ERROR(list.resize(20, 5));
+    CORE_TEST_ERROR(list.resize(10));
+}
+
+static void testCopyEmpty() {
+    IntList list;
+    IntList copy;
+    CORE_TEST_ERROR(copy.copyFrom(list));
+}
+
+void Core::testList(bool light) {
     testAdd();
     testMultipleAdd();
     testAddReplace();
     testClear();
     testShrink();
-    testBigAdd();
+    testBigAdd(light);
     testCopy();
     testMove();
     testMoveAssignment();
@@ -200,4 +226,8 @@ void Core::ListTests::test() {
     testRemove();
     testResize();
     testDefaultResize();
+    testInvalidReserve();
+    testShrinkExact();
+    testShrinkResize();
+    testCopyEmpty();
 }

+ 3 - 5
tests/MathTests.cpp → test/modules/MathTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/MathTests.hpp"
-
-#include "math/Math.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Math.hpp"
 
 constexpr float eps = 0.0001f;
 
@@ -93,7 +91,7 @@ static void testDegreeToRadian() {
     CORE_TEST_FLOAT(CMath::PI * 2.0f, CMath::degreeToRadian(360.0f), eps);
 }
 
-void Core::MathTests::test() {
+void Core::testMath() {
     testInterpolate();
     testIsPowerOf2();
     testRoundUpLog2();

+ 24 - 8
tests/MatrixStackTests.cpp → test/modules/MatrixStackTests.cpp

@@ -1,8 +1,7 @@
-#include "tests/MatrixStackTests.hpp"
-
-#include "math/MatrixStack.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/MatrixStack.hpp"
 
+template class Core::MatrixStack<5>;
 using Matrices = Core::MatrixStack<5>;
 
 static void testInit() {
@@ -24,12 +23,13 @@ static void testPop() {
     }
 }
 
-static void testPush() {
+static void testPush(bool light) {
     Matrices stack;
     for(int i = 0; i < 4; i++) {
         CORE_TEST_ERROR(stack.push());
     }
-    for(int i = 0; i < 1000000; i++) {
+    int limit = light ? 10000 : 1000000;
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_EQUAL(Core::Error::CAPACITY_REACHED, stack.push());
     }
 }
@@ -99,12 +99,28 @@ static void testToString3() {
                      stack);
 }
 
-void Core::MatrixStackTests::test() {
+static void testInvalidPop() {
+    Matrices stack;
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE, stack.pop());
+}
+
+static void testConstPeek() {
+    const Matrices stack;
+    CORE_TEST_STRING("[[1.00, 0.00, 0.00, 0.00], "
+                     "[0.00, 1.00, 0.00, 0.00], "
+                     "[0.00, 0.00, 1.00, 0.00], "
+                     "[0.00, 0.00, 0.00, 1.00]]",
+                     stack.peek());
+}
+
+void Core::testMatrixStack(bool light) {
     testInit();
     testPop();
-    testPush();
+    testPush(light);
     testClear();
     testToString1();
     testToString2();
     testToString3();
+    testInvalidPop();
+    testConstPeek();
 }

+ 3 - 5
tests/MatrixTests.cpp → test/modules/MatrixTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/MatrixTests.hpp"
-
-#include "math/Matrix.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Matrix.hpp"
 
 using V3 = Core::Vector3;
 
@@ -159,7 +157,7 @@ static void testQuaternionMatrix() {
     }
 }
 
-void Core::MatrixTests::test() {
+void Core::testMatrix() {
     testInit();
     testScale();
     testUniformScale();

+ 26 - 0
test/modules/NewTests.cpp

@@ -0,0 +1,26 @@
+#include "../Tests.hpp"
+
+struct NewTestClass {
+    int i;
+};
+
+static void testNewArray() {
+    NewTestClass* n = new NewTestClass[5];
+    delete[] n;
+}
+
+static void testNewFlat() {
+    NewTestClass* n = new NewTestClass();
+    delete n;
+}
+
+static void testNewArrayWithSize() {
+    NewTestClass* n = new NewTestClass[5];
+    operator delete[](n, 5);
+}
+
+void Core::testNew() {
+    testNewArray();
+    testNewFlat();
+    testNewArrayWithSize();
+}

+ 3 - 5
tests/PlaneTests.cpp → test/modules/PlaneTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/PlaneTests.hpp"
-
-#include "math/Plane.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Plane.hpp"
 
 const float eps = 0.0001f;
 
@@ -32,7 +30,7 @@ static void testSignedDistance() {
                     eps);
 }
 
-void Core::PlaneTests::test() {
+void Core::testPlane() {
     testToString1();
     testToString2();
     testSignedDistance();

+ 70 - 27
tests/ProbingHashMapTests.cpp → test/modules/ProbingHashMapTests.cpp

@@ -1,10 +1,8 @@
-#include "tests/ProbingHashMapTests.hpp"
-
-#include <stdio.h>
-
-#include "data/ProbingHashMap.hpp"
-#include "test/Test.hpp"
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/data/ProbingHashMap.hpp"
 
+template class Core::ProbingHashMap<int, int>;
 using IntMap = Core::ProbingHashMap<int, int>;
 
 static void testAdd() {
@@ -41,6 +39,10 @@ static void testMultipleAdd() {
 static void testSearch() {
     IntMap map;
     CORE_TEST_NULL(map.search(6));
+    CORE_TEST_ERROR(map.add(5, 4));
+    CORE_TEST_ERROR(map.add(10, 3));
+    CORE_TEST_ERROR(map.add(15, 2));
+    CORE_TEST_NULL(map.search(6));
 }
 
 static void testAddReplace() {
@@ -64,42 +66,45 @@ static void testClear() {
     CORE_TEST_FALSE(map.contains(4));
 }
 
-static void testOverflow() {
+static void testOverflow(bool light) {
+    int limit = light ? 10000 : 100000;
     IntMap map;
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_ERROR(map.add(i, i));
     }
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < limit; i++) {
         CORE_TEST_TRUE(map.contains(i));
     }
 }
 
 static int aInstances = 0;
 
-struct A final {
+struct ProbingHashMapTestStruct final {
     int a;
     int b;
 
-    A(int a_, int b_) : a(a_), b(b_) {
+    ProbingHashMapTestStruct(int a_, int b_) : a(a_), b(b_) {
         aInstances++;
     }
 
-    A(const A& o) : a(o.a), b(o.b) {
+    ProbingHashMapTestStruct(const ProbingHashMapTestStruct& o)
+        : a(o.a), b(o.b) {
         aInstances++;
     }
 
-    A(A&& o) : a(o.a), b(o.b) {
+    ProbingHashMapTestStruct(ProbingHashMapTestStruct&& o) : a(o.a), b(o.b) {
         aInstances++;
     }
 
-    ~A() {
+    ~ProbingHashMapTestStruct() {
         aInstances--;
     }
 
-    A& operator=(const A& o) = default;
-    A& operator=(A&& o) = default;
+    ProbingHashMapTestStruct&
+    operator=(const ProbingHashMapTestStruct& o) = default;
+    ProbingHashMapTestStruct& operator=(ProbingHashMapTestStruct&& o) = default;
 
-    bool operator==(const A& other) const {
+    bool operator==(const ProbingHashMapTestStruct& other) const {
         return a == other.a && b == other.b;
     }
 
@@ -116,9 +121,9 @@ struct A final {
 
 static void testEmplace() {
     {
-        Core::ProbingHashMap<int, A> map;
+        Core::ProbingHashMap<int, ProbingHashMapTestStruct> map;
 
-        A* ar = nullptr;
+        ProbingHashMapTestStruct* ar = nullptr;
         CORE_TEST_ERROR(map.tryEmplace(ar, 0, 3, 4));
         CORE_TEST_ERROR(map.tryEmplace(ar, 3, 4, 5));
         CORE_TEST_ERROR(map.tryEmplace(ar, 20, 5, 6));
@@ -126,18 +131,18 @@ static void testEmplace() {
         CORE_TEST_EQUAL(Core::Error::EXISTING_KEY,
                         map.tryEmplace(ar, 20, 7, 8));
 
-        A* a = map.search(0);
-        A* b = map.search(3);
-        A* c = map.search(20);
+        ProbingHashMapTestStruct* a = map.search(0);
+        ProbingHashMapTestStruct* b = map.search(3);
+        ProbingHashMapTestStruct* c = map.search(20);
 
         CORE_TEST_NOT_NULL(a);
         CORE_TEST_NOT_NULL(b);
         CORE_TEST_NOT_NULL(c);
 
         if(a != nullptr && b != nullptr && c != nullptr) {
-            CORE_TEST_EQUAL(A(3, 4), *a);
-            CORE_TEST_EQUAL(A(4, 5), *b);
-            CORE_TEST_EQUAL(A(5, 6), *c);
+            CORE_TEST_EQUAL(ProbingHashMapTestStruct(3, 4), *a);
+            CORE_TEST_EQUAL(ProbingHashMapTestStruct(4, 5), *b);
+            CORE_TEST_EQUAL(ProbingHashMapTestStruct(5, 6), *c);
         }
     }
     CORE_TEST_EQUAL(0, aInstances);
@@ -307,13 +312,48 @@ static void testTypes() {
     testType<unsigned long long>();
 }
 
-void Core::ProbingHashMapTests::test() {
+static void testOutOfMemory() {
+    IntMap map;
+    int memFails = 0;
+    for(int i = 0; i < 40; i++) {
+        Core::Fail::leftAllocations = i;
+        int* v = nullptr;
+        Core::Error e = map.put(v, 1, 1);
+        if(e == Core::Error::OUT_OF_MEMORY) {
+            memFails++;
+        }
+    }
+    int* found = map.search(1);
+    if(CORE_TEST_NOT_NULL(found)) {
+        CORE_TEST_EQUAL(1, *found);
+    }
+    Core::Fail::leftAllocations = -1;
+    CORE_TEST_TRUE(memFails > 0);
+}
+
+static void testInsertInvalid() {
+    IntMap map;
+    int* v;
+    CORE_TEST_EQUAL(Core::Error::INVALID_ARGUMENT,
+                    map.tryEmplace(v, Core::emptyValue<int>(), 2));
+    CORE_TEST_EQUAL(Core::Error::INVALID_ARGUMENT,
+                    map.put(v, Core::emptyValue<int>(), 2));
+}
+
+static void testAddCollisions() {
+    IntMap map;
+    for(int i = 0; i < 8; i++) {
+        CORE_TEST_ERROR(map.add(i * 16, i));
+    }
+}
+
+void Core::testProbingHashMap(bool light) {
     testAdd();
     testMultipleAdd();
     testSearch();
     testAddReplace();
     testClear();
-    testOverflow();
+    testOverflow(light);
     testEmplace();
     testToString1();
     testToString2();
@@ -325,4 +365,7 @@ void Core::ProbingHashMapTests::test() {
     testKeyForEach();
     testValueForEach();
     testTypes();
+    testOutOfMemory();
+    testInsertInvalid();
+    testAddCollisions();
 }

+ 3 - 5
tests/QuaternionTests.cpp → test/modules/QuaternionTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/QuaternionTests.hpp"
-
-#include "math/Quaternion.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Quaternion.hpp"
 
 using V3 = Core::Vector3;
 
@@ -61,7 +59,7 @@ static void testMulVector() {
     CORE_TEST_VECTOR(V3(0.0f, 0.0f, 1.0f), q3 * v3);
 }
 
-void Core::QuaternionTests::test() {
+void Core::testQuaternion() {
     testInit();
     testAxisAndDegreesInit();
     testLerp();

+ 90 - 0
test/modules/RandomTests.cpp

@@ -0,0 +1,90 @@
+#include "../Tests.hpp"
+#include "core/data/Array.hpp"
+#include "core/utils/Random.hpp"
+
+static void testAverage(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    float sum = 0;
+    for(int i = 0; i < limit; i++) {
+        sum += static_cast<float>(r.next(2, 10));
+    }
+    sum /= static_cast<float>(limit);
+    CORE_TEST_FLOAT(6.0f, sum, 0.01f);
+}
+
+static void testCoin(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    Core::Array<int, 2> c(0);
+    for(int i = 0; i < limit; i++) {
+        c[r.nextBool()]++;
+    }
+    CORE_TEST_FLOAT(0.0f,
+                    static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
+                    0.003f);
+}
+
+static void testDistribution(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    Core::Array<int, 102> c(0);
+    for(int i = 0; i < limit; i++) {
+        c[r.next(1, c.getLength() - 2)]++;
+    }
+    CORE_TEST_EQUAL(0, c[0]);
+    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
+    for(int i = 1; i < c.getLength() - 1; i++) {
+        CORE_TEST_FLOAT(0.01f,
+                        static_cast<float>(c[i]) / static_cast<float>(limit),
+                        0.001f);
+    }
+}
+
+static void testFloatAverage(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    float sum = 0;
+    for(int i = 0; i < limit; i++) {
+        sum += r.nextFloat();
+    }
+    sum /= static_cast<float>(limit);
+    CORE_TEST_FLOAT(0.5f, sum, 0.001f);
+}
+
+static void testFloatCoin(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    Core::Array<int, 2> c(0);
+    for(int i = 0; i < limit; i++) {
+        c[r.nextFloat() < 0.5f]++;
+    }
+    CORE_TEST_FLOAT(0.0f,
+                    static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
+                    0.003f);
+}
+
+static void testFloatDistribution(bool light) {
+    int limit = light ? 100000 : 1000000;
+    Core::Random r(553);
+    Core::Array<int, 102> c(0);
+    for(int i = 0; i < limit; i++) {
+        c[static_cast<int>(r.nextFloat(1.0f, c.getLength() - 1))]++;
+    }
+    CORE_TEST_EQUAL(0, c[0]);
+    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
+    for(int i = 1; i < c.getLength() - 1; i++) {
+        CORE_TEST_FLOAT(0.01f,
+                        static_cast<float>(c[i]) / static_cast<float>(limit),
+                        0.003f);
+    }
+}
+
+void Core::testRandom(bool light) {
+    testAverage(light);
+    testCoin(light);
+    testDistribution(light);
+    testFloatAverage(light);
+    testFloatCoin(light);
+    testFloatDistribution(light);
+}

+ 10 - 4
tests/RingBufferTests.cpp → test/modules/RingBufferTests.cpp

@@ -1,7 +1,7 @@
-#include "tests/RingBufferTests.hpp"
+#include "../Tests.hpp"
+#include "core/data/RingBuffer.hpp"
 
-#include "data/RingBuffer.hpp"
-#include "test/Test.hpp"
+template class Core::RingBuffer<int, 5>;
 
 struct Tester final {
     static int ids;
@@ -258,7 +258,12 @@ static void testOverall() {
     CORE_TEST_EQUAL(0, Tester::sum);
 }
 
-void Core::RingBufferTests::test() {
+static void testInvalidRemove() {
+    Core::RingBuffer<int, 3> buffer;
+    CORE_TEST_EQUAL(Core::Error::INVALID_STATE, buffer.remove());
+}
+
+void Core::testRingBuffer() {
     testReadAndWrite();
     testOverflow();
     testRefill();
@@ -269,4 +274,5 @@ void Core::RingBufferTests::test() {
     testMoveDestruct();
     testMoveAssignmentDestruct();
     testOverall();
+    testInvalidRemove();
 }

+ 23 - 8
tests/StackTests.cpp → test/modules/StackTests.cpp

@@ -1,7 +1,8 @@
-#include "tests/StackTests.hpp"
+#include "../Tests.hpp"
+#include "core/data/Stack.hpp"
 
-#include "data/Stack.hpp"
-#include "test/Test.hpp"
+template class Core::Internal::BaseStack<int, Core::List<int>>;
+template class Core::Internal::BaseStack<int, Core::ArrayList<int, 100>>;
 
 template<typename T>
 static void testPushPopPeek() {
@@ -10,10 +11,13 @@ static void testPushPopPeek() {
     CORE_TEST_ERROR(stack.push(2));
     CORE_TEST_ERROR(stack.push(3));
     CORE_TEST_EQUAL(3, stack.peek());
+    CORE_TEST_EQUAL(3, static_cast<const T&>(stack).peek());
     CORE_TEST_ERROR(stack.pop());
     CORE_TEST_EQUAL(2, stack.peek());
+    CORE_TEST_EQUAL(2, static_cast<const T&>(stack).peek());
     CORE_TEST_ERROR(stack.pop());
     CORE_TEST_EQUAL(1, stack.peek());
+    CORE_TEST_EQUAL(1, static_cast<const T&>(stack).peek());
     CORE_TEST_ERROR(stack.pop());
     CORE_TEST_TRUE(stack.isEmpty());
 }
@@ -53,13 +57,23 @@ static void testToString3() {
 }
 
 template<typename T>
-static void testPop() {
+static void testPop(int amount) {
     T stack;
-    for(int i = 0; i < 100000; i++) {
+    for(int i = 0; i < amount; i++) {
         CORE_TEST_EQUAL(Core::Error::INVALID_INDEX, stack.pop());
     }
 }
 
+template<typename T>
+static void testClear() {
+    T stack;
+    CORE_TEST_ERROR(stack.push(1));
+    CORE_TEST_ERROR(stack.push(2));
+    CORE_TEST_ERROR(stack.push(3));
+    stack.clear();
+    CORE_TEST_TRUE(stack.isEmpty());
+}
+
 template<typename T>
 static void testType(int amount) {
     testPushPopPeek<T>();
@@ -67,10 +81,11 @@ static void testType(int amount) {
     testToString1<T>();
     testToString2<T>();
     testToString3<T>();
-    testPop<T>();
+    testPop<T>(amount);
+    testClear<T>();
 }
 
-void Core::StackTests::test() {
-    testType<Core::ListStack<int>>(100000);
+void Core::testStack(bool light) {
+    testType<Core::ListStack<int>>(light ? 10000 : 100000);
     testType<Core::ArrayStack<int, 100>>(100);
 }

+ 87 - 0
test/modules/ThreadTests.cpp

@@ -0,0 +1,87 @@
+#include "../Tests.hpp"
+#include "core/thread/Thread.hpp"
+
+static int runDone = 0;
+
+struct IntHolder {
+    int value;
+};
+
+static int run(void*) {
+    runDone = 1;
+    return 7;
+}
+
+static void testStart() {
+    runDone = 0;
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start(run, nullptr));
+    int returnValue = 0;
+    CORE_TEST_ERROR(t.join(&returnValue));
+    CORE_TEST_EQUAL(1, runDone);
+    CORE_TEST_EQUAL(7, returnValue);
+}
+
+static void testLambda() {
+    IntHolder i(0);
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start(
+        [](void* p) {
+            IntHolder* ip = static_cast<IntHolder*>(p);
+            ip->value = 2;
+            return 0;
+        },
+        &i));
+    CORE_TEST_ERROR(t.join(nullptr));
+    CORE_TEST_EQUAL(2, i.value);
+}
+
+static void testJoinWithoutStart() {
+    Core::Thread t;
+    CORE_TEST_EQUAL(Core::Error::THREAD_ERROR, t.join(nullptr));
+}
+
+static void testAutoJoin() {
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
+}
+
+static void testMove() {
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
+    Core::Thread m = Core::move(t);
+    CORE_TEST_ERROR(m.join());
+}
+
+static void testMoveAssignment() {
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
+    Core::Thread m;
+    m = Core::move(t);
+    CORE_TEST_ERROR(m.join());
+}
+
+static void testMoveIntoActive() {
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
+    Core::Thread m;
+    t = Core::move(m);
+}
+
+static void testDoubleJoin() {
+    Core::Thread t;
+    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
+    CORE_TEST_ERROR(t.join(nullptr));
+    CORE_TEST_EQUAL(Core::Error::THREAD_ERROR, t.join(nullptr));
+}
+
+void Core::testThread() {
+    testStart();
+    testLambda();
+    testJoinWithoutStart();
+    testAutoJoin();
+    testMove();
+    testMoveAssignment();
+    testMoveIntoActive();
+    testDoubleJoin();
+}

+ 75 - 0
test/modules/UniquePointerTests.cpp

@@ -0,0 +1,75 @@
+#include "../Tests.hpp"
+#include "core/utils/UniquePointer.hpp"
+
+struct B final {
+    static int instances;
+
+    bool b = false;
+
+    B() {
+        instances++;
+    }
+
+    ~B() {
+        instances--;
+    }
+
+    void test() {
+        b = true;
+    }
+};
+
+int B::instances = 0;
+
+template class Core::UniquePointer<B>;
+using UniqueB = Core::UniquePointer<B>;
+
+static void testDestroy() {
+    {
+        UniqueB p(new B());
+        CORE_TEST_EQUAL(1, B::instances);
+    }
+    CORE_TEST_EQUAL(0, B::instances);
+}
+
+static void testMoveConstructDestroys() {
+    UniqueB p1(new B());
+    CORE_TEST_EQUAL(1, B::instances);
+    UniqueB p2(Core::move(p1));
+    CORE_TEST_EQUAL(1, B::instances);
+    p2 = nullptr;
+    CORE_TEST_EQUAL(0, B::instances);
+}
+
+static void testMoveDestroys() {
+    {
+        UniqueB p1(new B());
+        UniqueB p2(new B());
+        CORE_TEST_EQUAL(2, B::instances);
+        p1 = Core::move(p2);
+        CORE_TEST_EQUAL(1, B::instances);
+    }
+    CORE_TEST_EQUAL(0, B::instances);
+}
+
+static void testEmpty() {
+    UniqueB p;
+    CORE_TEST_TRUE(p == nullptr);
+    CORE_TEST_TRUE(static_cast<const UniqueB&>(p) == nullptr);
+}
+
+static void testCall() {
+    UniqueB p(new B());
+    CORE_TEST_FALSE(p->b);
+    p->test();
+    CORE_TEST_TRUE(p->b);
+    CORE_TEST_TRUE(static_cast<const UniqueB&>(p)->b);
+}
+
+void Core::testUniquePointer() {
+    testDestroy();
+    testMoveConstructDestroys();
+    testMoveDestroys();
+    testEmpty();
+    testCall();
+}

+ 54 - 0
test/modules/UtilityTests.cpp

@@ -0,0 +1,54 @@
+#include "../../src/ErrorSimulator.hpp"
+#include "../Tests.hpp"
+#include "core/utils/Utility.hpp"
+
+static void testPopCount() {
+    CORE_TEST_EQUAL(4, Core::popCount(0xF));
+    CORE_TEST_EQUAL(0, Core::popCount(0x0));
+    CORE_TEST_EQUAL(2, Core::popCount(0x6));
+    CORE_TEST_EQUAL(7, Core::popCount(0x7F));
+    CORE_TEST_EQUAL(3, Core::popCount(0x2A));
+    CORE_TEST_EQUAL(32, Core::popCount(0xFFFFFFFF));
+    CORE_TEST_EQUAL(64, Core::popCount(0xFFFFFFFFFFFFFFFFL));
+    CORE_TEST_EQUAL(44, Core::popCount(0xFFFF0FFFFFFF));
+    CORE_TEST_EQUAL(32, Core::popCount(-1));
+}
+
+static void testIf() {
+    CORE_TEST_TRUE((Core::IsSame<Core::If<true, int, double>, int>));
+    CORE_TEST_TRUE((Core::IsSame<Core::If<false, int, double>, double>));
+}
+
+static void testNegativeArguments() {
+    char from[16];
+    Core::memorySet(from, 1, sizeof(from));
+    Core::memorySet(from, 0, -1);
+    char to[16];
+    Core::memorySet(to, 1, sizeof(to));
+    Core::memoryCopy(to, from, -1);
+    CORE_TEST_TRUE(Core::memoryCompare(from, to, sizeof(from)));
+}
+
+static void testNegativeRellocate() {
+    char* buffer = nullptr;
+    CORE_TEST_ERROR(Core::reallocate(buffer, 16));
+    CORE_TEST_TRUE(buffer != nullptr);
+    CORE_TEST_ERROR(Core::reallocate(buffer, -1));
+    CORE_TEST_TRUE(buffer == nullptr);
+}
+
+static void testReallocateFail() {
+    char* buffer = nullptr;
+    Core::Fail::realloc = true;
+    CORE_TEST_EQUAL(Core::Error::OUT_OF_MEMORY, Core::reallocate(buffer, 16));
+    CORE_TEST_TRUE(buffer == nullptr);
+    Core::Fail::realloc = false;
+}
+
+void Core::testUtility() {
+    testPopCount();
+    testIf();
+    testNegativeArguments();
+    testNegativeRellocate();
+    testReallocateFail();
+}

+ 17 - 5
tests/VectorTests.cpp → test/modules/VectorTests.cpp

@@ -1,13 +1,18 @@
-#include "tests/VectorTests.hpp"
-
-#include "math/Vector.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/Vector.hpp"
 
 const float eps = 0.0001f;
 
 using V3 = Core::Vector3;
 using I3 = Core::IntVector3;
 
+template class Core::Vector<4, float>;
+template class Core::Vector<4, int>;
+template class Core::Vector<3, float>;
+template class Core::Vector<3, int>;
+template class Core::Vector<2, float>;
+template class Core::Vector<2, int>;
+
 static void testInitAndRead() {
     V3 v1;
     Core::Vector2 v2(1.0f, 2.0f);
@@ -192,7 +197,13 @@ static void testToString() {
     CORE_TEST_EQUAL(s2, s);
 }
 
-void Core::VectorTests::test() {
+static void testNormalizeIntVector() {
+    I3 i(1, 2, 3);
+    i.normalize();
+    CORE_TEST_VECTOR(I3(0, 0, 1), i);
+}
+
+void Core::testVector() {
     testInitAndRead();
     testSetAngles();
     testCross();
@@ -213,4 +224,5 @@ void Core::VectorTests::test() {
     testNormalize();
     testCast();
     testToString();
+    testNormalizeIntVector();
 }

+ 13 - 5
tests/ViewTests.cpp → test/modules/ViewTests.cpp

@@ -1,7 +1,5 @@
-#include "tests/ViewTests.hpp"
-
-#include "math/View.hpp"
-#include "test/Test.hpp"
+#include "../Tests.hpp"
+#include "core/math/View.hpp"
 
 using V3 = Core::Vector3;
 
@@ -27,7 +25,17 @@ static void testFromQuaternion() {
     CORE_TEST_VECTOR(V3(-1.0f, 0.0f, 0.0f), v.getBack());
 }
 
-void Core::ViewTests::test() {
+static void testUpdateMatrix() {
+    Core::View v;
+    CORE_TEST_STRING("[[0.00, 0.00, 0.00, 0.00], "
+                     "[0.00, 0.00, 0.00, 0.00], "
+                     "[0.00, 0.00, 0.00, 0.00], "
+                     "[0.00, 0.00, 0.00, 1.00]]",
+                     v.updateMatrix(Core::Vector3(1.0f, 2.0f, 3.0f)));
+}
+
+void Core::testView() {
     testFromAngles();
     testFromQuaternion();
+    testUpdateMatrix();
 }

+ 0 - 0
tests/resources/test → test/modules/resources/test


+ 0 - 8
tests/ArrayListTests.hpp

@@ -1,8 +0,0 @@
-#ifndef CORE_ARRAY_LIST_TESTS_HPP
-#define CORE_ARRAY_LIST_TESTS_HPP
-
-namespace Core::ArrayListTests {
-    void test();
-}
-
-#endif

Some files were not shown because too many files changed in this diff