Browse Source

Refactor to use modules

Kajetan Johannes Hammerle 1 tuần trước cách đây
mục cha
commit
40d01586cc
93 tập tin đã thay đổi với 2095 bổ sung1884 xóa
  1. 48 41
      CMakeLists.txt
  2. 0 10
      include/core/File.hpp
  3. 0 90
      include/core/Logger.hpp
  4. 0 120
      include/core/Terminal.hpp
  5. 0 61
      include/core/Test.hpp
  6. 0 16
      include/core/Types.hpp
  7. 0 78
      include/core/Utility.hpp
  8. 5 6
      modules/AlignedData.cppm
  9. 4 5
      modules/Array.cppm
  10. 7 7
      modules/ArrayList.cppm
  11. 5 6
      modules/BitArray.cppm
  12. 6 6
      modules/Box.cppm
  13. 4 5
      modules/Buffer.cppm
  14. 6 7
      modules/Clock.cppm
  15. 5 6
      modules/Color.cppm
  16. 8 7
      modules/Components.cppm
  17. 9 0
      modules/File.cppm
  18. 8 8
      modules/Frustum.cppm
  19. 17 13
      modules/HashMap.cppm
  20. 6 7
      modules/HashedString.cppm
  21. 16 11
      modules/List.cppm
  22. 104 0
      modules/Logger.cppm
  23. 6 7
      modules/Math.cppm
  24. 7 6
      modules/Matrix.cppm
  25. 4 5
      modules/Meta.cppm
  26. 6 6
      modules/Plane.cppm
  27. 6 6
      modules/Quaternion.cppm
  28. 8 7
      modules/Queue.cppm
  29. 6 7
      modules/Random.cppm
  30. 4 5
      modules/ReadLine.cppm
  31. 104 0
      modules/Terminal.cppm
  32. 60 0
      modules/Test.cppm
  33. 4 4
      modules/Thread.cppm
  34. 6 7
      modules/ToString.cppm
  35. 15 0
      modules/Types.cppm
  36. 5 6
      modules/Unicode.cppm
  37. 4 5
      modules/UniquePointer.cppm
  38. 94 0
      modules/Utility.cppm
  39. 8 8
      modules/Vector.cppm
  40. 7 6
      modules/View.cppm
  41. 13 12
      performance/Main.cpp
  42. 9 6
      src/BitArray.cpp
  43. 3 1
      src/Box.cpp
  44. 6 4
      src/Buffer.cpp
  45. 3 1
      src/Clock.cpp
  46. 30 0
      src/CustomNewDelete.cpp
  47. 11 8
      src/File.cpp
  48. 3 1
      src/Frustum.cpp
  49. 7 3
      src/Logger.cpp
  50. 7 1
      src/Matrix.cpp
  51. 3 1
      src/Plane.cpp
  52. 7 2
      src/Quaternion.cpp
  53. 3 1
      src/Random.cpp
  54. 22 15
      src/ReadLine.cpp
  55. 68 65
      src/Terminal.cpp
  56. 28 25
      src/Test.cpp
  57. 12 6
      src/Thread.cpp
  58. 4 2
      src/ToString.cpp
  59. 3 1
      src/Unicode.cpp
  60. 59 68
      src/Utility.cpp
  61. 5 1
      src/Vector.cpp
  62. 1 1
      src/View.cpp
  63. 7 7
      test/Main.cpp
  64. 40 0
      test/Tests.cppm
  65. 0 41
      test/Tests.hpp
  66. 80 77
      test/modules/ArrayListTests.cpp
  67. 10 8
      test/modules/ArrayTests.cpp
  68. 52 49
      test/modules/BitArrayTests.cpp
  69. 23 21
      test/modules/BoxTests.cpp
  70. 22 15
      test/modules/BufferTests.cpp
  71. 23 18
      test/modules/ClockTests.cpp
  72. 28 27
      test/modules/ColorTests.cpp
  73. 66 63
      test/modules/ComponentsTests.cpp
  74. 20 14
      test/modules/FileTests.cpp
  75. 29 26
      test/modules/FrustumTests.cpp
  76. 74 68
      test/modules/HashMapTests.cpp
  77. 31 30
      test/modules/HashedStringTests.cpp
  78. 97 93
      test/modules/ListTests.cpp
  79. 54 53
      test/modules/MathTests.cpp
  80. 35 29
      test/modules/MatrixTests.cpp
  81. 12 10
      test/modules/PlaneTests.cpp
  82. 21 18
      test/modules/QuaternionTests.cpp
  83. 94 87
      test/modules/QueueTests.cpp
  84. 18 15
      test/modules/RandomTests.cpp
  85. 29 23
      test/modules/ReadLineTests.cpp
  86. 46 41
      test/modules/TerminalTests.cpp
  87. 16 14
      test/modules/TestTests.cpp
  88. 38 31
      test/modules/ThreadTests.cpp
  89. 9 7
      test/modules/UnicodeTests.cpp
  90. 18 16
      test/modules/UniquePointerTests.cpp
  91. 66 60
      test/modules/UtilityTests.cpp
  92. 98 87
      test/modules/VectorTests.cpp
  93. 20 16
      test/modules/ViewTests.cpp

+ 48 - 41
CMakeLists.txt

@@ -1,13 +1,50 @@
-cmake_minimum_required(VERSION 3.25)
+cmake_minimum_required(VERSION 3.28)
 project(core CXX)
 
 set(CMAKE_CXX_STANDARD 23)
 
+set(SRC_MODULES
+    "modules/Array.cppm"
+    "modules/AlignedData.cppm"
+    "modules/ArrayList.cppm"
+    "modules/BitArray.cppm"
+    "modules/Box.cppm"
+    "modules/Buffer.cppm"
+    "modules/Clock.cppm"
+    "modules/Color.cppm"
+    "modules/Components.cppm"
+    "modules/File.cppm"
+    "modules/Frustum.cppm"
+    "modules/HashMap.cppm"
+    "modules/HashedString.cppm"
+    "modules/List.cppm"
+    "modules/Logger.cppm"
+    "modules/Math.cppm"
+    "modules/Matrix.cppm"
+    "modules/Meta.cppm"
+    "modules/Plane.cppm"
+    "modules/Quaternion.cppm"
+    "modules/Queue.cppm"
+    "modules/Random.cppm"
+    "modules/ReadLine.cppm"
+    "modules/Terminal.cppm"
+    "modules/Test.cppm"
+    "modules/Thread.cppm"
+    "modules/ToString.cppm"
+    "modules/Types.cppm"
+    "modules/Unicode.cppm"
+    "modules/UniquePointer.cppm"
+    "modules/Utility.cppm"
+    "modules/Vector.cppm"
+    "modules/View.cppm"
+)
+
 set(SRC
     "src/BitArray.cpp"
     "src/Box.cpp"
     "src/Buffer.cpp"
     "src/Clock.cpp"
+    "src/CustomNewDelete.cpp"
     "src/File.cpp"
     "src/Frustum.cpp"
     "src/Logger.cpp"
@@ -97,6 +134,11 @@ target_compile_options(core PUBLIC
     ${WARNINGS}
     -fdiagnostics-color=always
 )
+target_sources(core PUBLIC
+    FILE_SET CXX_MODULES
+    BASE_DIRS modules
+    FILES ${SRC_MODULES}
+)
 target_compile_definitions(core
     PRIVATE LOG_LEVEL=${LOG_LEVEL}
     PRIVATE ${DEFINITIONS}
@@ -104,47 +146,13 @@ target_compile_definitions(core
 target_link_libraries(core
     PRIVATE m ${LINK_OPTIONS}
 )
-target_sources(core PUBLIC
-    FILE_SET HEADERS
-    BASE_DIRS include
-    FILES
-        ./include/core/AlignedData.hpp
-        ./include/core/Array.hpp
-        ./include/core/ArrayList.hpp
-        ./include/core/BitArray.hpp
-        ./include/core/Box.hpp
-        ./include/core/Buffer.hpp
-        ./include/core/Clock.hpp
-        ./include/core/Color.hpp
-        ./include/core/Components.hpp
-        ./include/core/File.hpp
-        ./include/core/Frustum.hpp
-        ./include/core/HashMap.hpp
-        ./include/core/HashedString.hpp
-        ./include/core/List.hpp
-        ./include/core/Logger.hpp
-        ./include/core/Math.hpp
-        ./include/core/Matrix.hpp
-        ./include/core/Meta.hpp
-        ./include/core/Plane.hpp
-        ./include/core/Quaternion.hpp
-        ./include/core/Queue.hpp
-        ./include/core/Random.hpp
-        ./include/core/ReadLine.hpp
-        ./include/core/Terminal.hpp
-        ./include/core/Test.hpp
-        ./include/core/Thread.hpp
-        ./include/core/ToString.hpp
-        ./include/core/Types.hpp
-        ./include/core/Unicode.hpp
-        ./include/core/UniquePointer.hpp
-        ./include/core/Utility.hpp
-        ./include/core/Vector.hpp
-        ./include/core/View.hpp
-)
-install(TARGETS core FILE_SET HEADERS)
+install(TARGETS core FILE_SET CXX_MODULES DESTINATION modules)
 
 add_executable(test ${SRC_TESTS})
+target_sources(test PUBLIC
+    FILE_SET CXX_MODULES
+    FILES test/Tests.cppm
+)
 target_link_libraries(test PRIVATE core)
 target_compile_definitions(test PRIVATE ${DEFINITIONS})
 target_compile_definitions(test
@@ -153,7 +161,6 @@ target_compile_definitions(test
 
 add_executable(performance ${SRC_PERFORMANCE})
 target_link_libraries(performance PRIVATE core)
-target_include_directories(performance PRIVATE include)
 target_compile_definitions(performance PRIVATE ${DEFINITIONS})
 target_compile_definitions(performance
     PRIVATE LOG_LEVEL=4

+ 0 - 10
include/core/File.hpp

@@ -1,10 +0,0 @@
-#ifndef CORE_FILE_HPP
-#define CORE_FILE_HPP
-
-#include "core/List.hpp"
-
-namespace Core {
-    bool readFile(List<char>& content, const char* path);
-}
-
-#endif

+ 0 - 90
include/core/Logger.hpp

@@ -1,90 +0,0 @@
-#ifndef CORE_LOGGER_HPP
-#define CORE_LOGGER_HPP
-
-#include <cstdio>
-
-#include "core/Terminal.hpp"
-#include "core/ToString.hpp"
-
-namespace Core {
-    enum class LogLevel { NONE, ERROR, WARNING, INFO, DEBUG };
-
-    extern LogLevel logLevel;
-
-    using ReportHandler = void (*)(
-        LogLevel l, const char* file, int line, void* data,
-        const char* message);
-    void callReportHandler(
-        LogLevel l, const char* file, int line, const char* report);
-    void setReportHandler(ReportHandler h, void* data);
-
-    template<typename... Args>
-    void callReportHandler(
-        LogLevel l, const char* file, int line, const char* format,
-        Args&&... args) {
-        char buffer[512];
-        formatBuffer(
-            buffer, sizeof(buffer), format, Core::forward<Args>(args)...);
-        callReportHandler(l, file, line, buffer);
-    }
-
-#define REPORT(l, ...)                                          \
-    Core::callReportHandler(l, __FILE__, __LINE__, __VA_ARGS__)
-
-    const char* getShortFileName(const char* s);
-
-    template<typename... Args>
-    void printLog(
-        LogLevel l, const char* file, int line, const char* prefix,
-        const char* format, Args&&... args) {
-        if(logLevel < l) {
-            return;
-        }
-        file = getShortFileName(file);
-        fputs(prefix, stdout);
-        printf("%s:%d | ", file, line);
-        char buffer[512];
-        formatBuffer(
-            buffer, sizeof(buffer), format, Core::forward<Args>(args)...);
-        fputs(buffer, stdout);
-        puts(TERMINAL_RESET);
-    }
-}
-
-#if defined(LOG_LEVEL) && LOG_LEVEL >= 1
-#define LOG_ERROR(...)                               \
-    Core::printLog(                                  \
-        Core::LogLevel::ERROR, __FILE__, __LINE__,   \
-        TERMINAL_BRIGHT_RED "[ERROR] ", __VA_ARGS__)
-#else
-#define LOG_ERROR(...)
-#endif
-
-#if defined(LOG_LEVEL) && LOG_LEVEL >= 2
-#define LOG_WARNING(...)                                  \
-    Core::printLog(                                       \
-        Core::LogLevel::WARNING, __FILE__, __LINE__,      \
-        TERMINAL_BRIGHT_YELLOW "[WARNING] ", __VA_ARGS__)
-#else
-#define LOG_WARNING(...)
-#endif
-
-#if defined(LOG_LEVEL) && LOG_LEVEL >= 3
-#define LOG_INFO(...)                                                      \
-    Core::printLog(                                                        \
-        Core::LogLevel::INFO, __FILE__, __LINE__, TERMINAL_BOLD "[INFO] ", \
-        __VA_ARGS__)
-#else
-#define LOG_INFO(...)
-#endif
-
-#if defined(LOG_LEVEL) && LOG_LEVEL >= 4
-#define LOG_DEBUG(...)                                               \
-    Core::printLog(                                                  \
-        Core::LogLevel::DEBUG, __FILE__, __LINE__,                   \
-        TERMINAL_BOLD TERMINAL_BRIGHT_BLACK "[DEBUG] ", __VA_ARGS__)
-#else
-#define LOG_DEBUG(...)
-#endif
-
-#endif

+ 0 - 120
include/core/Terminal.hpp

@@ -1,120 +0,0 @@
-#ifndef CORE_TERMINAL_HPP
-#define CORE_TERMINAL_HPP
-
-#include "core/Types.hpp"
-#include "core/Vector.hpp"
-
-#define ESC "\33["
-#define TERMINAL_RESET ESC "0m"
-#define TERMINAL_BOLD ESC "1m"
-
-#define TERMINAL_BLACK ESC "30m"
-#define TERMINAL_RED ESC "31m"
-#define TERMINAL_GREEN ESC "32m"
-#define TERMINAL_YELLOW ESC "33m"
-#define TERMINAL_BLUE ESC "34m"
-#define TERMINAL_MAGENTA ESC "35m"
-#define TERMINAL_CYAN ESC "36m"
-#define TERMINAL_WHITE ESC "37m"
-#define TERMINAL_BRIGHT_BLACK ESC "90m"
-#define TERMINAL_BRIGHT_RED ESC "91m"
-#define TERMINAL_BRIGHT_GREEN ESC "92m"
-#define TERMINAL_BRIGHT_YELLOW ESC "93m"
-#define TERMINAL_BRIGHT_BLUE ESC "94m"
-#define TERMINAL_BRIGHT_MAGENTA ESC "95m"
-#define TERMINAL_BRIGHT_CYAN ESC "96m"
-#define TERMINAL_BRIGHT_WHITE ESC "97m"
-
-#define TERMINAL_FG_BLACK TERMINAL_BLACK
-#define TERMINAL_FG_RED TERMINAL_RED
-#define TERMINAL_FG_GREEN TERMINAL_GREEN
-#define TERMINAL_FG_YELLOW TERMINAL_YELLOW
-#define TERMINAL_FG_BLUE TERMINAL_BLUE
-#define TERMINAL_FG_MAGENTA TERMINAL_MAGENTA
-#define TERMINAL_FG_CYAN TERMINAL_CYAN
-#define TERMINAL_FG_WHITE TERMINAL_WHITE
-#define TERMINAL_FG_BRIGHT_BLACK TERMINAL_BRIGHT_BLACK
-#define TERMINAL_FG_BRIGHT_RED TERMINAL_BRIGHT_RED
-#define TERMINAL_FG_BRIGHT_GREEN TERMINAL_BRIGHT_GREEN
-#define TERMINAL_FG_BRIGHT_YELLOW TERMINAL_BRIGHT_YELLOW
-#define TERMINAL_FG_BRIGHT_BLUE TERMINAL_BRIGHT_BLUE
-#define TERMINAL_FG_BRIGHT_MAGENTA TERMINAL_BRIGHT_MAGENTA
-#define TERMINAL_FG_BRIGHT_CYAN TERMINAL_BRIGHT_CYAN
-#define TERMINAL_FG_BRIGHT_WHITE TERMINAL_BRIGHT_WHITE
-
-#define TERMINAL_BG_BLACK ESC "40m"
-#define TERMINAL_BG_RED ESC "41m"
-#define TERMINAL_BG_GREEN ESC "42m"
-#define TERMINAL_BG_YELLOW ESC "43m"
-#define TERMINAL_BG_BLUE ESC "44m"
-#define TERMINAL_BG_MAGENTA ESC "45m"
-#define TERMINAL_BG_CYAN ESC "46m"
-#define TERMINAL_BG_WHITE ESC "47m"
-#define TERMINAL_BG_BRIGHT_BLACK ESC "100m"
-#define TERMINAL_BG_BRIGHT_RED ESC "101m"
-#define TERMINAL_BG_BRIGHT_GREEN ESC "102m"
-#define TERMINAL_BG_BRIGHT_YELLOW ESC "103m"
-#define TERMINAL_BG_BRIGHT_BLUE ESC "104m"
-#define TERMINAL_BG_BRIGHT_MAGENTA ESC "105m"
-#define TERMINAL_BG_BRIGHT_CYAN ESC "106m"
-#define TERMINAL_BG_BRIGHT_WHITE ESC "107m"
-// keycodes
-#define TERMINAL_KEY_UNKNOWN 0x1'0000'0000lu
-// default keycodes
-#define TERMINAL_KEY_ARROW_LEFT 0x1'0000'0001lu
-#define TERMINAL_KEY_ARROW_RIGHT 0x1'0000'0002lu
-#define TERMINAL_KEY_ARROW_UP 0x1'0000'0003lu
-#define TERMINAL_KEY_ARROW_DOWN 0x1'0000'0004lu
-#define TERMINAL_KEY_DELETE 0x1'0000'0005lu
-#define TERMINAL_KEY_F1 0x1'0000'0006lu
-#define TERMINAL_KEY_F2 0x1'0000'0007lu
-#define TERMINAL_KEY_F3 0x1'0000'0008lu
-#define TERMINAL_KEY_F4 0x1'0000'0009lu
-#define TERMINAL_KEY_F5 0x1'0000'000Alu
-#define TERMINAL_KEY_F6 0x1'0000'000Blu
-#define TERMINAL_KEY_F7 0x1'0000'000Clu
-#define TERMINAL_KEY_F8 0x1'0000'000Dlu
-#define TERMINAL_KEY_F9 0x1'0000'000Elu
-#define TERMINAL_KEY_F10 0x1'0000'000Flu
-#define TERMINAL_KEY_F11 0x1'0000'0010lu
-#define TERMINAL_KEY_F12 0x1'0000'0011lu
-#define TERMINAL_KEY_PAGE_UP 0x1'0000'0012lu
-#define TERMINAL_KEY_PAGE_DOWN 0x1'0000'0013lu
-#define TERMINAL_KEY_HOME 0x1'0000'0014lu
-#define TERMINAL_KEY_END 0x1'0000'0015lu
-// key modifiers
-#define TERMINAL_KEY_CTRL 0x2'0000'0000lu
-#define TERMINAL_KEY_SHIFT 0x4'0000'0000lu
-#define TERMINAL_KEY_ALT 0x8'0000'0000lu
-
-namespace Core {
-    void enterAlternativeTerminal();
-    void leaveAlternativeTerminal();
-    IntVector2 getTerminalSize();
-    void clearTerminal();
-    void clearTerminalLine();
-
-    void hideCursor();
-    void showCursor();
-    void resetCursor();
-    void moveCursorLeft(int i);
-    void moveCursorRight(int i);
-    void moveCursorUp(int i);
-    void moveCursorDown(int i);
-
-    bool enterRawTerminal();
-    bool leaveRawTerminal();
-    u64 getRawChar();
-    bool isSpecialChar(u64 u);
-
-    struct SpecialChar {
-        u64 key = 0;
-        bool control = 0;
-        bool shift = 0;
-        bool alt = 0;
-    };
-
-    SpecialChar convertToSpecialChar(u64 u);
-}
-
-#endif

+ 0 - 61
include/core/Test.hpp

@@ -1,61 +0,0 @@
-#ifndef CORE_TEST_HPP
-#define CORE_TEST_HPP
-
-#include "core/Logger.hpp"
-
-namespace Core {
-    void finalizeTests(void);
-    bool addTestResult(const char* file, bool comparison);
-
-    template<typename T>
-    bool testEqual(
-        const char* file, int line, const T& wanted, const T& actual) {
-        file = getShortFileName(file);
-        if(addTestResult(file, wanted == actual)) {
-            return true;
-        }
-        char buffer[512];
-        formatBuffer(
-            buffer, sizeof(buffer),
-            TERMINAL_RED "#:# - expected '#' got '#'" TERMINAL_RESET, file,
-            line, wanted, actual);
-        puts(buffer);
-        return false;
-    }
-
-    bool testString(
-        const char* file, int line, const char* wanted, const char* actual);
-
-    template<typename A, typename B>
-    bool testString(
-        const char* file, int line, const A& wanted, const B& actual) {
-        char wantedString[512];
-        size_t lw = toString(wanted, wantedString, sizeof(wantedString));
-        char actualString[512];
-        size_t la = toString(actual, actualString, sizeof(actualString));
-        testEqual(file, line, lw, la);
-        return testString(
-            file, line, static_cast<const char*>(wantedString),
-            static_cast<const char*>(actualString));
-    }
-
-    bool testFloat(
-        const char* file, int line, float wanted, float actual, float error);
-    bool testNull(const char* file, int line, const void* p);
-    bool testNotNull(const char* file, int line, const void* p);
-
-#define TEST_FLOAT(wanted, actual, error)                      \
-    Core::testFloat(__FILE__, __LINE__, wanted, actual, error)
-
-#define TEST(wanted, actual)                                  \
-    Core::testEqual<Core::RemoveReference<decltype(actual)>>( \
-        __FILE__, __LINE__, wanted, actual)
-#define TEST_STRING(wanted, actual)                      \
-    Core::testString(__FILE__, __LINE__, wanted, actual)
-#define TEST_FALSE(actual) TEST(false, actual)
-#define TEST_TRUE(actual) TEST(true, actual)
-#define TEST_NULL(actual) Core::testNull(__FILE__, __LINE__, actual)
-#define TEST_NOT_NULL(actual) Core::testNotNull(__FILE__, __LINE__, actual)
-}
-
-#endif

+ 0 - 16
include/core/Types.hpp

@@ -1,16 +0,0 @@
-#ifndef CORE_TYPES_HPP
-#define CORE_TYPES_HPP
-
-#include <cstdint>
-
-using i64 = int64_t;
-using i32 = int32_t;
-using i16 = int16_t;
-using i8 = int8_t;
-using u64 = uint64_t;
-using u32 = uint32_t;
-using u16 = uint16_t;
-using u8 = uint8_t;
-using size_t = decltype(sizeof(int));
-
-#endif

+ 0 - 78
include/core/Utility.hpp

@@ -1,78 +0,0 @@
-#ifndef CORE_UTILITY_HPP
-#define CORE_UTILITY_HPP
-
-#include <cstddef>
-
-#include "core/Meta.hpp"
-
-#ifdef CHECK_MEMORY
-void* operator new(size_t count, const char* file, int line);
-void* operator new[](size_t count, const char* file, int line);
-#endif
-
-namespace Core {
-    inline void nothing() {
-    }
-
-    template<typename T, typename C = int>
-    C popCount(const T& t) {
-        static constexpr C map[16] = {0, 1, 1, 2, 1, 2, 2, 3,
-                                      1, 2, 2, 3, 2, 3, 3, 4};
-        C sum = 0;
-        for(size_t i = 0; i < sizeof(T) * 8; i += 4) {
-            sum += map[(t >> i) & 0xF];
-        }
-        return sum;
-    }
-
-    using ExitHandler = void (*)(int, void*);
-    [[noreturn]] void exitWithHandler(const char* file, int line, int value);
-    void setExitHandler(ExitHandler h, void* data);
-#define EXIT(exitValue) Core::exitWithHandler(__FILE__, __LINE__, exitValue)
-
-    using OutOfMemoryHandler = void (*)(void*);
-    void setOutOfMemoryHandler(OutOfMemoryHandler h, void* data);
-
-#ifdef CHECK_MEMORY
-    void* debugAllocateRaw(const char* file, int line, size_t n);
-    void* debugZeroAllocateRaw(const char* file, int line, size_t n);
-    void* debugReallocateRaw(const char* file, int line, void* p, size_t n);
-    void debugDeallocateRaw(void* p);
-    void printMemoryReport();
-#define allocateRaw(n) debugAllocateRaw(__FILE__, __LINE__, n)
-#define zeroAllocateRaw(n) debugZeroAllocateRaw(__FILE__, __LINE__, n)
-#define reallocateRaw(p, n) debugReallocateRaw(__FILE__, __LINE__, p, n)
-#define deallocateRaw(p) debugDeallocateRaw(p)
-#define coreNew(type, ...) new(__FILE__, __LINE__) type(__VA_ARGS__)
-#define coreNewN(type, n) new(__FILE__, __LINE__) type[n]
-#define coreDelete(p) delete(p)
-#define coreDeleteN(p) delete[](p)
-#else
-    void* allocateRaw(size_t n);
-    void* zeroAllocateRaw(size_t n);
-    void* reallocateRaw(void* p, size_t n);
-    void deallocateRaw(void* p);
-#define printMemoryReport() nothing()
-#define coreNew(type, ...) new type(__VA_ARGS__)
-#define coreNewN(type, n) new type[n]
-#define coreDelete(p) delete(p)
-#define coreDeleteN(p) delete[](p)
-#endif
-
-    template<typename T>
-    void bubbleSort(T* data, size_t n) {
-        bool swapped = true;
-        while(swapped && n > 0) {
-            swapped = false;
-            n--;
-            for(size_t i = 0; i < n; i++) {
-                if(data[i] > data[i + 1]) {
-                    swap(data[i], data[i + 1]);
-                    swapped = true;
-                }
-            }
-        }
-    }
-}
-
-#endif

+ 5 - 6
include/core/AlignedData.hpp → modules/AlignedData.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_ALIGNED_DATA_HPP
-#define CORE_ALIGNED_DATA_HPP
+module;
 
-#include <cstddef>
+export module Core.AlignedData;
 
-namespace Core {
+import Core.Types;
+
+export namespace Core {
     template<size_t SIZE, size_t ALIGNMENT>
     class alignas(ALIGNMENT) AlignedData final {
         char buffer[SIZE] = {};
@@ -31,5 +32,3 @@ namespace Core {
     template<typename T>
     using AlignedType = AlignedData<sizeof(T), alignof(T)>;
 }
-
-#endif

+ 4 - 5
include/core/Array.hpp → modules/Array.cppm

@@ -1,10 +1,11 @@
-#ifndef CORE_ARRAY_HPP
-#define CORE_ARRAY_HPP
+module;
 
 #include <cstddef>
 
+export module Core.Array;
+
 namespace Core {
-    template<typename T, size_t N>
+    export template<typename T, size_t N>
     struct Array final {
         T data[N] = {};
 
@@ -43,5 +44,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 7 - 7
include/core/ArrayList.hpp → modules/ArrayList.cppm

@@ -1,12 +1,14 @@
-#ifndef CORE_ARRAYLIST_HPP
-#define CORE_ARRAYLIST_HPP
+module;
 
 #include <cassert>
 
-#include "core/AlignedData.hpp"
-#include "core/Meta.hpp"
+export module Core.ArrayList;
 
-namespace Core {
+import Core.AlignedData;
+import Core.Meta;
+import Core.Types;
+
+export namespace Core {
     template<typename T, size_t N>
     class ArrayList final {
         AlignedType<T> data[N];
@@ -170,5 +172,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 5 - 6
include/core/BitArray.hpp → modules/BitArray.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_BIT_ARRAY_HPP
-#define CORE_BIT_ARRAY_HPP
+module;
 
-#include "core/Types.hpp"
+export module Core.BitArray;
 
-namespace Core {
+import Core.Types;
+
+export namespace Core {
     class BitArray final {
         u64 length : 56;
         u64 bits : 8;
@@ -30,5 +31,3 @@ namespace Core {
 
     static_assert(sizeof(BitArray) == 16, "invalid bit array size");
 }
-
-#endif

+ 6 - 6
include/core/Box.hpp → modules/Box.cppm

@@ -1,9 +1,11 @@
-#ifndef CORE_BOX_HPP
-#define CORE_BOX_HPP
+module;
 
-#include "core/Vector.hpp"
+export module Core.Box;
 
-namespace Core {
+import Core.Vector;
+import Core.Types;
+
+export namespace Core {
     class Box final {
         Vector3 min;
         Vector3 max;
@@ -24,5 +26,3 @@ namespace Core {
         size_t toString(char* s, size_t n) const;
     };
 }
-
-#endif

+ 4 - 5
include/core/Buffer.hpp → modules/Buffer.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_BUFFER_HPP
-#define CORE_BUFFER_HPP
+module;
 
 #include <cstddef>
 
-namespace Core {
+export module Core.Buffer;
+
+export namespace Core {
     class Buffer final {
         size_t length;
         size_t capacity;
@@ -30,5 +31,3 @@ namespace Core {
         void swap(Buffer& other) noexcept;
     };
 }
-
-#endif

+ 6 - 7
include/core/Clock.hpp → modules/Clock.cppm

@@ -1,10 +1,11 @@
-#ifndef CORE_CLOCK_HPP
-#define CORE_CLOCK_HPP
+module;
 
-#include "core/Array.hpp"
-#include "core/Types.hpp"
+export module Core.Clock;
 
-namespace Core {
+import Core.Array;
+import Core.Types;
+
+export namespace Core {
     struct Clock final {
         size_t index;
         i64 last;
@@ -23,5 +24,3 @@ namespace Core {
         static i64 getNanos();
     };
 }
-
-#endif

+ 5 - 6
include/core/Color.hpp → modules/Color.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_COLOR_HPP
-#define CORE_COLOR_HPP
+module;
 
-#include "core/Types.hpp"
+export module Core.Color;
 
-namespace Core {
+import Core.Types;
+
+export namespace Core {
     using ColorChannel = u8;
 
     template<size_t N>
@@ -42,5 +43,3 @@ namespace Core {
     using Color2 = Color<2>;
     using Color1 = Color<1>;
 }
-
-#endif

+ 8 - 7
include/core/Components.hpp → modules/Components.cppm

@@ -1,10 +1,13 @@
-#ifndef CORE_COMPONENTS_HPP
-#define CORE_COMPONENTS_HPP
+module;
 
-#include "core/HashMap.hpp"
-#include "core/List.hpp"
+export module Core.Components;
 
-namespace Core {
+import Core.HashMap;
+import Core.List;
+import Core.Types;
+import Core.Meta;
+
+export namespace Core {
     using Entity = size_t;
 
     template<typename T>
@@ -138,5 +141,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 9 - 0
modules/File.cppm

@@ -0,0 +1,9 @@
+module;
+
+export module Core.File;
+
+import Core.List;
+
+export namespace Core {
+    bool readFile(List<char>& content, const char* path);
+}

+ 8 - 8
include/core/Frustum.hpp → modules/Frustum.cppm

@@ -1,11 +1,13 @@
-#ifndef CORE_FRUSTUM_HPP
-#define CORE_FRUSTUM_HPP
+module;
 
-#include "core/Array.hpp"
-#include "core/Matrix.hpp"
-#include "core/Plane.hpp"
+export module Core.Frustum;
 
-namespace Core {
+import Core.Array;
+import Core.Matrix;
+import Core.Plane;
+import Core.Vector;
+
+export namespace Core {
     class Frustum final {
         Matrix projection;
         Array<Plane, 6> planes;
@@ -24,5 +26,3 @@ namespace Core {
         bool isInside(const Vector3& pos, float radius) const;
     };
 }
-
-#endif

+ 17 - 13
include/core/HashMap.hpp → modules/HashMap.cppm

@@ -1,17 +1,21 @@
-#ifndef CORE_HASHMAP_HPP
-#define CORE_HASHMAP_HPP
+module;
 
-#include "core/List.hpp"
-#include "core/ToString.hpp"
-#include "core/Types.hpp"
-#include "core/Utility.hpp"
+export module Core.HashMap;
 
-template<typename T>
+import Core.List;
+import Core.ToString;
+import Core.Types;
+import Core.Utility;
+import Core.AlignedData;
+import Core.Math;
+import Core.Meta;
+
+export template<typename T>
 concept Hashable = requires(const T& t) { t.hashCode(); };
-template<typename T>
+export template<typename T>
 concept HashCast = requires(const T& t) { static_cast<size_t>(t); };
 
-namespace Core {
+export namespace Core {
     template<Hashable H>
     inline size_t hashCode(const H& key) {
         return key.hashCode();
@@ -173,7 +177,8 @@ namespace Core {
                     values[length].~V();
                 }
             }
-            coreDeleteN(reinterpret_cast<AlignedType<V>*>(values));
+            deleteWithSourceN<AlignedType<V>>(
+                reinterpret_cast<AlignedType<V>*>(values));
         }
 
         HashMap& operator=(HashMap other) noexcept {
@@ -188,7 +193,8 @@ namespace Core {
             HashMap<K, V> map;
             size_t l = (1lu << roundUpLog2(max(minCapacity, 8lu))) + 1;
             map.keys.resize(l, INVALID);
-            map.values = reinterpret_cast<V*>(coreNewN(AlignedType<V>, l));
+            map.values =
+                reinterpret_cast<V*>(newWithSourceN<AlignedType<V>>(l));
             map.jumps.resize(l, 0);
             size_t length = keys.getLength();
             if(length > 0) {
@@ -391,5 +397,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 6 - 7
include/core/HashedString.hpp → modules/HashedString.cppm

@@ -1,9 +1,11 @@
-#ifndef CORE_HASHED_STRING_HPP
-#define CORE_HASHED_STRING_HPP
+module;
 
+#include <cstdio>
 #include <cstring>
 
-namespace Core {
+export module Core.HashedString;
+
+export namespace Core {
     struct StringHash {
         size_t length = 0;
         size_t hash = 0;
@@ -28,8 +30,7 @@ namespace Core {
         }
 
         HashedString(const char* s) : hash(hashString(s)) {
-            strncpy(data, s, N - 1);
-            data[N - 1] = 0;
+            snprintf(data, N, "%s", s);
         }
 
         bool operator==(const HashedString& other) const {
@@ -59,5 +60,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 16 - 11
include/core/List.hpp → modules/List.cppm

@@ -1,15 +1,19 @@
-#ifndef CORE_LIST_HPP
-#define CORE_LIST_HPP
+module;
 
 #include <cassert>
+#include <cstdio>
 #include <new>
 
-#include "core/AlignedData.hpp"
-#include "core/Math.hpp"
-#include "core/Meta.hpp"
-#include "core/Utility.hpp"
+export module Core.List;
 
-namespace Core {
+import Core.AlignedData;
+import Core.Math;
+import Core.Meta;
+import Core.Types;
+import Core.Utility;
+import Core.ToString;
+
+export namespace Core {
     template<typename T>
     class List final {
         size_t length;
@@ -34,7 +38,8 @@ namespace Core {
 
         ~List() {
             clear();
-            delete[] reinterpret_cast<AlignedType<T>*>(data);
+            deleteWithSourceN<AlignedType<T>>(
+                reinterpret_cast<AlignedType<T>*>(data));
         }
 
         List& operator=(List other) noexcept {
@@ -115,6 +120,8 @@ namespace Core {
             if(t >= length) {
                 return put(Core::forward<Args>(args)...);
             }
+            // put must not reallocate, to keep the moved element alive
+            ensureCapacity();
             put(Core::move(data[length - 1]));
             for(size_t i = length - 2; i > t; i--) {
                 data[i] = Core::move(data[i - 1]);
@@ -201,7 +208,7 @@ namespace Core {
             if(n <= 0) {
                 return nullptr;
             }
-            return reinterpret_cast<T*>(coreNewN(AlignedType<T>, n));
+            return reinterpret_cast<T*>(newWithSourceN<AlignedType<T>>(n));
         }
 
         void ensureCapacity() {
@@ -227,5 +234,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 104 - 0
modules/Logger.cppm

@@ -0,0 +1,104 @@
+module;
+
+#include <cstdio>
+#include <source_location>
+
+export module Core.Logger;
+
+import Core.Terminal;
+import Core.Meta;
+import Core.ToString;
+
+#define SOURCE const std::source_location& sl = std::source_location::current()
+
+export namespace Core {
+    enum class LogLevel { NONE, ERROR, WARNING, INFO, DEBUG };
+
+    extern LogLevel logLevel;
+
+    using ReportHandler = void (*)(
+        LogLevel l, const std::source_location& sl, void* data,
+        const char* message);
+    void callReportHandler(LogLevel l, const char* report, SOURCE);
+    void setReportHandler(ReportHandler h, void* data);
+
+    struct FormatLocation final {
+        const char* format;
+        std::source_location location;
+
+        FormatLocation(
+            const char* f,
+            const std::source_location& l = std::source_location::current()) :
+            format(f), location(l) {
+        }
+    };
+
+    template<typename... Args>
+    void report(LogLevel l, const FormatLocation& format, Args&&... args) {
+        char buffer[512];
+        formatBuffer(
+            buffer, sizeof(buffer), format.format,
+            Core::forward<Args>(args)...);
+        callReportHandler(l, buffer, format.location);
+    }
+
+    const char* getShortFileName(const char* s);
+
+    template<typename... Args>
+    void log(
+        LogLevel l, const char* prefix, const FormatLocation& format,
+        Args&&... args) {
+        if(logLevel < l) {
+            return;
+        }
+        const char* file = getShortFileName(format.location.file_name());
+        fputs(prefix, stdout);
+        printf("%s:%u | ", file, format.location.line());
+        char buffer[512];
+        formatBuffer(
+            buffer, sizeof(buffer), format.format,
+            Core::forward<Args>(args)...);
+        fputs(buffer, stdout);
+        puts(Terminal::RESET);
+    }
+
+    template<typename... Args>
+    void logError(const FormatLocation& format, Args&&... args) {
+        if constexpr(LOG_LEVEL >= 1) {
+            char buffer[32];
+            snprintf(buffer, sizeof(buffer), "%s[ERROR] ", Terminal::FG_RED);
+            log(LogLevel::ERROR, buffer, format, forward<Args>(args)...);
+        }
+    }
+
+    template<typename... Args>
+    void logWarning(const FormatLocation& format, Args&&... args) {
+        if constexpr(LOG_LEVEL >= 2) {
+            char buffer[32];
+            snprintf(
+                buffer, sizeof(buffer), "%s[WARNING] ",
+                Terminal::FG_BRIGHT_YELLOW);
+            log(LogLevel::WARNING, buffer, format, forward<Args>(args)...);
+        }
+    }
+
+    template<typename... Args>
+    void logInfo(const FormatLocation& format, Args&&... args) {
+        if constexpr(LOG_LEVEL >= 3) {
+            char buffer[32];
+            snprintf(buffer, sizeof(buffer), "%s[INFO] ", Terminal::BOLD);
+            log(LogLevel::INFO, buffer, format, forward<Args>(args)...);
+        }
+    }
+
+    template<typename... Args>
+    void logDebug(const FormatLocation& format, Args&&... args) {
+        if constexpr(LOG_LEVEL >= 4) {
+            char buffer[32];
+            snprintf(
+                buffer, sizeof(buffer), "%s%s[DEBUG] ", Terminal::BOLD,
+                Terminal::FG_BRIGHT_BLACK);
+            log(LogLevel::DEBUG, buffer, format, forward<Args>(args)...);
+        }
+    }
+}

+ 6 - 7
include/core/Math.hpp → modules/Math.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_MATH_HPP
-#define CORE_MATH_HPP
+module;
 
-#include "core/Meta.hpp"
+export module Core.Math;
 
-namespace Core {
+import Core.Meta;
+
+export namespace Core {
     template<typename T>
     T interpolate(const T& a, const T& b, float f) {
         return a * (1.0f - f) + b * f;
@@ -54,7 +55,7 @@ namespace Core {
         return max(low, min(high, t));
     }
 
-    constexpr float PI = 3.14159265358979323846f;
+    inline constexpr float PI = 3.14159265358979323846f;
 
     template<typename T>
     constexpr T radianToDegree(const T& radians) {
@@ -66,5 +67,3 @@ namespace Core {
         return radians * static_cast<T>(PI / 180.0f);
     }
 }
-
-#endif

+ 7 - 6
include/core/Matrix.hpp → modules/Matrix.cppm

@@ -1,9 +1,12 @@
-#ifndef CORE_MATRIX_HPP
-#define CORE_MATRIX_HPP
+module;
 
-#include "core/Quaternion.hpp"
+export module Core.Matrix;
 
-namespace Core {
+import Core.Quaternion;
+import Core.Vector;
+import Core.Types;
+
+export namespace Core {
     class Matrix final {
         Vector4 data[4];
 
@@ -33,5 +36,3 @@ namespace Core {
         Matrix& rotate(float degrees, int a, int b);
     };
 }
-
-#endif

+ 4 - 5
include/core/Meta.hpp → modules/Meta.cppm

@@ -1,7 +1,8 @@
-#ifndef CORE_META_HPP
-#define CORE_META_HPP
+module;
 
-namespace Core {
+export module Core.Meta;
+
+export namespace Core {
     namespace Internal {
         template<typename T>
         struct BaseRemovePointer final {
@@ -89,5 +90,3 @@ namespace Core {
         b = Core::move(tmp);
     }
 }
-
-#endif

+ 6 - 6
include/core/Plane.hpp → modules/Plane.cppm

@@ -1,9 +1,11 @@
-#ifndef CORE_PLANE_HPP
-#define CORE_PLANE_HPP
+module;
 
-#include "core/Vector.hpp"
+export module Core.Plane;
 
-namespace Core {
+import Core.Vector;
+import Core.Types;
+
+export namespace Core {
     class Plane final {
         Vector3 abc;
         float d;
@@ -15,5 +17,3 @@ namespace Core {
         size_t toString(char* s, size_t n) const;
     };
 }
-
-#endif

+ 6 - 6
include/core/Quaternion.hpp → modules/Quaternion.cppm

@@ -1,9 +1,11 @@
-#ifndef CORE_QUATERNION_HPP
-#define CORE_QUATERNION_HPP
+module;
 
-#include "core/Vector.hpp"
+export module Core.Quaternion;
 
-namespace Core {
+import Core.Vector;
+import Core.Types;
+
+export namespace Core {
     class Quaternion final {
         Vector4 v;
 
@@ -17,5 +19,3 @@ namespace Core {
         size_t toString(char* s, size_t n) const;
     };
 }
-
-#endif

+ 8 - 7
include/core/Queue.hpp → modules/Queue.cppm

@@ -1,12 +1,15 @@
-#ifndef CORE_QUEUE_HPP
-#define CORE_QUEUE_HPP
+module;
 
 #include <cassert>
 
-#include "core/AlignedData.hpp"
-#include "core/ToString.hpp"
+export module Core.Queue;
 
-namespace Core {
+import Core.AlignedData;
+import Core.ToString;
+import Core.Types;
+import Core.Meta;
+
+export namespace Core {
     template<typename T, size_t N>
     class Queue final {
         AlignedType<T> data[N];
@@ -126,5 +129,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 6 - 7
include/core/Random.hpp → modules/Random.cppm

@@ -1,10 +1,11 @@
-#ifndef CORE_RANDOM_HPP
-#define CORE_RANDOM_HPP
+module;
 
-#include "core/Array.hpp"
-#include "core/Types.hpp"
+export module Core.Random;
 
-namespace Core {
+import Core.Array;
+import Core.Types;
+
+export namespace Core {
     class Random final {
         Array<u32, 25> data;
         size_t index;
@@ -23,5 +24,3 @@ namespace Core {
         void update();
     };
 }
-
-#endif

+ 4 - 5
include/core/ReadLine.hpp → modules/ReadLine.cppm

@@ -1,12 +1,11 @@
-#ifndef CORE_READ_LINE_HPP
-#define CORE_READ_LINE_HPP
+module;
 
 #include <cstddef>
 
-namespace Core {
+export module Core.ReadLine;
+
+export namespace Core {
     bool startReadLine();
     bool readLine(char* buffer, size_t n);
     void stopReadLine();
 }
-
-#endif

+ 104 - 0
modules/Terminal.cppm

@@ -0,0 +1,104 @@
+module;
+export module Core.Terminal;
+
+import Core.Types;
+import Core.Vector;
+
+#define ESC "\33["
+
+export namespace Core {
+    namespace Terminal {
+        inline constexpr const char RESET[] = ESC "0m";
+        inline constexpr const char BOLD[] = ESC "1m";
+        // foreground colors
+        inline constexpr const char FG_BLACK[] = ESC "30m";
+        inline constexpr const char FG_RED[] = ESC "31m";
+        inline constexpr const char FG_GREEN[] = ESC "32m";
+        inline constexpr const char FG_YELLOW[] = ESC "33m";
+        inline constexpr const char FG_BLUE[] = ESC "34m";
+        inline constexpr const char FG_MAGENTA[] = ESC "35m";
+        inline constexpr const char FG_CYAN[] = ESC "36m";
+        inline constexpr const char FG_WHITE[] = ESC "37m";
+        inline constexpr const char FG_BRIGHT_BLACK[] = ESC "90m";
+        inline constexpr const char FG_BRIGHT_RED[] = ESC "91m";
+        inline constexpr const char FG_BRIGHT_GREEN[] = ESC "92m";
+        inline constexpr const char FG_BRIGHT_YELLOW[] = ESC "93m";
+        inline constexpr const char FG_BRIGHT_BLUE[] = ESC "94m";
+        inline constexpr const char FG_BRIGHT_MAGENTA[] = ESC "95m";
+        inline constexpr const char FG_BRIGHT_CYAN[] = ESC "96m";
+        inline constexpr const char FG_BRIGHT_WHITE[] = ESC "97m";
+        // background colors
+        inline constexpr const char BG_BLACK[] = ESC "40m";
+        inline constexpr const char BG_RED[] = ESC "41m";
+        inline constexpr const char BG_GREEN[] = ESC "42m";
+        inline constexpr const char BG_YELLOW[] = ESC "43m";
+        inline constexpr const char BG_BLUE[] = ESC "44m";
+        inline constexpr const char BG_MAGENTA[] = ESC "45m";
+        inline constexpr const char BG_CYAN[] = ESC "46m";
+        inline constexpr const char BG_WHITE[] = ESC "47m";
+        inline constexpr const char BG_BRIGHT_BLACK[] = ESC "100m";
+        inline constexpr const char BG_BRIGHT_RED[] = ESC "101m";
+        inline constexpr const char BG_BRIGHT_GREEN[] = ESC "102m";
+        inline constexpr const char BG_BRIGHT_YELLOW[] = ESC "103m";
+        inline constexpr const char BG_BRIGHT_BLUE[] = ESC "104m";
+        inline constexpr const char BG_BRIGHT_MAGENTA[] = ESC "105m";
+        inline constexpr const char BG_BRIGHT_CYAN[] = ESC "106m";
+        inline constexpr const char BG_BRIGHT_WHITE[] = ESC "107m";
+        // keycodes
+        inline constexpr const unsigned long KEY_UNKNOWN = 0x1'0000'0000lu;
+        // default keycodes
+        inline constexpr const unsigned long KEY_ARROW_LEFT = 0x1'0000'0001lu;
+        inline constexpr const unsigned long KEY_ARROW_RIGHT = 0x1'0000'0002lu;
+        inline constexpr const unsigned long KEY_ARROW_UP = 0x1'0000'0003lu;
+        inline constexpr const unsigned long KEY_ARROW_DOWN = 0x1'0000'0004lu;
+        inline constexpr const unsigned long KEY_DELETE = 0x1'0000'0005lu;
+        inline constexpr const unsigned long KEY_F1 = 0x1'0000'0006lu;
+        inline constexpr const unsigned long KEY_F2 = 0x1'0000'0007lu;
+        inline constexpr const unsigned long KEY_F3 = 0x1'0000'0008lu;
+        inline constexpr const unsigned long KEY_F4 = 0x1'0000'0009lu;
+        inline constexpr const unsigned long KEY_F5 = 0x1'0000'000Alu;
+        inline constexpr const unsigned long KEY_F6 = 0x1'0000'000Blu;
+        inline constexpr const unsigned long KEY_F7 = 0x1'0000'000Clu;
+        inline constexpr const unsigned long KEY_F8 = 0x1'0000'000Dlu;
+        inline constexpr const unsigned long KEY_F9 = 0x1'0000'000Elu;
+        inline constexpr const unsigned long KEY_F10 = 0x1'0000'000Flu;
+        inline constexpr const unsigned long KEY_F11 = 0x1'0000'0010lu;
+        inline constexpr const unsigned long KEY_F12 = 0x1'0000'0011lu;
+        inline constexpr const unsigned long KEY_PAGE_UP = 0x1'0000'0012lu;
+        inline constexpr const unsigned long KEY_PAGE_DOWN = 0x1'0000'0013lu;
+        inline constexpr const unsigned long KEY_HOME = 0x1'0000'0014lu;
+        inline constexpr const unsigned long KEY_END = 0x1'0000'0015lu;
+        // key modifiers
+        inline constexpr const unsigned long KEY_CTRL = 0x2'0000'0000lu;
+        inline constexpr const unsigned long KEY_SHIFT = 0x4'0000'0000lu;
+        inline constexpr const unsigned long KEY_ALT = 0x8'0000'0000lu;
+    }
+
+    void enterAlternativeTerminal();
+    void leaveAlternativeTerminal();
+    IntVector2 getTerminalSize();
+    void clearTerminal();
+    void clearTerminalLine();
+
+    void hideCursor();
+    void showCursor();
+    void resetCursor();
+    void moveCursorLeft(int i);
+    void moveCursorRight(int i);
+    void moveCursorUp(int i);
+    void moveCursorDown(int i);
+
+    bool enterRawTerminal();
+    bool leaveRawTerminal();
+    u64 getRawChar();
+    bool isSpecialChar(u64 u);
+
+    struct SpecialChar {
+        u64 key = 0;
+        bool control = 0;
+        bool shift = 0;
+        bool alt = 0;
+    };
+
+    SpecialChar convertToSpecialChar(u64 u);
+}

+ 60 - 0
modules/Test.cppm

@@ -0,0 +1,60 @@
+module;
+
+#include <cstdio>
+#include <source_location>
+
+export module Core.Test;
+
+import Core.Logger;
+import Core.Terminal;
+import Core.Meta;
+import Core.ToString;
+
+#define SOURCE const std::source_location& l = std::source_location::current()
+
+export namespace Core {
+    void finalizeTests(void);
+    bool addTestResult(const char* file, bool comparison);
+
+    template<typename A, typename B>
+    bool test(const A& wanted, const B& actual, SOURCE) {
+        const char* file = getShortFileName(l.file_name());
+        if(addTestResult(file, static_cast<const B&>(wanted) == actual)) {
+            return true;
+        }
+        char buffer[512];
+        formatBuffer(
+            buffer, sizeof(buffer), "#@#:# - expected '#' got '#'#",
+            Terminal::FG_RED, file, l.line(), wanted, actual, Terminal::RESET);
+        puts(buffer);
+        return false;
+    }
+
+    template<typename T>
+    bool testTrue(const T& actual) {
+        return test<T>(true, actual);
+    }
+
+    template<typename T>
+    bool testFalse(const T& actual) {
+        return test<T>(false, actual);
+    }
+
+    bool testString(const char* wanted, const char* actual, SOURCE);
+
+    template<typename A, typename B>
+    bool testString(const A& wanted, const B& actual, SOURCE) {
+        char wantedString[512];
+        size_t lw = toString(wanted, wantedString, sizeof(wantedString));
+        char actualString[512];
+        size_t la = toString(actual, actualString, sizeof(actualString));
+        test(lw, la, l);
+        return testString(
+            static_cast<const char*>(wantedString),
+            static_cast<const char*>(actualString), l);
+    }
+
+    bool testFloat(float wanted, float actual, float error, SOURCE);
+    bool testNull(const void* p, SOURCE);
+    bool testNotNull(const void* p, SOURCE);
+}

+ 4 - 4
include/core/Thread.hpp → modules/Thread.cppm

@@ -1,10 +1,11 @@
-#ifndef CORE_THREAD_HPP
-#define CORE_THREAD_HPP
+module;
 
 #include <mutex>
 #include <thread>
 
-namespace Core {
+export module Core.Thread;
+
+export namespace Core {
     class Mutex final {
         std::mutex mutex{};
 
@@ -42,4 +43,3 @@ namespace Core {
         bool join();
     };
 }
-#endif

+ 6 - 7
include/core/ToString.hpp → modules/ToString.cppm

@@ -1,13 +1,14 @@
-#ifndef CORE_TO_STRING_HPP
-#define CORE_TO_STRING_HPP
+module;
 
 #include <cstdio>
 #include <cstring>
 
-#include "core/Math.hpp"
-#include "core/Meta.hpp"
+export module Core.ToString;
 
-namespace Core {
+import Core.Math;
+import Core.Meta;
+
+export namespace Core {
     size_t toString(signed char v, char* s, size_t n);
     size_t toString(char v, char* s, size_t n);
     size_t toString(short v, char* s, size_t n);
@@ -85,5 +86,3 @@ namespace Core {
         return copyFormatUntil(format, s, n);
     }
 }
-
-#endif

+ 15 - 0
modules/Types.cppm

@@ -0,0 +1,15 @@
+module;
+
+#include <cstdint>
+
+export module Core.Types;
+
+export using i64 = int64_t;
+export using i32 = int32_t;
+export using i16 = int16_t;
+export using i8 = int8_t;
+export using u64 = uint64_t;
+export using u32 = uint32_t;
+export using u16 = uint16_t;
+export using u8 = uint8_t;
+export using size_t = decltype(sizeof(int));

+ 5 - 6
include/core/Unicode.hpp → modules/Unicode.cppm

@@ -1,9 +1,10 @@
-#ifndef CORE_UNICODE_HPP
-#define CORE_UNICODE_HPP
+module;
 
-#include "core/Types.hpp"
+export module Core.Unicode;
 
-namespace Core {
+import Core.Types;
+
+export namespace Core {
     struct UTF8 {
         u8 data[4] = {0};
         u32 length = 0;
@@ -14,5 +15,3 @@ namespace Core {
     bool isUTF8Remainder(u8 c);
     u32 getUTF8Length(u8 c);
 }
-
-#endif

+ 4 - 5
include/core/UniquePointer.hpp → modules/UniquePointer.cppm

@@ -1,7 +1,8 @@
-#ifndef CORE_UNIQUE_POINTER_HPP
-#define CORE_UNIQUE_POINTER_HPP
+module;
 
-namespace Core {
+export module Core.UniquePointer;
+
+export namespace Core {
     template<typename T>
     class UniquePointer final {
         T* t;
@@ -50,5 +51,3 @@ namespace Core {
         }
     };
 }
-
-#endif

+ 94 - 0
modules/Utility.cppm

@@ -0,0 +1,94 @@
+module;
+
+#include <cstddef>
+#include <source_location>
+
+export module Core.Utility;
+
+import Core.Meta;
+
+#define SOURCE const std::source_location& l = std::source_location::current()
+
+#ifdef CHECK_MEMORY
+export void* operator new(size_t count, const std::source_location& l);
+export void* operator new[](size_t count, const std::source_location& l);
+#endif
+
+export namespace Core {
+    template<typename T, typename C = int>
+    C popCount(const T& t) {
+        static constexpr C map[16] = {0, 1, 1, 2, 1, 2, 2, 3,
+                                      1, 2, 2, 3, 2, 3, 3, 4};
+        C sum = 0;
+        for(size_t i = 0; i < sizeof(T) * 8; i += 4) {
+            sum += map[(t >> i) & 0xF];
+        }
+        return sum;
+    }
+
+    using ExitHandler = void (*)(int, void*);
+    [[noreturn]] void exitWithHandler(int value, SOURCE);
+    void setExitHandler(ExitHandler h, void* data);
+
+    using OutOfMemoryHandler = void (*)(void*);
+    void setOutOfMemoryHandler(OutOfMemoryHandler h, void* data);
+
+#ifdef CHECK_MEMORY
+    void* allocateRaw(size_t n, SOURCE);
+    void* zeroAllocateRaw(size_t n, SOURCE);
+    void* reallocateRaw(void* p, size_t n, SOURCE);
+    void deallocateRaw(void* p, SOURCE);
+
+    template<typename T, typename... Args>
+    T* newWithSource(Args&&... args) {
+        return new(std::source_location::current())
+            T(Core::forward<Args>(args)...);
+    }
+
+    template<typename T>
+    T* newWithSourceN(size_t n) {
+        return new(std::source_location::current()) T[n];
+    }
+#else
+    void* allocateRaw(size_t n);
+    void* zeroAllocateRaw(size_t n);
+    void* reallocateRaw(void* p, size_t n);
+    void deallocateRaw(void* p);
+
+    template<typename T, typename... Args>
+    T* newWithSource(Args&&... args) {
+        return new T(Core::forward<Args>(args)...);
+    }
+
+    template<typename T>
+    T* newWithSourceN(size_t n) {
+        return new T[n];
+    }
+#endif
+    void printMemoryReport();
+
+    template<typename T>
+    void deleteWithSource(T* p) {
+        delete p;
+    }
+
+    template<typename T>
+    void deleteWithSourceN(T* p) {
+        delete[] p;
+    }
+
+    template<typename T>
+    void bubbleSort(T* data, size_t n) {
+        bool swapped = true;
+        while(swapped && n > 0) {
+            swapped = false;
+            n--;
+            for(size_t i = 0; i < n; i++) {
+                if(data[i] > data[i + 1]) {
+                    swap(data[i], data[i + 1]);
+                    swapped = true;
+                }
+            }
+        }
+    }
+}

+ 8 - 8
include/core/Vector.hpp → modules/Vector.cppm

@@ -1,12 +1,14 @@
-#ifndef CORE_VECTOR_HPP
-#define CORE_VECTOR_HPP
+module;
 
 #include <cmath>
 
-#include "core/Math.hpp"
-#include "core/ToString.hpp"
+export module Core.Vector;
 
-namespace Core {
+import Core.Math;
+import Core.Meta;
+import Core.ToString;
+
+export namespace Core {
     template<size_t N, typename T>
     class alignas(sizeof(T) * (Core::isPowerOf2(N) ? N : 1)) Vector final {
         T values[N];
@@ -234,9 +236,7 @@ namespace Core {
     Vector3 cross(const Vector3& a, const Vector3& b);
 }
 
-template<size_t N, typename T>
+export template<size_t N, typename T>
 Core::Vector<N, T> operator*(T factor, const Core::Vector<N, T>& v) {
     return v * factor;
 }
-
-#endif

+ 7 - 6
include/core/View.hpp → modules/View.cppm

@@ -1,9 +1,12 @@
-#ifndef CORE_VIEW_HPP
-#define CORE_VIEW_HPP
+module;
 
-#include "core/Matrix.hpp"
+export module Core.View;
 
-namespace Core {
+import Core.Matrix;
+import Core.Vector;
+import Core.Quaternion;
+
+export namespace Core {
     class View {
         Matrix view{};
         Vector3 back{};
@@ -25,5 +28,3 @@ namespace Core {
         const Vector3& getUp() const;
     };
 }
-
-#endif

+ 13 - 12
performance/Main.cpp

@@ -1,7 +1,8 @@
-#include "core/Clock.hpp"
-#include "core/HashMap.hpp"
-#include "core/Logger.hpp"
-#include "core/Random.hpp"
+import Core.Clock;
+import Core.HashMap;
+import Core.Logger;
+import Core.Random;
+import Core.Types;
 
 using HashMapInt = Core::HashMap<int, int>;
 using Core::Clock;
@@ -37,7 +38,7 @@ static void fillOrder(HashMapInt& m) {
     for(int i = 0; i < 10'000; i++) {
         m.put(i, i * i);
     }
-    LOG_INFO("Fill Order: #ns", Clock::getNanos() - nanos);
+    Core::logInfo("Fill Order: #ns", Clock::getNanos() - nanos);
 }
 
 static void fillChaos(HashMapInt& m) {
@@ -47,7 +48,7 @@ static void fillChaos(HashMapInt& m) {
         int r = random.nextI32(0, 10'000);
         m.put(r, r * r);
     }
-    LOG_INFO("Fill Chaos: #ns", Clock::getNanos() - nanos);
+    Core::logInfo("Fill Chaos: #ns", Clock::getNanos() - nanos);
 }
 
 static i64 average(HashMapInt& m, i64 (*f)(const HashMapInt& m), int n) {
@@ -61,17 +62,17 @@ static i64 average(HashMapInt& m, i64 (*f)(const HashMapInt& m), int n) {
 static void order(int n) {
     HashMapInt m;
     fillOrder(m);
-    LOG_INFO("Order Probing");
-    LOG_INFO("Search | #ms", average(m, testSearch, n));
-    LOG_INFO("EmptySearch | #ms", average(m, testEmptySearch, n));
+    Core::logInfo("Order Probing");
+    Core::logInfo("Search | #ms", average(m, testSearch, n));
+    Core::logInfo("EmptySearch | #ms", average(m, testEmptySearch, n));
 }
 
 static void chaos(int n) {
     HashMapInt m;
     fillChaos(m);
-    LOG_INFO("Chaos Probing");
-    LOG_INFO("Search | #ms", average(m, testSearch, n));
-    LOG_INFO("EmptySearch | #ms", average(m, testEmptySearch, n));
+    Core::logInfo("Chaos Probing");
+    Core::logInfo("Search | #ms", average(m, testSearch, n));
+    Core::logInfo("EmptySearch | #ms", average(m, testEmptySearch, n));
 }
 
 int main() {

+ 9 - 6
src/BitArray.cpp

@@ -1,11 +1,14 @@
-#include "core/BitArray.hpp"
+module;
 
 #include <cassert>
 #include <cstring>
 
-#include "core/BitArray.hpp"
-#include "core/ToString.hpp"
-#include "core/Utility.hpp"
+module Core.BitArray;
+
+import Core.ToString;
+import Core.Math;
+import Core.Meta;
+import Core.Utility;
 
 using Core::BitArray;
 
@@ -64,7 +67,7 @@ BitArray::BitArray(BitArray&& other) noexcept : BitArray() {
 }
 
 BitArray::~BitArray() {
-    coreDeleteN(data);
+    Core::deleteWithSourceN(data);
 }
 
 BitArray& BitArray::operator=(BitArray other) noexcept {
@@ -138,7 +141,7 @@ void BitArray::resize(size_t newLength, size_t newBits) {
         newBits = 64;
     }
     size_t arrayLength = getArrayLength(newLength, newBits);
-    u64* newData = coreNewN(u64, arrayLength);
+    u64* newData = newWithSourceN<u64>(arrayLength);
     memset(newData, 0, arrayLength * sizeof(u64));
 
     size_t end = Core::min(length, newLength);

+ 3 - 1
src/Box.cpp

@@ -1,7 +1,9 @@
-#include "core/Box.hpp"
+module;
 
 #include <cstdio>
 
+module Core.Box;
+
 using Core::Box;
 
 Box::Box(const Vector3& min_, const Vector3& max_) : min(min_), max(max_) {

+ 6 - 4
src/Buffer.cpp

@@ -1,10 +1,12 @@
-#include "core/Buffer.hpp"
+module;
 
 #include <cstring>
 
-#include "core/Buffer.hpp"
-#include "core/Math.hpp"
-#include "core/Utility.hpp"
+module Core.Buffer;
+
+import Core.Math;
+import Core.Utility;
+import Core.Meta;
 
 Core::Buffer::Buffer() : length(0), capacity(0), buffer(nullptr) {
 }

+ 3 - 1
src/Clock.cpp

@@ -1,10 +1,12 @@
-#include "core/Clock.hpp"
+module;
 
 #include <chrono>
 #include <thread>
 
 #include "ErrorSimulator.hpp"
 
+module Core.Clock;
+
 using Core::Clock;
 
 Clock::Clock() : index(0), last(0), sum(0), time({}) {

+ 30 - 0
src/CustomNewDelete.cpp

@@ -0,0 +1,30 @@
+#include <cstddef>
+#include <source_location>
+
+import Core.Utility;
+
+#ifdef CHECK_MEMORY
+void* operator new(size_t count) {
+    return Core::allocateRaw(count, std::source_location());
+}
+
+void* operator new[](size_t count) {
+    return Core::allocateRaw(count, std::source_location());
+}
+
+void operator delete(void* p) noexcept {
+    Core::deallocateRaw(p, std::source_location());
+}
+
+void operator delete(void* p, size_t) noexcept {
+    Core::deallocateRaw(p, std::source_location());
+}
+
+void operator delete[](void* p) noexcept {
+    Core::deallocateRaw(p, std::source_location());
+}
+
+void operator delete[](void* p, size_t) noexcept {
+    Core::deallocateRaw(p, std::source_location());
+}
+#endif

+ 11 - 8
src/File.cpp

@@ -1,32 +1,35 @@
-#include "core/File.hpp"
+module;
 
 #include <stdio.h>
 
 #include "ErrorSimulator.hpp"
-#include "core/Logger.hpp"
+
+module Core.File;
+
+import Core.Logger;
 
 using Core::LogLevel;
 
 static bool readOpenFile(FILE* file, Core::List<char>& f, const char* path) {
     if(FAIL_STEP || fseek(file, 0, SEEK_END)) {
-        REPORT(LogLevel::ERROR, "cannot seek file end of '#'", path);
+        Core::report(LogLevel::ERROR, "cannot seek file end of '#'", path);
         return true;
     }
     long l = ftell(file);
     if(FAIL_STEP || l < 0) {
-        REPORT(LogLevel::ERROR, "cannot tell file position of '#'", path);
+        Core::report(LogLevel::ERROR, "cannot tell file position of '#'", path);
         return true;
     }
     size_t size = static_cast<size_t>(l);
     f.resize(size + 1);
     if(FAIL_STEP || fseek(file, 0, SEEK_SET)) {
-        REPORT(LogLevel::ERROR, "cannot seek file start of '#'", path);
+        Core::report(LogLevel::ERROR, "cannot seek file start of '#'", path);
         return true;
     }
     size_t read = fread(&f[0], 1, size, file);
     f.getLast() = 0;
     if(FAIL_STEP || read != size) {
-        REPORT(
+        Core::report(
             LogLevel::ERROR, "expected to read # bytes from '#' but read #",
             size, path, read);
         return true;
@@ -37,12 +40,12 @@ static bool readOpenFile(FILE* file, Core::List<char>& f, const char* path) {
 bool Core::readFile(List<char>& f, const char* path) {
     FILE* file = fopen(path, "rb");
     if(file == nullptr) {
-        REPORT(LogLevel::ERROR, "cannot read file '#'", path);
+        report(LogLevel::ERROR, "cannot read file '#'", path);
         return true;
     }
     bool r = readOpenFile(file, f, path);
     if(FAIL_STEP || fclose(file)) {
-        REPORT(LogLevel::ERROR, "cannot close file '#'", path);
+        report(LogLevel::ERROR, "cannot close file '#'", path);
         r = true;
     }
     return r;

+ 3 - 1
src/Frustum.cpp

@@ -1,7 +1,9 @@
-#include "core/Frustum.hpp"
+module;
 
 #include <cmath>
 
+module Core.Frustum;
+
 using Core::Frustum;
 using V3 = Core::Vector3;
 using V4 = Core::Vector4;

+ 7 - 3
src/Logger.cpp

@@ -1,4 +1,8 @@
-#include "core/Logger.hpp"
+module;
+
+#include <source_location>
+
+module Core.Logger;
 
 Core::LogLevel Core::logLevel = Core::LogLevel::DEBUG;
 static Core::ReportHandler reportHandler = nullptr;
@@ -10,9 +14,9 @@ void Core::setReportHandler(ReportHandler h, void* data) {
 }
 
 void Core::callReportHandler(
-    LogLevel l, const char* file, int line, const char* report) {
+    LogLevel l, const char* report, const std::source_location& sl) {
     if(reportHandler != nullptr) {
-        reportHandler(l, file, line, reportData, report);
+        reportHandler(l, sl, reportData, report);
     }
 }
 

+ 7 - 1
src/Matrix.cpp

@@ -1,4 +1,10 @@
-#include "core/Matrix.hpp"
+module;
+
+#include <math.h>
+
+module Core.Matrix;
+
+import Core.ToString;
 
 Core::Matrix::Matrix() {
     unit();

+ 3 - 1
src/Plane.cpp

@@ -1,7 +1,9 @@
-#include "core/Plane.hpp"
+module;
 
 #include <cstdio>
 
+module Core.Plane;
+
 using Core::Plane;
 
 Plane::Plane() : abc(), d(0) {

+ 7 - 2
src/Quaternion.cpp

@@ -1,6 +1,11 @@
-#include "core/Quaternion.hpp"
+module;
 
-#include <cmath>
+#include <math.h>
+#include <stdio.h>
+
+module Core.Quaternion;
+
+import Core.Math;
 
 Core::Quaternion::Quaternion() : v(0.0f, 0.0f, 0.0f, 1.0f) {
 }

+ 3 - 1
src/Random.cpp

@@ -1,7 +1,9 @@
-#include "core/Random.hpp"
+module;
 
 #include <cstring>
 
+module Core.Random;
+
 using Core::Random;
 
 Random::Random(u32 seed) : data(), index(0) {

+ 22 - 15
src/ReadLine.cpp

@@ -1,13 +1,20 @@
-#include "core/ReadLine.hpp"
+module;
 
 #include <atomic>
 
-#include "core/Array.hpp"
-#include "core/Clock.hpp"
-#include "core/Logger.hpp"
-#include "core/Queue.hpp"
-#include "core/Thread.hpp"
-#include "core/Unicode.hpp"
+#include <ctype.h>
+#include <stdio.h>
+
+module Core.ReadLine;
+
+import Core.Array;
+import Core.Clock;
+import Core.Logger;
+import Core.Queue;
+import Core.Thread;
+import Core.Terminal;
+import Core.Types;
+import Core.Unicode;
 
 static constexpr size_t HISTORY_LENGTH = 10;
 static constexpr size_t CONSOLE_BUFFER_SIZE = 256;
@@ -168,15 +175,15 @@ static void handleChars() {
         u64 c = Core::getRawChar();
         if(c == 0) {
             break;
-        } else if(c == TERMINAL_KEY_ARROW_UP) {
+        } else if(c == Core::Terminal::KEY_ARROW_UP) {
             handleUpArrow();
-        } else if(c == TERMINAL_KEY_ARROW_DOWN) {
+        } else if(c == Core::Terminal::KEY_ARROW_DOWN) {
             handleDownArrow();
-        } else if(c == TERMINAL_KEY_ARROW_RIGHT) {
+        } else if(c == Core::Terminal::KEY_ARROW_RIGHT) {
             handleRightArrow();
-        } else if(c == TERMINAL_KEY_ARROW_LEFT) {
+        } else if(c == Core::Terminal::KEY_ARROW_LEFT) {
             handleLeftArrow();
-        } else if(c == TERMINAL_KEY_DELETE) {
+        } else if(c == Core::Terminal::KEY_DELETE) {
             if(move > 0) {
                 handleRightArrow();
                 removeChar();
@@ -202,11 +209,11 @@ static void loop(void*) {
 
 bool Core::startReadLine(void) {
     if(enterRawTerminal()) {
-        REPORT(LogLevel::WARNING, "cannot set terminal attributes");
+        report(LogLevel::WARNING, "cannot set terminal attributes");
     }
     running = true;
     if(readThread.start(loop, nullptr)) {
-        REPORT(LogLevel::ERROR, "cannot start read thread");
+        report(LogLevel::ERROR, "cannot start read thread");
         stopReadLine();
         return true;
     }
@@ -227,7 +234,7 @@ void Core::stopReadLine() {
     running = false;
     readThread.join();
     if(Core::leaveRawTerminal()) {
-        REPORT(LogLevel::WARNING, "cannot restore terminal attributes");
+        report(LogLevel::WARNING, "cannot restore terminal attributes");
     }
     buffer.clear();
 }

+ 68 - 65
src/Terminal.cpp

@@ -1,4 +1,4 @@
-#include "core/Terminal.hpp"
+module;
 
 #include <cstdio>
 
@@ -6,9 +6,11 @@
 #include <termios.h>
 #include <unistd.h>
 
-#include "core/Array.hpp"
-#include "core/Logger.hpp"
-#include "core/Unicode.hpp"
+module Core.Terminal;
+
+import Core.Array;
+import Core.Logger;
+import Core.Unicode;
 
 #define ESC "\33["
 #define esc(s) fputs(ESC s, stdout)
@@ -21,64 +23,64 @@ struct EscapeSequence {
     u64 mapping = 0;
 };
 
-#define K_CTRL TERMINAL_KEY_CTRL
-#define K_SHIFT TERMINAL_KEY_SHIFT
-#define K_ALT TERMINAL_KEY_ALT
-
-#define KEY_WITH_MODIFIER(c, m, n) {c, m | TERMINAL_KEY_##n}
-#define KEYS_WITH_MODIFIER(c, m)                       \
-    {u8"[1;" c "A", m | TERMINAL_KEY_ARROW_UP},        \
-        {u8"[1;" c "B", m | TERMINAL_KEY_ARROW_DOWN},  \
-        {u8"[1;" c "C", m | TERMINAL_KEY_ARROW_RIGHT}, \
-        {u8"[1;" c "D", m | TERMINAL_KEY_ARROW_LEFT},  \
-        {u8"[3;" c "~", m | TERMINAL_KEY_DELETE},      \
-        {u8"[1;" c "P", m | TERMINAL_KEY_F1},          \
-        {u8"[1;" c "Q", m | TERMINAL_KEY_F2},          \
-        {u8"[1;" c "R", m | TERMINAL_KEY_F3},          \
-        {u8"[1;" c "S", m | TERMINAL_KEY_F4},          \
-        {u8"[15;" c "~", m | TERMINAL_KEY_F5},         \
-        {u8"[17;" c "~", m | TERMINAL_KEY_F6},         \
-        {u8"[18;" c "~", m | TERMINAL_KEY_F7},         \
-        {u8"[19;" c "~", m | TERMINAL_KEY_F8},         \
-        {u8"[20;" c "~", m | TERMINAL_KEY_F9},         \
-        {u8"[21;" c "~", m | TERMINAL_KEY_F10},        \
-        {u8"[23;" c "~", m | TERMINAL_KEY_F11},        \
-        {u8"[24;" c "~", m | TERMINAL_KEY_F12},        \
-        {u8"[5;" c "~", m | TERMINAL_KEY_PAGE_UP},     \
-        {u8"[6;" c "~", m | TERMINAL_KEY_PAGE_DOWN},   \
-        {u8"[1;" c "H", m | TERMINAL_KEY_HOME}, {      \
-        u8"[1;" c "F", m | TERMINAL_KEY_END            \
+#define K_CTRL Core::Terminal::KEY_CTRL
+#define K_SHIFT Core::Terminal::KEY_SHIFT
+#define K_ALT Core::Terminal::KEY_ALT
+
+#define KEY_WITH_MODIFIER(c, m, n) {c, m | Core::Terminal::KEY_##n}
+#define KEYS_WITH_MODIFIER(c, m)                              \
+    {u8"[1;" c "A", m | Core::Terminal::KEY_ARROW_UP},        \
+        {u8"[1;" c "B", m | Core::Terminal::KEY_ARROW_DOWN},  \
+        {u8"[1;" c "C", m | Core::Terminal::KEY_ARROW_RIGHT}, \
+        {u8"[1;" c "D", m | Core::Terminal::KEY_ARROW_LEFT},  \
+        {u8"[3;" c "~", m | Core::Terminal::KEY_DELETE},      \
+        {u8"[1;" c "P", m | Core::Terminal::KEY_F1},          \
+        {u8"[1;" c "Q", m | Core::Terminal::KEY_F2},          \
+        {u8"[1;" c "R", m | Core::Terminal::KEY_F3},          \
+        {u8"[1;" c "S", m | Core::Terminal::KEY_F4},          \
+        {u8"[15;" c "~", m | Core::Terminal::KEY_F5},         \
+        {u8"[17;" c "~", m | Core::Terminal::KEY_F6},         \
+        {u8"[18;" c "~", m | Core::Terminal::KEY_F7},         \
+        {u8"[19;" c "~", m | Core::Terminal::KEY_F8},         \
+        {u8"[20;" c "~", m | Core::Terminal::KEY_F9},         \
+        {u8"[21;" c "~", m | Core::Terminal::KEY_F10},        \
+        {u8"[23;" c "~", m | Core::Terminal::KEY_F11},        \
+        {u8"[24;" c "~", m | Core::Terminal::KEY_F12},        \
+        {u8"[5;" c "~", m | Core::Terminal::KEY_PAGE_UP},     \
+        {u8"[6;" c "~", m | Core::Terminal::KEY_PAGE_DOWN},   \
+        {u8"[1;" c "H", m | Core::Terminal::KEY_HOME}, {      \
+        u8"[1;" c "F", m | Core::Terminal::KEY_END            \
     }
 
 static const Core::Array<EscapeSequence, 175> ESCAPE_SEQUENCES = {{
-    {u8"[A", TERMINAL_KEY_ARROW_UP},
-    {u8"[B", TERMINAL_KEY_ARROW_DOWN},
-    {u8"[C", TERMINAL_KEY_ARROW_RIGHT},
-    {u8"[D", TERMINAL_KEY_ARROW_LEFT},
-    {u8"[3~", TERMINAL_KEY_DELETE},
-    {u8"OP", TERMINAL_KEY_F1},
-    {u8"[[A", TERMINAL_KEY_F1},
-    {u8"OQ", TERMINAL_KEY_F2},
-    {u8"[[B", TERMINAL_KEY_F2},
-    {u8"OR", TERMINAL_KEY_F3},
-    {u8"[[C", TERMINAL_KEY_F3},
-    {u8"OS", TERMINAL_KEY_F4},
-    {u8"[[D", TERMINAL_KEY_F4},
-    {u8"[15~", TERMINAL_KEY_F5},
-    {u8"[[E", TERMINAL_KEY_F5},
-    {u8"[17~", TERMINAL_KEY_F6},
-    {u8"[18~", TERMINAL_KEY_F7},
-    {u8"[19~", TERMINAL_KEY_F8},
-    {u8"[20~", TERMINAL_KEY_F9},
-    {u8"[21~", TERMINAL_KEY_F10},
-    {u8"[23~", TERMINAL_KEY_F11},
-    {u8"[24~", TERMINAL_KEY_F12},
-    {u8"[5~", TERMINAL_KEY_PAGE_UP},
-    {u8"[6~", TERMINAL_KEY_PAGE_DOWN},
-    {u8"[H", TERMINAL_KEY_HOME},
-    {u8"[1~", TERMINAL_KEY_HOME},
-    {u8"[F", TERMINAL_KEY_END},
-    {u8"[4~", TERMINAL_KEY_END},
+    {u8"[A", Core::Terminal::KEY_ARROW_UP},
+    {u8"[B", Core::Terminal::KEY_ARROW_DOWN},
+    {u8"[C", Core::Terminal::KEY_ARROW_RIGHT},
+    {u8"[D", Core::Terminal::KEY_ARROW_LEFT},
+    {u8"[3~", Core::Terminal::KEY_DELETE},
+    {u8"OP", Core::Terminal::KEY_F1},
+    {u8"[[A", Core::Terminal::KEY_F1},
+    {u8"OQ", Core::Terminal::KEY_F2},
+    {u8"[[B", Core::Terminal::KEY_F2},
+    {u8"OR", Core::Terminal::KEY_F3},
+    {u8"[[C", Core::Terminal::KEY_F3},
+    {u8"OS", Core::Terminal::KEY_F4},
+    {u8"[[D", Core::Terminal::KEY_F4},
+    {u8"[15~", Core::Terminal::KEY_F5},
+    {u8"[[E", Core::Terminal::KEY_F5},
+    {u8"[17~", Core::Terminal::KEY_F6},
+    {u8"[18~", Core::Terminal::KEY_F7},
+    {u8"[19~", Core::Terminal::KEY_F8},
+    {u8"[20~", Core::Terminal::KEY_F9},
+    {u8"[21~", Core::Terminal::KEY_F10},
+    {u8"[23~", Core::Terminal::KEY_F11},
+    {u8"[24~", Core::Terminal::KEY_F12},
+    {u8"[5~", Core::Terminal::KEY_PAGE_UP},
+    {u8"[6~", Core::Terminal::KEY_PAGE_DOWN},
+    {u8"[H", Core::Terminal::KEY_HOME},
+    {u8"[1~", Core::Terminal::KEY_HOME},
+    {u8"[F", Core::Terminal::KEY_END},
+    {u8"[4~", Core::Terminal::KEY_END},
     KEYS_WITH_MODIFIER("2", K_SHIFT),
     KEYS_WITH_MODIFIER("3", K_ALT),
     KEYS_WITH_MODIFIER("4", K_ALT | K_SHIFT),
@@ -196,8 +198,9 @@ static u64 readEscapeSequence() {
         }
         s[l++] = readChar();
     }
-    REPORT(LogLevel::WARNING, "Unknown escape sequence starting with '#'", s);
-    return TERMINAL_KEY_UNKNOWN;
+    Core::report(
+        LogLevel::WARNING, "Unknown escape sequence starting with '#'", s);
+    return Core::Terminal::KEY_UNKNOWN;
 }
 
 static u64 readUTF8() {
@@ -219,14 +222,14 @@ u64 Core::getRawChar() {
 }
 
 bool Core::isSpecialChar(u64 u) {
-    return u >= TERMINAL_KEY_UNKNOWN;
+    return u >= Core::Terminal::KEY_UNKNOWN;
 }
 
 Core::SpecialChar Core::convertToSpecialChar(u64 u) {
     SpecialChar c;
-    c.shift = TERMINAL_KEY_SHIFT & u;
-    c.control = TERMINAL_KEY_CTRL & u;
-    c.alt = TERMINAL_KEY_ALT & u;
+    c.shift = Terminal::KEY_SHIFT & u;
+    c.control = Terminal::KEY_CTRL & u;
+    c.alt = Terminal::KEY_ALT & u;
     c.key = u & 0x1'FFFF'FFFF;
     return c;
 }

+ 28 - 25
src/Test.cpp

@@ -1,12 +1,15 @@
-#include "core/Test.hpp"
+module;
+
+#include <source_location>
 
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include "core/Logger.hpp"
-#include "core/Utility.hpp"
+module Core.Test;
+import Core.Logger;
+import Core.Utility;
 
 typedef struct {
     char* file;
@@ -42,10 +45,10 @@ void Core::finalizeTests(void) {
     for(size_t i = 0; i < resultsIndex; i++) {
         Result* r = results + i;
         bool c = r->successTests == r->tests;
-        fputs(c ? TERMINAL_GREEN : TERMINAL_RED, stdout);
+        fputs(c ? Terminal::FG_GREEN : Terminal::FG_RED, stdout);
         printf(
             "%s - %d / %d tests succeeded", r->file, r->successTests, r->tests);
-        puts(TERMINAL_RESET);
+        puts(Terminal::RESET);
         free(r->file);
     }
     Core::deallocateRaw(results);
@@ -64,46 +67,46 @@ bool Core::addTestResult(const char* file, bool comparison) {
     return false;
 }
 
-#define TEST_SUCCESS(result)                \
-    file = getShortFileName(file);          \
-    if(Core::addTestResult(file, result)) { \
-        return true;                        \
+#define TEST_SUCCESS(result)                            \
+    const char* file = getShortFileName(l.file_name()); \
+    if(Core::addTestResult(file, result)) {             \
+        return true;                                    \
     }
 
 bool Core::testString(
-    const char* file, int line, const char* wanted, const char* actual) {
+    const char* wanted, const char* actual, const std::source_location& l) {
     TEST_SUCCESS(strcmp(wanted, actual) == 0)
-    fputs(TERMINAL_RED, stdout);
-    printf("%s:%d - expected '%s' got '%s'", file, line, wanted, actual);
-    puts(TERMINAL_RESET);
+    fputs(Terminal::FG_RED, stdout);
+    printf("%s:%u - expected '%s' got '%s'", file, l.line(), wanted, actual);
+    puts(Terminal::RESET);
     return false;
 }
 
 bool Core::testFloat(
-    const char* file, int line, float wanted, float actual, float error) {
+    float wanted, float actual, float error, const std::source_location& l) {
     float diff = wanted - actual;
     diff = diff < 0.0f ? -diff : diff;
     TEST_SUCCESS(diff <= error)
-    fputs(TERMINAL_RED, stdout);
+    fputs(Terminal::FG_RED, stdout);
     printf(
-        "%s:%d - expected '%.3f' got '%.3f'", file, line,
+        "%s:%u - expected '%.3f' got '%.3f'", file, l.line(),
         static_cast<double>(wanted), static_cast<double>(actual));
-    puts(TERMINAL_RESET);
+    puts(Terminal::RESET);
     return false;
 }
 
-bool Core::testNull(const char* file, int line, const void* actual) {
+bool Core::testNull(const void* actual, const std::source_location& l) {
     TEST_SUCCESS(actual == nullptr)
-    fputs(TERMINAL_RED, stdout);
-    printf("%s:%d - expected null", file, line);
-    puts(TERMINAL_RESET);
+    fputs(Terminal::FG_RED, stdout);
+    printf("%s:%u - expected null", file, l.line());
+    puts(Terminal::RESET);
     return false;
 }
 
-bool Core::testNotNull(const char* file, int line, const void* actual) {
+bool Core::testNotNull(const void* actual, const std::source_location& l) {
     TEST_SUCCESS(actual != nullptr)
-    fputs(TERMINAL_RED, stdout);
-    printf("%s:%d - expected valid pointer", file, line);
-    puts(TERMINAL_RESET);
+    fputs(Terminal::FG_RED, stdout);
+    printf("%s:%u - expected valid pointer", file, l.line());
+    puts(Terminal::RESET);
     return false;
 }

+ 12 - 6
src/Thread.cpp

@@ -1,7 +1,13 @@
-#include "core/Thread.hpp"
+module;
+
+#include <thread>
 
 #include "ErrorSimulator.hpp"
-#include "core/Logger.hpp"
+
+module Core.Thread;
+
+import Core.Logger;
+import Core.Meta;
 
 using Core::Mutex;
 using Core::MutexGuard;
@@ -12,7 +18,7 @@ void Mutex::lock() noexcept {
         FAIL_STEP_THROW();
         mutex.lock();
     } catch(std::exception& e) {
-        REPORT(LogLevel::ERROR, "Could not lock mutex: #", e.what());
+        Core::report(LogLevel::ERROR, "Could not lock mutex: #", e.what());
     }
 }
 
@@ -21,7 +27,7 @@ void Mutex::unlock() noexcept {
         FAIL_STEP_THROW();
         mutex.unlock();
     } catch(std::exception& e) {
-        REPORT(LogLevel::ERROR, "Could not unlock mutex: #", e.what());
+        Core::report(LogLevel::ERROR, "Could not unlock mutex: #", e.what());
     }
 }
 
@@ -64,7 +70,7 @@ bool Thread::start(Function f, void* p) {
         FAIL_STEP_THROW();
         thread = std::thread([f, p]() { f(p); });
     } catch(std::exception& e) {
-        REPORT(LogLevel::ERROR, "Could not start thread: #", e.what());
+        Core::report(LogLevel::ERROR, "Could not start thread: #", e.what());
         return true;
     }
     return false;
@@ -76,7 +82,7 @@ bool Thread::join() {
             FAIL_STEP_THROW();
             thread.join();
         } catch(std::exception& e) {
-            REPORT(LogLevel::ERROR, "Could not join thread: #", e.what());
+            Core::report(LogLevel::ERROR, "Could not join thread: #", e.what());
             return true;
         }
     }

+ 4 - 2
src/ToString.cpp

@@ -1,8 +1,9 @@
-#include "core/ToString.hpp"
+module;
 
-#include <cstdarg>
 #include <cstdio>
 
+module Core.ToString;
+
 #define TO_STRING(type, format)                        \
     size_t Core::toString(type v, char* s, size_t n) { \
         int e = snprintf(s, n, format, v);             \
@@ -46,6 +47,7 @@ size_t Core::copyFormatUntil(const char*& format, char*& s, size_t& n) {
         char u = *(format++);
         if(u == '#') {
             if(*format != '#') {
+                format += *format == '@';
                 break;
             }
             format++;

+ 3 - 1
src/Unicode.cpp

@@ -1,4 +1,6 @@
-#include "core/Unicode.hpp"
+module;
+
+module Core.Unicode;
 
 Core::UTF8 Core::convertUnicodeToUTF8(u32 c) {
     UTF8 u = {};

+ 59 - 68
src/Utility.cpp

@@ -1,22 +1,28 @@
-#include "core/Utility.hpp"
+module;
 
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
+#include <new>
+#include <source_location>
 
-#include "core/Logger.hpp"
-#include "core/Thread.hpp"
+module Core.Utility;
+
+import Core.Logger;
+import Core.Thread;
+import Core.Types;
 
 static Core::ExitHandler exitHandler = nullptr;
 static void* exitData = nullptr;
 static Core::OutOfMemoryHandler outOfMemoryHandler = nullptr;
 static void* outOfMemoryData = nullptr;
 
-[[noreturn]] void Core::exitWithHandler(const char* file, int line, int value) {
+[[noreturn]] void Core::exitWithHandler(
+    int value, const std::source_location& l) {
     if(value != 0) {
-        file = getShortFileName(file);
-        LOG_ERROR("Exit from #:# with value #", file, line, value);
+        const char* file = getShortFileName(l.file_name());
+        logError("Exit from #:# with value #", file, l.line(), value);
     }
     if(exitHandler != nullptr) {
         exitHandler(value, exitData);
@@ -36,16 +42,16 @@ void Core::setOutOfMemoryHandler(OutOfMemoryHandler h, void* data) {
         if(outOfMemoryHandler != nullptr) {
             outOfMemoryHandler(outOfMemoryData);
         } else {
-            LOG_ERROR("Out of memory");
-            EXIT(1);
+            logError("Out of memory");
+            exitWithHandler(1);
         }
     });
 }
 
 static void* exitOnNull(void* p, size_t n) {
     if(p == nullptr) {
-        LOG_ERROR("Out of memory, requested '#' bytes", n);
-        EXIT(1);
+        Core::logError("Out of memory, requested '#' bytes", n);
+        Core::exitWithHandler(1);
     }
     return p;
 }
@@ -87,7 +93,7 @@ struct MemoryInfo {
     MemoryInfo* next;
     MemoryInfo* previous;
     size_t size;
-    int line;
+    u32 line;
     char buffer[64 - 2 * sizeof(void*) - sizeof(size_t) - sizeof(int)];
     char canary[sizeof(CANARY)];
 };
@@ -95,12 +101,15 @@ struct MemoryInfo {
 static_assert(sizeof(MemoryInfo) == 80, "memory info has invalid size");
 static MemoryInfo* headMemoryInfo = nullptr;
 
-static void addMemoryInfo(MemoryInfo* i, const char* file, int line, size_t n) {
+static void addMemoryInfo(
+    MemoryInfo* i, const std::source_location& l, size_t n) {
     i->next = nullptr;
     i->previous = nullptr;
     i->size = n;
-    i->line = line;
-    snprintf(i->buffer, sizeof(i->buffer), "%s", Core::getShortFileName(file));
+    i->line = l.line();
+    snprintf(
+        i->buffer, sizeof(i->buffer), "%s",
+        Core::getShortFileName(l.file_name()));
     memcpy(i->canary, CANARY, sizeof(CANARY));
     memcpy(reinterpret_cast<char*>(i) + n, CANARY, sizeof(CANARY));
     Core::MutexGuard mg(memoryInfoMutex);
@@ -132,20 +141,20 @@ static void removeMemoryInfo(MemoryInfo* info) {
     }
 }
 
-void* Core::debugAllocateRaw(const char* file, int line, size_t n) {
+void* Core::allocateRaw(size_t n, const std::source_location& l) {
     n += sizeof(MemoryInfo);
     void* p = realAllocate(n + sizeof(CANARY));
-    addMemoryInfo(static_cast<MemoryInfo*>(p), file, line, n);
+    addMemoryInfo(static_cast<MemoryInfo*>(p), l, n);
     return static_cast<char*>(p) + sizeof(MemoryInfo);
 }
 
-void* Core::debugZeroAllocateRaw(const char* file, int line, size_t n) {
-    void* p = debugAllocateRaw(file, line, n);
+void* Core::zeroAllocateRaw(size_t n, const std::source_location& l) {
+    void* p = allocateRaw(n, l);
     memset(p, 0, n);
     return p;
 }
 
-void* Core::debugReallocateRaw(const char* file, int line, void* p, size_t n) {
+void* Core::reallocateRaw(void* p, size_t n, const std::source_location& l) {
     if(n > 0) {
         n += sizeof(MemoryInfo) + sizeof(CANARY);
     }
@@ -158,7 +167,7 @@ void* Core::debugReallocateRaw(const char* file, int line, void* p, size_t n) {
     if(np == nullptr) {
         return nullptr;
     }
-    addMemoryInfo(static_cast<MemoryInfo*>(np), file, line, n - sizeof(CANARY));
+    addMemoryInfo(static_cast<MemoryInfo*>(np), l, n - sizeof(CANARY));
     return static_cast<char*>(np) + sizeof(MemoryInfo);
 }
 
@@ -166,7 +175,7 @@ static bool checkCanary(void* p) {
     return memcmp(p, CANARY, sizeof(CANARY)) != 0;
 }
 
-void Core::debugDeallocateRaw(void* p) {
+void Core::deallocateRaw(void* p, const std::source_location& l) {
     if(p == nullptr) {
         return;
     }
@@ -174,61 +183,26 @@ void Core::debugDeallocateRaw(void* p) {
     MemoryInfo* rp = static_cast<MemoryInfo*>(w);
     rp->buffer[sizeof(rp->buffer) - 1] = '\0'; // end might be broken
     if(checkCanary(rp->canary)) {
-        LOG_ERROR("Free at #:# violated pre canary", rp->buffer, rp->line);
-        EXIT(1);
+        logError(
+            "Free at #:# from #:# violated pre canary",
+            getShortFileName(l.file_name()), l.line(), rp->buffer, rp->line);
+        exitWithHandler(1, l);
     } else if(checkCanary(reinterpret_cast<char*>(rp) + rp->size)) {
-        LOG_ERROR("Free at #:# violated post canary", rp->buffer, rp->line);
-        EXIT(1);
+        logError(
+            "Free at #:# from #:# violated post canary",
+            getShortFileName(l.file_name()), l.line(), rp->buffer, rp->line);
+        exitWithHandler(1, l);
     }
     removeMemoryInfo(rp);
     realFree(rp);
 }
 
-void Core::printMemoryReport() {
-    Core::MutexGuard mg(memoryInfoMutex);
-    size_t counter = 0;
-    for(MemoryInfo* i = headMemoryInfo; i != nullptr; i = i->next) {
-        if(i->line < 0) {
-            counter++;
-        } else {
-            LOG_ERROR("#:# was not freed", i->buffer, i->line);
-        }
-    }
-    if(counter > 0) {
-        LOG_ERROR("# unknown entries were not freed", counter);
-    }
-}
-
-void* operator new(size_t count) {
-    return Core::debugAllocateRaw("unknown", -1, count);
-}
-
-void* operator new(size_t count, const char* file, int line) {
-    return Core::debugAllocateRaw(file, line, count);
+void* operator new(size_t count, const std::source_location& l) {
+    return Core::allocateRaw(count, l);
 }
 
-void* operator new[](size_t count) {
-    return Core::debugAllocateRaw("unknown", -1, count);
-}
-
-void* operator new[](size_t count, const char* file, int line) {
-    return Core::debugAllocateRaw(file, line, count);
-}
-
-void operator delete(void* p) noexcept {
-    Core::debugDeallocateRaw(p);
-}
-
-void operator delete(void* p, size_t) noexcept {
-    Core::debugDeallocateRaw(p);
-}
-
-void operator delete[](void* p) noexcept {
-    Core::debugDeallocateRaw(p);
-}
-
-void operator delete[](void* p, size_t) noexcept {
-    Core::debugDeallocateRaw(p);
+void* operator new[](size_t count, const std::source_location& l) {
+    return Core::allocateRaw(count, l);
 }
 
 #else
@@ -252,3 +226,20 @@ void Core::deallocateRaw(void* p) {
 }
 
 #endif
+
+void Core::printMemoryReport() {
+#ifdef CHECK_MEMORY
+    Core::MutexGuard mg(memoryInfoMutex);
+    size_t counter = 0;
+    for(MemoryInfo* i = headMemoryInfo; i != nullptr; i = i->next) {
+        if(i->line == 0) {
+            counter++;
+        } else {
+            logError("#:# was not freed", i->buffer, i->line);
+        }
+    }
+    if(counter > 0) {
+        logError("# unknown entries were not freed", counter);
+    }
+#endif
+}

+ 5 - 1
src/Vector.cpp

@@ -1,4 +1,8 @@
-#include "core/Vector.hpp"
+module;
+
+#include <math.h>
+
+module Core.Vector;
 
 void Core::setAngles(Vector3& v, float lengthAngle, float widthAngle) {
     float sWidth = 0.0f;

+ 1 - 1
src/View.cpp

@@ -1,4 +1,4 @@
-#include "core/View.hpp"
+module Core.View;
 
 using Core::View;
 

+ 7 - 7
test/Main.cpp

@@ -2,10 +2,10 @@
 #include <cstdio>
 #include <cstring>
 
-#include "Tests.hpp"
-#include "core/Logger.hpp"
-#include "core/Test.hpp"
-#include "core/Utility.hpp"
+import Tests;
+import Core.Logger;
+import Core.Test;
+import Core.Utility;
 
 static void finalize() {
     Core::finalizeTests();
@@ -14,7 +14,7 @@ static void finalize() {
 
 static void onExit(int code, void* data) {
     unsigned int i = *static_cast<unsigned int*>(data);
-    LOG_WARNING("Hello from exit #: #", code, i);
+    Core::logWarning("Hello from exit #: #", code, i);
     finalize();
 }
 
@@ -95,11 +95,11 @@ int main(int argAmount, const char** args) {
     testView();
 
     Core::logLevel = Core::LogLevel::WARNING;
-    LOG_DEBUG("You won't see this!");
+    Core::logDebug("You won't see this!");
     Core::logLevel = Core::LogLevel::DEBUG;
 
     unsigned int data = 123'456'789;
     Core::setExitHandler(onExit, &data);
 
-    EXIT(1);
+    Core::exitWithHandler(1);
 }

+ 40 - 0
test/Tests.cppm

@@ -0,0 +1,40 @@
+module;
+
+export module Tests;
+
+export [[noreturn]] void testInvalidAllocate();
+export [[noreturn]] void testInvalidReallocate();
+export [[noreturn]] void testInvalidNew();
+export [[noreturn]] void testPostCanary();
+export [[noreturn]] void testPreCanary();
+export [[noreturn]] void testPreCanaryNew();
+export [[noreturn]] void testPreCanaryNewArray();
+export void testArray();
+export void testArrayList(bool light);
+export void testBitArray();
+export void testBox();
+export void testBuffer(bool light);
+export void testClock(bool light);
+export void testColor();
+export void testComponents();
+export void testFile();
+export void testFrustum();
+export void testHashMap(bool light);
+export void testHashedString();
+export void testInteractiveTerminal();
+export void testList(bool light);
+export void testMath();
+export void testMatrix();
+export void testPlane();
+export void testQuaternion();
+export void testQueue();
+export void testRandom(bool light);
+export void testReadLine();
+export void testTerminal(bool tty);
+export void testTest();
+export void testThread();
+export void testUnicode();
+export void testUniquePointer();
+export void testUtility();
+export void testVector();
+export void testView();

+ 0 - 41
test/Tests.hpp

@@ -1,41 +0,0 @@
-#ifndef CORE_TESTS_HPP
-#define CORE_TESTS_HPP
-
-[[noreturn]] void testInvalidAllocate();
-[[noreturn]] void testInvalidReallocate();
-[[noreturn]] void testInvalidNew();
-[[noreturn]] void testPostCanary();
-[[noreturn]] void testPreCanary();
-[[noreturn]] void testPreCanaryNew();
-[[noreturn]] void testPreCanaryNewArray();
-void testArray();
-void testArrayList(bool light);
-void testBitArray();
-void testBox();
-void testBuffer(bool light);
-void testClock(bool light);
-void testColor();
-void testComponents();
-void testFile();
-void testFrustum();
-void testHashMap(bool light);
-void testHashedString();
-void testInteractiveTerminal();
-void testList(bool light);
-void testMath();
-void testMatrix();
-void testPlane();
-void testQuaternion();
-void testQueue();
-void testRandom(bool light);
-void testReadLine();
-void testTerminal(bool tty);
-void testTest();
-void testThread();
-void testUnicode();
-void testUniquePointer();
-void testUtility();
-void testVector();
-void testView();
-
-#endif

+ 80 - 77
test/modules/ArrayListTests.cpp

@@ -1,6 +1,9 @@
-#include "../Tests.hpp"
-#include "core/ArrayList.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.ArrayList;
+import Core.Test;
+import Core.Types;
+import Core.Meta;
 
 template class Core::ArrayList<size_t, 20>;
 using IntList = Core::ArrayList<size_t, 20>;
@@ -8,34 +11,34 @@ using IntList = Core::ArrayList<size_t, 20>;
 static void testAdd() {
     IntList list;
     list.add(5u);
-    TEST(5, list[0]);
-    TEST(5, list.getLast());
+    Core::test(5, list[0]);
+    Core::test(5, list.getLast());
     const IntList& cList = list;
-    TEST(5, cList.getLast());
-    TEST(1, list.getLength());
+    Core::test(5, cList.getLast());
+    Core::test(1, list.getLength());
 }
 
 static void testMultipleAdd() {
     IntList list;
     list.add(4u).add(3u).add(2u);
-    TEST(4, list[0]);
-    TEST(3, list[1]);
-    TEST(2, list[2]);
-    TEST(3, list.getLength());
+    Core::test(4, list[0]);
+    Core::test(3, list[1]);
+    Core::test(2, list[2]);
+    Core::test(3, list.getLength());
 }
 
 static void testAddReplace() {
     IntList list;
     list.add(5u);
     list[0] = 3;
-    TEST(3, list[0]);
+    Core::test(3, list[0]);
 }
 
 static void testClear() {
     IntList list;
     list.add(5u).add(4u);
     list.clear();
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testOverflow(bool light) {
@@ -48,7 +51,7 @@ static void testOverflow(bool light) {
         list.add(i);
     }
     for(size_t i = 0; i < list.getLength(); i++) {
-        TEST(i, list[i]);
+        Core::test(i, list[i]);
     }
 }
 
@@ -56,9 +59,9 @@ static void testCopy() {
     IntList list;
     list.add(1u).add(2u).add(3u);
     IntList copy(list);
-    TEST(list.getLength(), copy.getLength());
+    Core::test(list.getLength(), copy.getLength());
     for(size_t i = 0; i < copy.getLength() && i < list.getLength(); i++) {
-        TEST(list[i], copy[i]);
+        Core::test(list[i], copy[i]);
     }
 }
 
@@ -67,9 +70,9 @@ static void testCopyAssignment() {
     list.add(1u).add(2u).add(3u);
     IntList copy;
     copy = list;
-    TEST(list.getLength(), copy.getLength());
+    Core::test(list.getLength(), copy.getLength());
     for(size_t i = 0; i < copy.getLength() && i < list.getLength(); i++) {
-        TEST(list[i], copy[i]);
+        Core::test(list[i], copy[i]);
     }
 }
 
@@ -77,11 +80,11 @@ static void testMove() {
     IntList list;
     list.add(1u).add(2u).add(3u);
     IntList move(Core::move(list));
-    TEST(0, list.getLength());
-    TEST(3, move.getLength());
-    TEST(1, move[0]);
-    TEST(2, move[1]);
-    TEST(3, move[2]);
+    Core::test(0, list.getLength());
+    Core::test(3, move.getLength());
+    Core::test(1, move[0]);
+    Core::test(2, move[1]);
+    Core::test(3, move[2]);
 }
 
 static void testMoveAssignment() {
@@ -89,47 +92,47 @@ static void testMoveAssignment() {
     list.add(1u).add(2u).add(3u);
     IntList move;
     move = Core::move(list);
-    TEST(0, list.getLength());
-    TEST(3, move.getLength());
-    TEST(1, move[0]);
-    TEST(2, move[1]);
-    TEST(3, move[2]);
+    Core::test(0, list.getLength());
+    Core::test(3, move.getLength());
+    Core::test(1, move[0]);
+    Core::test(2, move[1]);
+    Core::test(3, move[2]);
 }
 
 static void testToString() {
     IntList list;
     list.add(1u).add(243u).add(423u);
-    TEST_STRING("[1, 243, 423]", list);
-    TEST_STRING("[1]", IntList().add(1u));
-    TEST_STRING("[]", IntList());
+    Core::testString("[1, 243, 423]", list);
+    Core::testString("[1]", IntList().add(1u));
+    Core::testString("[]", IntList());
 }
 
 static void testRemoveBySwap() {
     IntList list;
     list.add(4u).add(3u).add(2u);
     list.removeBySwap(0);
-    TEST(2, list[0]);
-    TEST(3, list[1]);
-    TEST(2, list.getLength());
+    Core::test(2, list[0]);
+    Core::test(3, list[1]);
+    Core::test(2, list.getLength());
     list.removeBySwap(1);
-    TEST(2, list[0]);
-    TEST(1, list.getLength());
+    Core::test(2, list[0]);
+    Core::test(1, list.getLength());
     list.removeBySwap(0);
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testRemove() {
     IntList list;
     list.add(4u).add(3u).add(2u);
     list.remove(0);
-    TEST(3, list[0]);
-    TEST(2, list[1]);
-    TEST(2, list.getLength());
+    Core::test(3, list[0]);
+    Core::test(2, list[1]);
+    Core::test(2, list.getLength());
     list.remove(1);
-    TEST(3, list[0]);
-    TEST(1, list.getLength());
+    Core::test(3, list[0]);
+    Core::test(1, list.getLength());
     list.removeLast();
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testForRange() {
@@ -139,7 +142,7 @@ static void testForRange() {
         i++;
     }
     for(size_t i = 0; i < list.getLength(); i++) {
-        TEST(i + 2, list[i]);
+        Core::test(i + 2, list[i]);
     }
 }
 
@@ -173,51 +176,51 @@ struct ArrayListTest final {
 static void testInsert() {
     Core::ArrayList<ArrayListTest, 5> l;
     l.addAt(0, 1);
-    TEST(1, l.getLength());
-    TEST(1, l[0].value);
-    TEST(1, arrayListInstances);
+    Core::test(1, l.getLength());
+    Core::test(1, l[0].value);
+    Core::test(1, arrayListInstances);
 
     l.addAt(0, 2);
-    TEST(2, l.getLength());
-    TEST(2, l[0].value);
-    TEST(1, l[1].value);
-    TEST(2, arrayListInstances);
+    Core::test(2, l.getLength());
+    Core::test(2, l[0].value);
+    Core::test(1, l[1].value);
+    Core::test(2, arrayListInstances);
 
     l.addAt(0, 3);
-    TEST(3, l.getLength());
-    TEST(3, l[0].value);
-    TEST(2, l[1].value);
-    TEST(1, l[2].value);
-    TEST(3, arrayListInstances);
+    Core::test(3, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(2, l[1].value);
+    Core::test(1, l[2].value);
+    Core::test(3, arrayListInstances);
 
     l.addAt(1, 4);
-    TEST(4, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(2, l[2].value);
-    TEST(1, l[3].value);
-    TEST(4, arrayListInstances);
+    Core::test(4, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(2, l[2].value);
+    Core::test(1, l[3].value);
+    Core::test(4, arrayListInstances);
 
     l.addAt(2, 5);
-    TEST(5, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(5, l[2].value);
-    TEST(2, l[3].value);
-    TEST(1, l[4].value);
-    TEST(5, arrayListInstances);
+    Core::test(5, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(5, l[2].value);
+    Core::test(2, l[3].value);
+    Core::test(1, l[4].value);
+    Core::test(5, arrayListInstances);
 
     l.addAt(5, 4);
-    TEST(5, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(5, l[2].value);
-    TEST(2, l[3].value);
-    TEST(1, l[4].value);
-    TEST(5, arrayListInstances);
+    Core::test(5, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(5, l[2].value);
+    Core::test(2, l[3].value);
+    Core::test(1, l[4].value);
+    Core::test(5, arrayListInstances);
 
     l.clear();
-    TEST(0, arrayListInstances);
+    Core::test(0, arrayListInstances);
 }
 
 void testArrayList(bool light) {

+ 10 - 8
test/modules/ArrayTests.cpp

@@ -1,6 +1,8 @@
-#include "../Tests.hpp"
-#include "core/Array.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Array;
+import Core.Test;
+import Core.Types;
 
 template struct Core::Array<size_t, 3>;
 using TestArray = Core::Array<size_t, 3>;
@@ -10,15 +12,15 @@ static void testToString1() {
     a[0] = 1;
     a[1] = 243;
     a[2] = 423;
-    TEST_STRING("[1, 243, 423]", a);
+    Core::testString("[1, 243, 423]", a);
     Core::Array<int, 3> b = {{1, 2, 3}};
-    TEST_STRING("[1, 2, 3]", b);
+    Core::testString("[1, 2, 3]", b);
 }
 
 static void testToString2() {
     Core::Array<int, 1> a;
     a[0] = 1;
-    TEST_STRING("[1]", a);
+    Core::testString("[1]", a);
 }
 
 static void testReadConst() {
@@ -28,7 +30,7 @@ static void testReadConst() {
     }
     const TestArray& c = a;
     for(size_t i = 0; i < c.getLength(); i++) {
-        TEST(i, c[i]);
+        Core::test(i, c[i]);
     }
 }
 
@@ -41,7 +43,7 @@ static void testRangeFor() {
         i++;
     }
     for(size_t i = 0; i < a.getLength(); i++) {
-        TEST(i + 1, a[i]);
+        Core::test(i + 1, a[i]);
     }
 }
 

+ 52 - 49
test/modules/BitArrayTests.cpp

@@ -1,26 +1,29 @@
-#include "../Tests.hpp"
-#include "core/BitArray.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.BitArray;
+import Core.Test;
+import Core.Meta;
+import Core.Types;
 
 static void testSetRead() {
     Core::BitArray bits;
     bits.resize(4, 3);
     bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
-    TEST(1, bits.get(0));
-    TEST(2, bits.get(1));
-    TEST(3, bits.get(2));
-    TEST(4, bits.get(3));
+    Core::test(1, bits.get(0));
+    Core::test(2, bits.get(1));
+    Core::test(3, bits.get(2));
+    Core::test(4, bits.get(3));
     Core::BitArray copy;
     copy = bits;
-    TEST(1, copy.get(0));
-    TEST(2, copy.get(1));
-    TEST(3, copy.get(2));
-    TEST(4, copy.get(3));
+    Core::test(1, copy.get(0));
+    Core::test(2, copy.get(1));
+    Core::test(3, copy.get(2));
+    Core::test(4, copy.get(3));
     Core::BitArray move = Core::move(copy);
-    TEST(1, move.get(0));
-    TEST(2, move.get(1));
-    TEST(3, move.get(2));
-    TEST(4, move.get(3));
+    Core::test(1, move.get(0));
+    Core::test(2, move.get(1));
+    Core::test(3, move.get(2));
+    Core::test(4, move.get(3));
 }
 
 static void testBigSetRead() {
@@ -30,7 +33,7 @@ static void testBigSetRead() {
         bits.set(i, i);
     }
     for(size_t i = 0; i < bits.getLength(); i++) {
-        TEST(i, bits.get(i));
+        Core::test(i, bits.get(i));
     }
 }
 
@@ -48,13 +51,13 @@ static void testRandomSetReadResize() {
         }
     }
     for(size_t i = 0; i < bits.getLength(); i++) {
-        TEST(data[i], bits.get(i));
+        Core::test(data[i], bits.get(i));
     }
     bits.resize(bits.getLength(), bits.getBits() + 1);
-    TEST(14, bits.getBits());
-    TEST(100, bits.getLength());
+    Core::test(14, bits.getBits());
+    Core::test(100, bits.getLength());
     for(size_t i = 0; i < bits.getLength(); i++) {
-        TEST(data[i], bits.get(i));
+        Core::test(data[i], bits.get(i));
     }
 }
 
@@ -63,10 +66,10 @@ static void testReadOnly() {
     bits.resize(4, 3);
     bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
     const Core::BitArray copy = bits;
-    TEST(1, copy.get(0));
-    TEST(2, copy.get(1));
-    TEST(3, copy.get(2));
-    TEST(4, copy.get(3));
+    Core::test(1, copy.get(0));
+    Core::test(2, copy.get(1));
+    Core::test(3, copy.get(2));
+    Core::test(4, copy.get(3));
 }
 
 static void testSelect() {
@@ -75,39 +78,39 @@ static void testSelect() {
     bits.fill(0);
     bits.set(0, 1).set(5, 1).set(20, 1).set(31, 1);
     bits.set(32, 1).set(33, 1).set(60, 1);
-    TEST(-1, bits.select(0));
-    TEST(0, bits.select(1));
-    TEST(5, bits.select(2));
-    TEST(20, bits.select(3));
-    TEST(31, bits.select(4));
-    TEST(32, bits.select(5));
-    TEST(33, bits.select(6));
-    TEST(60, bits.select(7));
-    TEST(-1, bits.select(8));
+    Core::test(-1, bits.select(0));
+    Core::test(0, bits.select(1));
+    Core::test(5, bits.select(2));
+    Core::test(20, bits.select(3));
+    Core::test(31, bits.select(4));
+    Core::test(32, bits.select(5));
+    Core::test(33, bits.select(6));
+    Core::test(60, bits.select(7));
+    Core::test(-1, bits.select(8));
 }
 
 static void testToString1() {
     Core::BitArray bits;
     bits.resize(4, 3);
     bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
-    TEST_STRING("[1, 2, 3, 4]", bits);
+    Core::testString("[1, 2, 3, 4]", bits);
 }
 
 static void testToString2() {
     Core::BitArray bits;
     bits.resize(1, 3);
     bits.set(0, 1);
-    TEST_STRING("[1]", bits);
+    Core::testString("[1]", bits);
 }
 
 static void testToString3() {
     Core::BitArray bits;
-    TEST_STRING("[]", bits);
+    Core::testString("[]", bits);
 }
 
 static void testResizeExact() {
     Core::BitArray bits;
-    TEST(0, bits.getInternalByteSize());
+    Core::test(0, bits.getInternalByteSize());
     // the size in bytes matches the internal storage type
     size_t elements = sizeof(u64);
     bits.resize(elements, 8);
@@ -115,28 +118,28 @@ static void testResizeExact() {
         bits.set(i, i);
     }
     for(size_t i = 0; i < elements; i++) {
-        TEST(i, bits.get(i));
+        Core::test(i, bits.get(i));
     }
-    TEST(sizeof(u64), bits.getInternalByteSize());
+    Core::test(sizeof(u64), bits.getInternalByteSize());
 }
 
 static void testInvalidArgument() {
     Core::BitArray bits;
     bits.resize(0, 5);
-    TEST(0, bits.getLength());
-    TEST(0, bits.getBits());
+    Core::test(0, bits.getLength());
+    Core::test(0, bits.getBits());
     bits.resize(5, 0);
-    TEST(0, bits.getLength());
-    TEST(0, bits.getBits());
+    Core::test(0, bits.getLength());
+    Core::test(0, bits.getBits());
     bits.resize(0, 0);
-    TEST(0, bits.getLength());
-    TEST(0, bits.getBits());
+    Core::test(0, bits.getLength());
+    Core::test(0, bits.getBits());
     bits.resize(1, 65);
-    TEST(1, bits.getLength());
-    TEST(64, bits.getBits());
+    Core::test(1, bits.getLength());
+    Core::test(64, bits.getBits());
     bits.resize(5, 68);
-    TEST(5, bits.getLength());
-    TEST(64, bits.getBits());
+    Core::test(5, bits.getLength());
+    Core::test(64, bits.getBits());
 }
 
 void testBitArray() {

+ 23 - 21
test/modules/BoxTests.cpp

@@ -1,25 +1,27 @@
-#include "../Tests.hpp"
-#include "core/Box.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Box;
+import Core.Test;
+import Core.Vector;
 
 using Core::Box;
 using V3 = Core::Vector3;
 
 static void testInit() {
     Box box(V3(1.0f, 2.0f, 3.0f));
-    TEST_STRING("Box([0.00, 0.00, 0.00], [1.00, 2.00, 3.00])", box);
-    TEST_STRING("[0.00, 0.00, 0.00]", box.getMin());
-    TEST_STRING("[1.00, 2.00, 3.00]", box.getMax());
+    Core::testString("Box([0.00, 0.00, 0.00], [1.00, 2.00, 3.00])", box);
+    Core::testString("[0.00, 0.00, 0.00]", box.getMin());
+    Core::testString("[1.00, 2.00, 3.00]", box.getMax());
     box = Box(V3(-1.0f, -2.0f, -3.0f));
-    TEST_STRING("Box([-1.00, -2.00, -3.00], [0.00, 0.00, 0.00])", box);
-    TEST_STRING("[-1.00, -2.00, -3.00]", box.getMin());
-    TEST_STRING("[0.00, 0.00, 0.00]", box.getMax());
+    Core::testString("Box([-1.00, -2.00, -3.00], [0.00, 0.00, 0.00])", box);
+    Core::testString("[-1.00, -2.00, -3.00]", box.getMin());
+    Core::testString("[0.00, 0.00, 0.00]", box.getMax());
 }
 
 static void testOffset() {
     Box box(V3(1.0f, 2.0f, 3.0f));
     box = box.offset(V3(7.0f, -4.0f, 6.0f));
-    TEST_STRING("Box([7.00, -4.00, 6.00], [8.00, -2.00, 9.00])", box);
+    Core::testString("Box([7.00, -4.00, 6.00], [8.00, -2.00, 9.00])", box);
 }
 
 static void testCollidesWith() {
@@ -27,32 +29,32 @@ static void testCollidesWith() {
     Box boxB(V3(-1.0f, -2.0f, -3.0f));
     Box boxC(V3(2.0f, 2.0f, 2.0f));
     boxC = boxC.offset(V3(-1.0f, -1.0f, -1.0f));
-    TEST_TRUE(boxC.collidesWith(boxA));
-    TEST_TRUE(boxC.collidesWith(boxB));
-    TEST_TRUE(boxA.collidesWith(boxC));
-    TEST_TRUE(boxB.collidesWith(boxC));
-    TEST_FALSE(boxA.collidesWith(boxB));
-    TEST_FALSE(boxB.collidesWith(boxA));
+    Core::testTrue(boxC.collidesWith(boxA));
+    Core::testTrue(boxC.collidesWith(boxB));
+    Core::testTrue(boxA.collidesWith(boxC));
+    Core::testTrue(boxB.collidesWith(boxC));
+    Core::testFalse(boxA.collidesWith(boxB));
+    Core::testFalse(boxB.collidesWith(boxA));
 }
 
 static void testExpand() {
     Box box(V3(1.0f, 2.0f, 3.0f));
     box = box.expand(V3(7.0f, -4.0f, 6.0f));
-    TEST_STRING("Box([0.00, -4.00, 0.00], [8.00, 2.00, 9.00])", box);
+    Core::testString("Box([0.00, -4.00, 0.00], [8.00, 2.00, 9.00])", box);
     box = Box(V3(1.0f, 2.0f, 3.0f));
     box = box.expand(V3(-7.0f, 4.0f, -6.0f));
-    TEST_STRING("Box([-7.00, 0.00, -6.00], [1.00, 6.00, 3.00])", box);
+    Core::testString("Box([-7.00, 0.00, -6.00], [1.00, 6.00, 3.00])", box);
 }
 
 static void testGrow() {
     Box box(V3(1.0f, 2.0f, 3.0f));
-    TEST_STRING(
+    Core::testString(
         "Box([-2.00, -1.00, -3.00], [3.00, 3.00, 6.00])",
         box.grow(V3(4.0f, 2.0f, 6.0f)));
-    TEST_STRING(
+    Core::testString(
         "Box([0.50, 1.00, 1.50], [0.50, 1.00, 1.50])",
         box.grow(V3(-4.0f, -2.0f, -6.0f)));
-    TEST_STRING(
+    Core::testString(
         "Box([0.05, 1.00, 0.50], [0.95, 1.00, 2.50])",
         box.grow(V3(-0.1f, -4.0f, -1.0f)));
 }

+ 22 - 15
test/modules/BufferTests.cpp

@@ -1,6 +1,13 @@
-#include "../Tests.hpp"
-#include "core/Buffer.hpp"
-#include "core/Test.hpp"
+module;
+
+#include <cstring>
+
+module Tests;
+
+import Core.Buffer;
+import Core.Test;
+import Core.Types;
+import Core.Meta;
 
 static constexpr size_t SIZE_TYPES =
     sizeof(int) + sizeof(long) + sizeof(float) + sizeof(double);
@@ -14,11 +21,11 @@ static void testAdd(bool light) {
         buffer.add(5.0f);
         buffer.add(5.0);
     }
-    TEST(SIZE_TYPES * limit, buffer.getLength());
+    Core::test(SIZE_TYPES * limit, buffer.getLength());
     buffer.clear();
-    TEST(0, buffer.getLength());
+    Core::test(0, buffer.getLength());
     buffer.add(20l);
-    TEST(sizeof(long), buffer.getLength());
+    Core::test(sizeof(long), buffer.getLength());
 }
 
 static void testCopy() {
@@ -32,12 +39,12 @@ static void testCopy() {
     Core::Buffer buffer2 = buffer;
     Core::Buffer buffer3;
     buffer3 = buffer;
-    if(TEST(SIZE_TYPES * 10, buffer.getLength()) &&
-       TEST(SIZE_TYPES * 10, buffer2.getLength()) &&
-       TEST(SIZE_TYPES * 10, buffer3.getLength())) {
-        TEST_TRUE(
+    if(Core::test(SIZE_TYPES * 10, buffer.getLength()) &&
+       Core::test(SIZE_TYPES * 10, buffer2.getLength()) &&
+       Core::test(SIZE_TYPES * 10, buffer3.getLength())) {
+        Core::testTrue(
             memcmp(buffer.getData(), buffer2.getData(), SIZE_TYPES * 10) == 0);
-        TEST_TRUE(
+        Core::testTrue(
             memcmp(buffer.getData(), buffer3.getData(), SIZE_TYPES * 10) == 0);
     }
 }
@@ -51,8 +58,8 @@ static void testMoveConstruct() {
         buffer.add(5.0);
     }
     Core::Buffer buffer2(Core::move(buffer));
-    TEST(0, buffer.getLength());
-    TEST(SIZE_TYPES * 10, buffer2.getLength());
+    Core::test(0, buffer.getLength());
+    Core::test(SIZE_TYPES * 10, buffer2.getLength());
 }
 
 static void testMove() {
@@ -65,8 +72,8 @@ static void testMove() {
     }
     Core::Buffer buffer2;
     buffer2 = Core::move(buffer);
-    TEST(0, buffer.getLength());
-    TEST(SIZE_TYPES * 10, buffer2.getLength());
+    Core::test(0, buffer.getLength());
+    Core::test(SIZE_TYPES * 10, buffer2.getLength());
 }
 
 void testBuffer(bool light) {

+ 23 - 18
test/modules/ClockTests.cpp

@@ -1,52 +1,57 @@
+module;
+
 #include "../../src/ErrorSimulator.hpp"
-#include "../Tests.hpp"
-#include "core/Clock.hpp"
-#include "core/Test.hpp"
+
+module Tests;
+
+import Core.Clock;
+import Core.Test;
+import Core.Types;
 
 using Core::Clock;
 
 static void testUpdate() {
     Clock c;
-    TEST_TRUE(c.update() == 0);
-    TEST_TRUE(c.update() >= 0);
-    TEST_TRUE(c.update() >= 0);
-    TEST_TRUE(c.update() >= 0);
+    Core::testTrue(c.update() == 0);
+    Core::testTrue(c.update() >= 0);
+    Core::testTrue(c.update() >= 0);
+    Core::testTrue(c.update() >= 0);
 }
 
 static void testUpdatesPerSecond() {
     Clock c;
     for(int i = 0; i < 1000; i++) {
-        TEST_TRUE(c.update() >= 0);
+        Core::testTrue(c.update() >= 0);
     }
-    TEST_TRUE(c.getUpdatesPerSecond() > 0.0f);
+    Core::testTrue(c.getUpdatesPerSecond() > 0.0f);
 }
 
 static void testSleep(i64 nanos) {
     Clock c;
-    TEST_TRUE(c.update() >= 0);
-    TEST_FALSE(Clock::sleepNanos(nanos));
+    Core::testTrue(c.update() >= 0);
+    Core::testFalse(Clock::sleepNanos(nanos));
     i64 n = c.update();
-    TEST_TRUE(n >= nanos && n <= nanos * 13 / 10);
+    Core::testTrue(n >= nanos && n <= nanos * 13 / 10);
 }
 
 static void testSleepMillis(i64 millis) {
     Clock c;
-    TEST_TRUE(c.update() >= 0);
-    TEST_FALSE(Clock::sleepMillis(millis));
+    Core::testTrue(c.update() >= 0);
+    Core::testFalse(Clock::sleepMillis(millis));
     i64 n = c.update();
     i64 nanos = millis * 1'000'000;
-    TEST_TRUE(n >= nanos && n <= nanos * 13 / 10);
+    Core::testTrue(n >= nanos && n <= nanos * 13 / 10);
 }
 
 static void testFail() {
     Clock c;
 #ifdef ERROR_SIMULATOR
     failStepThrow = 1;
-    TEST(-1l, c.update());
+    Core::test(-1l, c.update());
     failStepThrow = 1;
-    TEST(-1l, Clock::getNanos());
+    Core::test(-1l, Clock::getNanos());
     failStepThrow = 1;
-    TEST_TRUE(Clock::sleepMillis(5));
+    Core::testTrue(Clock::sleepMillis(5));
     failStepThrow = 0;
 #endif
 }

+ 28 - 27
test/modules/ColorTests.cpp

@@ -1,6 +1,7 @@
-#include "../Tests.hpp"
-#include "core/Color.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Color;
+import Core.Test;
 
 template class Core::Color<1>;
 template class Core::Color<2>;
@@ -11,47 +12,47 @@ const float eps = 0.0001f;
 
 static void testColor1() {
     Core::Color1 c(36);
-    TEST(36, c[0]);
-    TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
+    Core::test(36, c[0]);
+    Core::testFloat(36.0f / 255.0f, c.asFloat(0), eps);
 }
 
 static void testColor2() {
     Core::Color2 c(36 + 256, 100.0);
-    TEST(36, c[0]);
-    TEST(100, c[1]);
-    TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
-    TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
+    Core::test(36, c[0]);
+    Core::test(100, c[1]);
+    Core::testFloat(36.0f / 255.0f, c.asFloat(0), eps);
+    Core::testFloat(100.0f / 255.0f, c.asFloat(1), eps);
 }
 
 static void testColor3() {
     Core::Color3 c(36, 100.0f, 200);
-    TEST(36, c[0]);
-    TEST(100, c[1]);
-    TEST(200, c[2]);
-    TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
-    TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
-    TEST_FLOAT(200.0f / 255.0f, c.asFloat(2), eps);
+    Core::test(36, c[0]);
+    Core::test(100, c[1]);
+    Core::test(200, c[2]);
+    Core::testFloat(36.0f / 255.0f, c.asFloat(0), eps);
+    Core::testFloat(100.0f / 255.0f, c.asFloat(1), eps);
+    Core::testFloat(200.0f / 255.0f, c.asFloat(2), eps);
 }
 
 template<typename T>
 static void testColor4() {
     T c(36, 100, 200, 142);
-    TEST(36, c[0]);
-    TEST(100, c[1]);
-    TEST(200, c[2]);
-    TEST(142, c[3]);
-    TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
-    TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
-    TEST_FLOAT(200.0f / 255.0f, c.asFloat(2), eps);
-    TEST_FLOAT(142.0f / 255.0f, c.asFloat(3), eps);
+    Core::test(36, c[0]);
+    Core::test(100, c[1]);
+    Core::test(200, c[2]);
+    Core::test(142, c[3]);
+    Core::testFloat(36.0f / 255.0f, c.asFloat(0), eps);
+    Core::testFloat(100.0f / 255.0f, c.asFloat(1), eps);
+    Core::testFloat(200.0f / 255.0f, c.asFloat(2), eps);
+    Core::testFloat(142.0f / 255.0f, c.asFloat(3), eps);
 }
 
 static void testColor4Empty() {
     Core::Color4 c;
-    TEST(0, c[0]);
-    TEST(0, c[1]);
-    TEST(0, c[2]);
-    TEST(0, c[3]);
+    Core::test(0, c[0]);
+    Core::test(0, c[1]);
+    Core::test(0, c[2]);
+    Core::test(0, c[3]);
 }
 
 void testColor() {

+ 66 - 63
test/modules/ComponentsTests.cpp

@@ -1,6 +1,7 @@
-#include "../Tests.hpp"
-#include "core/Components.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Components;
+import Core.Test;
 
 template class Core::Components<int>;
 using IntComponent = Core::Components<int>;
@@ -10,25 +11,25 @@ static void testEntityIterator(T c) {
     auto iter = c.entities();
     auto pos = iter.begin();
     auto end = iter.end();
-    if(TEST_TRUE(pos != end)) {
-        TEST(1, (*pos).entity);
-        TEST(10, (*pos).component);
-        TEST_TRUE(pos != end);
+    if(Core::testTrue(pos != end)) {
+        Core::test(1, (*pos).entity);
+        Core::test(10, (*pos).component);
+        Core::testTrue(pos != end);
         ++pos;
     }
-    if(TEST_TRUE(pos != end)) {
-        TEST(5, (*pos).entity);
-        TEST(20, (*pos).component);
-        TEST_TRUE(pos != end);
+    if(Core::testTrue(pos != end)) {
+        Core::test(5, (*pos).entity);
+        Core::test(20, (*pos).component);
+        Core::testTrue(pos != end);
         ++pos;
     }
-    if(TEST_TRUE(pos != end)) {
-        TEST(10, (*pos).entity);
-        TEST(30, (*pos).component);
-        TEST_TRUE(pos != end);
+    if(Core::testTrue(pos != end)) {
+        Core::test(10, (*pos).entity);
+        Core::test(30, (*pos).component);
+        Core::testTrue(pos != end);
         ++pos;
     }
-    TEST_FALSE(pos != end);
+    Core::testFalse(pos != end);
 }
 
 static void testAddForEach() {
@@ -36,14 +37,15 @@ static void testAddForEach() {
     int* i1 = nullptr;
     int* i2 = nullptr;
     int* i3 = nullptr;
-    TEST_TRUE(c.put(i1, 1, 10));
-    TEST_FALSE(c.put(i1, 1, 20));
-    TEST_TRUE(c.put(i2, 5, 20));
-    TEST_TRUE(c.put(i3, 10, 30));
-    if(TEST_NOT_NULL(i1) && TEST_NOT_NULL(i2) && TEST_NOT_NULL(i3)) {
-        TEST(10, *i1);
-        TEST(20, *i2);
-        TEST(30, *i3);
+    Core::testTrue(c.put(i1, 1, 10));
+    Core::testFalse(c.put(i1, 1, 20));
+    Core::testTrue(c.put(i2, 5, 20));
+    Core::testTrue(c.put(i3, 10, 30));
+    if(Core::testNotNull(i1) && Core::testNotNull(i2) &&
+       Core::testNotNull(i3)) {
+        Core::test(10, *i1);
+        Core::test(20, *i2);
+        Core::test(30, *i3);
     }
     testEntityIterator<IntComponent&>(c);
     testEntityIterator<const IntComponent&>(c);
@@ -53,16 +55,16 @@ template<typename T>
 static void testComponentIterator(T c) {
     auto iter = c.begin();
     auto end = c.end();
-    if(TEST_TRUE(iter != end)) {
-        TEST(10, *(iter++));
+    if(Core::testTrue(iter != end)) {
+        Core::test(10, *(iter++));
     }
-    if(TEST_TRUE(iter != end)) {
-        TEST(20, *(iter++));
+    if(Core::testTrue(iter != end)) {
+        Core::test(20, *(iter++));
     }
-    if(TEST_TRUE(iter != end)) {
-        TEST(30, *(iter++));
+    if(Core::testTrue(iter != end)) {
+        Core::test(30, *(iter++));
     }
-    TEST_FALSE(iter != c.end());
+    Core::testFalse(iter != c.end());
 }
 
 static void testAddComponentForEach() {
@@ -70,13 +72,14 @@ static void testAddComponentForEach() {
     int* i1 = nullptr;
     int* i2 = nullptr;
     int* i3 = nullptr;
-    TEST_TRUE(c.put(i1, 1, 10));
-    TEST_TRUE(c.put(i2, 5, 20));
-    TEST_TRUE(c.put(i3, 10, 30));
-    if(TEST_NOT_NULL(i1) && TEST_NOT_NULL(i2) && TEST_NOT_NULL(i3)) {
-        TEST(10, *i1);
-        TEST(20, *i2);
-        TEST(30, *i3);
+    Core::testTrue(c.put(i1, 1, 10));
+    Core::testTrue(c.put(i2, 5, 20));
+    Core::testTrue(c.put(i3, 10, 30));
+    if(Core::testNotNull(i1) && Core::testNotNull(i2) &&
+       Core::testNotNull(i3)) {
+        Core::test(10, *i1);
+        Core::test(20, *i2);
+        Core::test(30, *i3);
     }
     testComponentIterator<IntComponent&>(c);
     testComponentIterator<const IntComponent&>(c);
@@ -84,44 +87,44 @@ static void testAddComponentForEach() {
 
 static void testRemove() {
     IntComponent c;
-    TEST_TRUE(c.add(1, 10));
-    TEST_TRUE(c.add(5, 20));
-    TEST_TRUE(c.add(10, 30));
-    TEST_FALSE(c.remove(20));
-    TEST_TRUE(c.remove(5));
-    TEST_FALSE(c.remove(30));
-    TEST_TRUE(c.add(20, 40));
-    TEST_TRUE(c.remove(20));
+    Core::testTrue(c.add(1, 10));
+    Core::testTrue(c.add(5, 20));
+    Core::testTrue(c.add(10, 30));
+    Core::testFalse(c.remove(20));
+    Core::testTrue(c.remove(5));
+    Core::testFalse(c.remove(30));
+    Core::testTrue(c.add(20, 40));
+    Core::testTrue(c.remove(20));
     int* i1 = c.search(1);
-    TEST_NULL(c.search(5));
+    Core::testNull(c.search(5));
     int* i3 = c.search(10);
-    if(TEST_NOT_NULL(i1) && TEST_NOT_NULL(i3)) {
-        TEST(10, *i1);
-        TEST(30, *i3);
+    if(Core::testNotNull(i1) && Core::testNotNull(i3)) {
+        Core::test(10, *i1);
+        Core::test(30, *i3);
     }
-    TEST_TRUE(c.remove(10));
+    Core::testTrue(c.remove(10));
     i1 = c.search(1);
-    TEST_NULL(c.search(5));
-    TEST_NULL(c.search(10));
-    if(TEST_NOT_NULL(i1)) {
-        TEST(10, *i1);
+    Core::testNull(c.search(5));
+    Core::testNull(c.search(10));
+    if(Core::testNotNull(i1)) {
+        Core::test(10, *i1);
     }
-    TEST_TRUE(c.remove(1));
-    TEST_NULL(c.search(1));
-    TEST_NULL(c.search(5));
-    TEST_NULL(c.search(10));
+    Core::testTrue(c.remove(1));
+    Core::testNull(c.search(1));
+    Core::testNull(c.search(5));
+    Core::testNull(c.search(10));
 }
 
 static void testConstSearch() {
     IntComponent c;
     int* i = nullptr;
-    TEST_TRUE(c.put(i, 1, 10));
+    Core::testTrue(c.put(i, 1, 10));
     const IntComponent& cc = c;
     const int* component = cc.search(1);
-    if(TEST_TRUE(component != nullptr)) {
-        TEST(10, *component);
+    if(Core::testTrue(component != nullptr)) {
+        Core::test(10, *component);
     }
-    TEST_NULL(cc.search(2));
+    Core::testNull(cc.search(2));
 }
 
 void testComponents() {

+ 20 - 14
test/modules/FileTests.cpp

@@ -1,10 +1,16 @@
+module;
+
 #include <cstring>
+#include <source_location>
+
+#include "../../src/ErrorSimulator.hpp"
+
+module Tests;
 
-#include "../Tests.hpp"
-#include "../src/ErrorSimulator.hpp"
-#include "core/File.hpp"
-#include "core/Logger.hpp"
-#include "core/Test.hpp"
+import Core.File;
+import Core.Logger;
+import Core.Test;
+import Core.List;
 
 static int failIndex = 0;
 static const char* fails[] = {
@@ -16,25 +22,25 @@ static const char* fails[] = {
     "cannot close file 'testData/someFile'"};
 
 static void printReport(
-    Core::LogLevel l, const char*, int, void*, const char* message) {
-    TEST(static_cast<int>(Core::LogLevel::ERROR), static_cast<int>(l));
-    if(!TEST_TRUE(strstr(message, fails[failIndex]) != nullptr)) {
-        LOG_ERROR("'#' does not contain '#'", message, fails[failIndex]);
+    Core::LogLevel l, const std::source_location&, void*, const char* message) {
+    Core::test(static_cast<int>(Core::LogLevel::ERROR), static_cast<int>(l));
+    if(!Core::testTrue(strstr(message, fails[failIndex]) != nullptr)) {
+        Core::logError("'#' does not contain '#'", message, fails[failIndex]);
     }
 }
 
 static void testExistingFile() {
     Core::List<char> f;
-    if(!TEST_FALSE(readFile(f, "testData/someFile"))) {
+    if(!Core::testFalse(readFile(f, "testData/someFile"))) {
         return;
     }
-    TEST(21lu, f.getLength());
-    TEST_STRING("Just\nSome\nTest File\n", &f[0]);
+    Core::test(21lu, f.getLength());
+    Core::testString("Just\nSome\nTest File\n", &f[0]);
 }
 
 static void testNotExistingFile() {
     Core::List<char> f;
-    TEST_TRUE(readFile(f, "gdfgdfg"));
+    Core::testTrue(readFile(f, "gdfgdfg"));
 }
 
 static void testFails(int steps) {
@@ -43,7 +49,7 @@ static void testFails(int steps) {
     failStep = steps;
     failIndex = steps;
     Core::List<char> f;
-    TEST_TRUE(readFile(f, "testData/someFile"));
+    Core::testTrue(readFile(f, "testData/someFile"));
     failStep = -1;
 #endif
 }

+ 29 - 26
test/modules/FrustumTests.cpp

@@ -1,6 +1,9 @@
-#include "../Tests.hpp"
-#include "core/Frustum.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Frustum;
+import Core.Test;
+import Core.Vector;
+import Core.Math;
 
 #define R60 Core::degreeToRadian(60.0f)
 
@@ -11,13 +14,13 @@ static void testPointIsInside() {
     Core::Frustum f(R60, 0.1f, 1000.0f);
     f.updatePlanes(V3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0), V3(0, 0, 1), size);
 
-    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f)));
-    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f)));
-    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f)));
-    TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f)));
-    TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f)));
-    TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f)));
-    TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f)));
+    Core::testTrue(f.isInside(V3(0.0f, 0.0f, 5.0f)));
+    Core::testFalse(f.isInside(V3(0.0f, 0.0f, 1004.0f)));
+    Core::testFalse(f.isInside(V3(0.0f, 0.0f, -5.0f)));
+    Core::testFalse(f.isInside(V3(0.0f, 50.0f, 5.0f)));
+    Core::testFalse(f.isInside(V3(0.0f, -50.0f, 5.0f)));
+    Core::testFalse(f.isInside(V3(50.0f, 0.0f, 5.0f)));
+    Core::testFalse(f.isInside(V3(-50.0f, 0.0f, 5.0f)));
 }
 
 static void testSphereIsInside() {
@@ -25,26 +28,26 @@ static void testSphereIsInside() {
     Core::Frustum f(R60, 0.1f, 1000.0f);
     f.updatePlanes(V3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0), V3(0, 0, 1), size);
 
-    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f), 3.0f));
-    TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 3.0f));
-
-    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
-    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 50.0f));
-    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, -5.0f), 50.0f));
-    TEST_TRUE(f.isInside(V3(0.0f, 50.0f, 5.0f), 50.0f));
-    TEST_TRUE(f.isInside(V3(0.0f, -50.0f, 5.0f), 50.0f));
-    TEST_TRUE(f.isInside(V3(50.0f, 0.0f, 5.0f), 50.0f));
-    TEST_TRUE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(0.0f, 0.0f, 1004.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(0.0f, 0.0f, -5.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(0.0f, 50.0f, 5.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(0.0f, -50.0f, 5.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(50.0f, 0.0f, 5.0f), 3.0f));
+    Core::testFalse(f.isInside(V3(-50.0f, 0.0f, 5.0f), 3.0f));
+
+    Core::testTrue(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
+    Core::testTrue(f.isInside(V3(0.0f, 0.0f, 1004.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(0.0f, 0.0f, -5.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(0.0f, 50.0f, 5.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(0.0f, -50.0f, 5.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(50.0f, 0.0f, 5.0f), 50.0f));
+    Core::testTrue(f.isInside(V3(-50.0f, 0.0f, 5.0f), 50.0f));
 }
 
 static void testUpdateProjection() {
     Core::Frustum f(R60, 0.1f, 1000.0f);
-    TEST_STRING(
+    Core::testString(
         "[[1.30, 0.00, 0.00, 0.00], "
         "[0.00, 1.73, 0.00, 0.00], "
         "[0.00, 0.00, -1.00, -0.20], "

+ 74 - 68
test/modules/HashMapTests.cpp

@@ -1,7 +1,12 @@
-#include "../Tests.hpp"
-#include "core/HashMap.hpp"
-#include "core/Random.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.HashMap;
+import Core.Random;
+import Core.Test;
+import Core.Types;
+import Core.ToString;
+import Core.Meta;
+import Core.Array;
 
 template struct Core::HashMap<int, int>;
 using IntMap = Core::HashMap<int, int>;
@@ -17,12 +22,12 @@ static void checkIntMap(IntMap& map) {
     int* b = map.search(2);
     int* c = map.search(3);
     int* d = map.search(0);
-    if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c) &&
-       TEST_NOT_NULL(d)) {
-        TEST(3, *a);
-        TEST(4, *b);
-        TEST(5, *c);
-        TEST(20, *d);
+    if(Core::testNotNull(a) && Core::testNotNull(b) && Core::testNotNull(c) &&
+       Core::testNotNull(d)) {
+        Core::test(3, *a);
+        Core::test(4, *b);
+        Core::test(5, *c);
+        Core::test(20, *d);
     }
 }
 
@@ -30,34 +35,34 @@ static void testAdd() {
     IntMap map;
     map.add(5, 4);
     int* value = map.search(5);
-    if(TEST_NOT_NULL(value)) {
-        TEST(4, *value);
+    if(Core::testNotNull(value)) {
+        Core::test(4, *value);
     }
 }
 
 static void testMultipleAdd() {
     IntMap map = getTestIntMap();
-    TEST_TRUE(map.contains(0));
-    TEST_TRUE(map.contains(1));
-    TEST_TRUE(map.contains(2));
-    TEST_TRUE(map.contains(3));
+    Core::testTrue(map.contains(0));
+    Core::testTrue(map.contains(1));
+    Core::testTrue(map.contains(2));
+    Core::testTrue(map.contains(3));
     checkIntMap(map);
 }
 
 static void testSearch() {
     IntMap map;
-    TEST_NULL(map.search(6));
+    Core::testNull(map.search(6));
     map.add(5, 4).add(10, 3).add(15, 2);
-    TEST_NULL(map.search(6));
+    Core::testNull(map.search(6));
 }
 
 static void testAddReplace() {
     IntMap map;
     map.add(5, 4).add(5, 10);
-    TEST_TRUE(map.contains(5));
+    Core::testTrue(map.contains(5));
     int* a = map.search(5);
-    if(TEST_NOT_NULL(a)) {
-        TEST(10, *a);
+    if(Core::testNotNull(a)) {
+        Core::test(10, *a);
     }
 }
 
@@ -66,8 +71,8 @@ static void testClear() {
     map.clear();
     map.add(5, 4).add(4, 10);
     map.clear();
-    TEST_FALSE(map.contains(5));
-    TEST_FALSE(map.contains(4));
+    Core::testFalse(map.contains(5));
+    Core::testFalse(map.contains(4));
 }
 
 static void testOverflow(bool light) {
@@ -77,7 +82,7 @@ static void testOverflow(bool light) {
         map.add(i, i);
     }
     for(int i = 0; i < limit; i++) {
-        TEST_TRUE(map.contains(i));
+        Core::testTrue(map.contains(i));
     }
 }
 
@@ -129,29 +134,30 @@ static void testEmplaceProbing() {
         Core::HashMap<int, ProbingTest> map;
 
         ProbingTest* ar = nullptr;
-        TEST_TRUE(map.tryEmplace(ar, 0, 3, 4));
-        TEST_TRUE(map.tryEmplace(ar, 3, 4, 5));
-        TEST_TRUE(map.tryEmplace(ar, 20, 5, 6));
-        TEST_FALSE(map.tryEmplace(ar, 3, 6, 7));
-        TEST_FALSE(map.tryEmplace(ar, 20, 7, 8));
+        Core::testTrue(map.tryEmplace(ar, 0, 3, 4));
+        Core::testTrue(map.tryEmplace(ar, 3, 4, 5));
+        Core::testTrue(map.tryEmplace(ar, 20, 5, 6));
+        Core::testFalse(map.tryEmplace(ar, 3, 6, 7));
+        Core::testFalse(map.tryEmplace(ar, 20, 7, 8));
 
         ProbingTest* a = map.search(0);
         ProbingTest* b = map.search(3);
         ProbingTest* c = map.search(20);
 
-        if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c)) {
-            TEST(ProbingTest(3, 4), *a);
-            TEST(ProbingTest(4, 5), *b);
-            TEST(ProbingTest(5, 6), *c);
+        if(Core::testNotNull(a) && Core::testNotNull(b) &&
+           Core::testNotNull(c)) {
+            Core::test(ProbingTest(3, 4), *a);
+            Core::test(ProbingTest(4, 5), *b);
+            Core::test(ProbingTest(5, 6), *c);
         }
     }
-    TEST(0, aInstances);
+    Core::test(0, aInstances);
 }
 
 static void testToString() {
-    TEST_STRING("[0 = 20, 2 = 4, 1 = 3, 3 = 5]", getTestIntMap());
-    TEST_STRING("[1 = 3]", IntMap().add(1, 3));
-    TEST_STRING("[]", IntMap());
+    Core::testString("[0 = 20, 2 = 4, 1 = 3, 3 = 5]", getTestIntMap());
+    Core::testString("[1 = 3]", IntMap().add(1, 3));
+    Core::testString("[]", IntMap());
 }
 
 static void testCopy() {
@@ -188,14 +194,14 @@ static void testEntryForEach() {
     for(auto entry : map) {
         counter += entry.getKey() + entry.value;
     }
-    TEST(40, counter);
+    Core::test(40, counter);
 
     const IntMap& cmap = map;
     counter = 0;
     for(auto entry : cmap) {
         counter += entry.getKey() + entry.value;
     }
-    TEST(40, counter);
+    Core::test(40, counter);
 }
 
 static void testKeyForEach() {
@@ -206,14 +212,14 @@ static void testKeyForEach() {
     for(const int& key : map.getKeys()) {
         counter += key;
     }
-    TEST(30, counter);
+    Core::test(30, counter);
 
     const IntMap& cmap = map;
     counter = 0;
     for(const int& key : cmap.getKeys()) {
         counter += key;
     }
-    TEST(30, counter);
+    Core::test(30, counter);
 }
 
 static void testValueForEach() {
@@ -224,14 +230,14 @@ static void testValueForEach() {
     for(int& value : map.getValues()) {
         counter += value;
     }
-    TEST(9, counter);
+    Core::test(9, counter);
 
     const IntMap& cmap = map;
     counter = 0;
     for(const int& value : cmap.getValues()) {
         counter += value;
     }
-    TEST(9, counter);
+    Core::test(9, counter);
 }
 
 template<typename T>
@@ -257,35 +263,35 @@ static void testTypes() {
 static void testInvalid() {
     IntMap map;
     int* v = nullptr;
-    TEST_TRUE(map.tryEmplace(v, 0, 2));
-    if(TEST_NOT_NULL(v)) {
-        TEST(2, *v);
+    Core::testTrue(map.tryEmplace(v, 0, 2));
+    if(Core::testNotNull(v)) {
+        Core::test(2, *v);
     }
-    TEST_FALSE(map.tryEmplace(v, 0, 6));
-    if(TEST_NOT_NULL(v)) {
-        TEST(2, *v);
+    Core::testFalse(map.tryEmplace(v, 0, 6));
+    if(Core::testNotNull(v)) {
+        Core::test(2, *v);
     }
-    TEST(3, map.put(0, 3));
+    Core::test(3, map.put(0, 3));
     v = map.search(0);
-    if(TEST_NOT_NULL(v)) {
-        TEST(3, *v);
+    if(Core::testNotNull(v)) {
+        Core::test(3, *v);
     }
     map.clear();
-    TEST_NULL(map.search(0));
+    Core::testNull(map.search(0));
 }
 
 static void testInvalidPut() {
     IntMap map;
-    TEST_STRING("[]", map);
-    TEST(3, map.put(0, 3));
-    TEST_STRING("[0 = 3]", map);
+    Core::testString("[]", map);
+    Core::test(3, map.put(0, 3));
+    Core::testString("[0 = 3]", map);
     int* v = map.search(0);
-    if(TEST_NOT_NULL(v)) {
-        TEST(3, *v);
+    if(Core::testNotNull(v)) {
+        Core::test(3, *v);
     }
     map.clear();
-    TEST_FALSE(map.contains(0));
-    TEST_STRING("[]", map);
+    Core::testFalse(map.contains(0));
+    Core::testString("[]", map);
 }
 
 static void testAddCollisions() {
@@ -299,17 +305,17 @@ static void testRemove() {
     IntMap map;
     map.add(1, 3).add(2, 4).add(3, 5);
 
-    TEST_TRUE(map.remove(2));
-    TEST_FALSE(map.remove(7));
+    Core::testTrue(map.remove(2));
+    Core::testFalse(map.remove(7));
 
     int* a = map.search(1);
     int* b = map.search(2);
     int* c = map.search(3);
 
-    TEST_NULL(b);
-    if(TEST_NOT_NULL(a) && TEST_NOT_NULL(c)) {
-        TEST(3, *a);
-        TEST(5, *c);
+    Core::testNull(b);
+    if(Core::testNotNull(a) && Core::testNotNull(c)) {
+        Core::test(3, *a);
+        Core::test(5, *c);
     }
 }
 
@@ -333,11 +339,11 @@ static void testRemoveLong() {
 
         Core::Array<i32, LIMIT> copy = a;
         for(int key : map.getKeys()) {
-            TEST_TRUE(copy[static_cast<size_t>(key)]);
+            Core::testTrue(copy[static_cast<size_t>(key)]);
             copy[static_cast<size_t>(key)] = 0;
         }
         for(int key : copy) {
-            TEST(0, key);
+            Core::test(0, key);
         }
     }
 }

+ 31 - 30
test/modules/HashedStringTests.cpp

@@ -1,47 +1,48 @@
-#include "../Tests.hpp"
-#include "core/HashMap.hpp"
-#include "core/HashedString.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.HashMap;
+import Core.HashedString;
+import Core.Test;
 
 template class Core::HashedString<32>;
 using HString = Core::HashedString<32>;
 
 static void testComparison() {
     HString a("test");
-    TEST_STRING("test", static_cast<const char*>(a));
+    Core::testString("test", static_cast<const char*>(a));
     HString b("testA");
     HString c("");
     HString d("Btest");
 
-    TEST_TRUE(a == a);
-    TEST_TRUE(b == b);
-    TEST_TRUE(c == c);
-    TEST_TRUE(d == d);
-    TEST_TRUE(a != b);
-    TEST_TRUE(a != c);
-    TEST_TRUE(a != d);
-    TEST_TRUE(b != a);
-    TEST_TRUE(b != c);
-    TEST_TRUE(b != d);
-    TEST_TRUE(c != a);
-    TEST_TRUE(c != b);
-    TEST_TRUE(c != d);
-    TEST_TRUE(d != a);
-    TEST_TRUE(d != b);
-    TEST_TRUE(d != c);
+    Core::testTrue(a == a);
+    Core::testTrue(b == b);
+    Core::testTrue(c == c);
+    Core::testTrue(d == d);
+    Core::testTrue(a != b);
+    Core::testTrue(a != c);
+    Core::testTrue(a != d);
+    Core::testTrue(b != a);
+    Core::testTrue(b != c);
+    Core::testTrue(b != d);
+    Core::testTrue(c != a);
+    Core::testTrue(c != b);
+    Core::testTrue(c != d);
+    Core::testTrue(d != a);
+    Core::testTrue(d != b);
+    Core::testTrue(d != c);
 }
 
 static void testLength() {
     HString s("test");
-    TEST(4, s.getLength());
-    TEST(31, s.getCapacity());
+    Core::test(4, s.getLength());
+    Core::test(31, s.getCapacity());
 }
 
 static void testHashCode() {
     HString a;
     HString b("wusi");
-    TEST(a.hashCode(), 0lu);
-    TEST_TRUE(b.hashCode() != 0u);
+    Core::test(a.hashCode(), 0lu);
+    Core::testTrue(b.hashCode() != 0u);
 }
 
 static void testAsHashMapKey() {
@@ -50,12 +51,12 @@ static void testAsHashMapKey() {
     int* a = map.search("wusi");
     int* b = map.search("hiThere");
     int* c = map.search("baum123");
-    TEST_NULL(map.search("423hifd"));
+    Core::testNull(map.search("423hifd"));
 
-    if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c)) {
-        TEST(3, *a);
-        TEST(7, *b);
-        TEST(5, *c);
+    if(Core::testNotNull(a) && Core::testNotNull(b) && Core::testNotNull(c)) {
+        Core::test(3, *a);
+        Core::test(7, *b);
+        Core::test(5, *c);
     }
 }
 

+ 97 - 93
test/modules/ListTests.cpp

@@ -1,6 +1,10 @@
-#include "../Tests.hpp"
-#include "core/List.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.List;
+import Core.Test;
+import Core.Types;
+import Core.Math;
+import Core.Meta;
 
 template class Core::List<size_t>;
 using IntList = Core::List<size_t>;
@@ -9,46 +13,46 @@ static void testAdd() {
     IntList list;
     list.add(5u);
     const IntList& cList = list;
-    TEST(5, list[0]);
-    TEST(5, list.getLast());
-    TEST(5, cList[0]);
-    TEST(5, cList.getLast());
-    TEST(1, list.getLength());
+    Core::test(5, list[0]);
+    Core::test(5, list.getLast());
+    Core::test(5, cList[0]);
+    Core::test(5, cList.getLast());
+    Core::test(1, list.getLength());
 }
 
 static void testMultipleAdd() {
     IntList list;
     list.add(4u).add(3u).add(2u);
-    TEST(4, list[0]);
-    TEST(3, list[1]);
-    TEST(2, list[2]);
-    TEST(3, list.getLength());
+    Core::test(4, list[0]);
+    Core::test(3, list[1]);
+    Core::test(2, list[2]);
+    Core::test(3, list.getLength());
 }
 
 static void testAddReplace() {
     IntList list;
     list.add(5u);
     list[0] = 3;
-    TEST(3, list[0]);
+    Core::test(3, list[0]);
 }
 
 static void testClear() {
     IntList list;
     list.add(5u).add(4u);
     list.clear();
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testShrink() {
     IntList list;
     list.add(5u).add(4u).add(3u);
-    TEST_TRUE(list.getCapacity() >= 3);
+    Core::testTrue(list.getCapacity() >= 3);
     list.shrink();
-    TEST_TRUE(list.getLength() == 3);
-    TEST_TRUE(list.getCapacity() == 3);
-    TEST(5, list[0]);
-    TEST(4, list[1]);
-    TEST(3, list[2]);
+    Core::testTrue(list.getLength() == 3);
+    Core::testTrue(list.getCapacity() == 3);
+    Core::test(5, list[0]);
+    Core::test(4, list[1]);
+    Core::test(3, list[2]);
 }
 
 static void testBigAdd(bool light) {
@@ -58,9 +62,9 @@ static void testBigAdd(bool light) {
         list.add(i);
     }
     for(size_t i = 0; i < list.getLength(); i++) {
-        TEST(i, list[i]);
+        Core::test(i, list[i]);
     }
-    TEST(limit, list.getLength());
+    Core::test(limit, list.getLength());
 }
 
 static void testCopy() {
@@ -68,10 +72,10 @@ static void testCopy() {
     list.add(1u).add(2u).add(3u);
 
     IntList copy = list;
-    TEST(list.getLength(), copy.getLength());
+    Core::test(list.getLength(), copy.getLength());
     size_t limit = Core::min(copy.getLength(), list.getLength());
     for(size_t i = 0; i < limit; i++) {
-        TEST(list[i], copy[i]);
+        Core::test(list[i], copy[i]);
     }
 }
 
@@ -80,11 +84,11 @@ static void testMove() {
     list.add(1u).add(2u).add(3u);
 
     IntList move(Core::move(list));
-    TEST(0, list.getLength());
-    TEST(3, move.getLength());
-    TEST(1, move[0]);
-    TEST(2, move[1]);
-    TEST(3, move[2]);
+    Core::test(0, list.getLength());
+    Core::test(3, move.getLength());
+    Core::test(1, move[0]);
+    Core::test(2, move[1]);
+    Core::test(3, move[2]);
 }
 
 static void testMoveAssignment() {
@@ -93,87 +97,87 @@ static void testMoveAssignment() {
 
     IntList move;
     move = Core::move(list);
-    TEST(0, list.getLength());
-    TEST(3, move.getLength());
-    TEST(1, move[0]);
-    TEST(2, move[1]);
-    TEST(3, move[2]);
+    Core::test(0, list.getLength());
+    Core::test(3, move.getLength());
+    Core::test(1, move[0]);
+    Core::test(2, move[1]);
+    Core::test(3, move[2]);
 }
 
 static void testToString1() {
     IntList list;
     list.add(1u).add(243u).add(423u);
-    TEST_STRING("[1, 243, 423]", list);
+    Core::testString("[1, 243, 423]", list);
 }
 
 static void testToString2() {
     IntList list;
     list.add(1u);
-    TEST_STRING("[1]", list);
+    Core::testString("[1]", list);
 }
 
 static void testToString3() {
     IntList list;
-    TEST_STRING("[]", list);
+    Core::testString("[]", list);
 }
 
 static void testRemoveBySwap() {
     IntList list;
     list.add(4u).add(3u).add(2u);
     list.removeBySwap(0);
-    TEST(2, list[0]);
-    TEST(3, list[1]);
-    TEST(2, list.getLength());
+    Core::test(2, list[0]);
+    Core::test(3, list[1]);
+    Core::test(2, list.getLength());
     list.removeBySwap(1);
-    TEST(2, list[0]);
-    TEST(1, list.getLength());
+    Core::test(2, list[0]);
+    Core::test(1, list.getLength());
     list.removeBySwap(0);
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testRemove() {
     IntList list;
     list.add(4u).add(3u).add(2u);
     list.remove(0);
-    TEST(3, list[0]);
-    TEST(2, list[1]);
-    TEST(2, list.getLength());
+    Core::test(3, list[0]);
+    Core::test(2, list[1]);
+    Core::test(2, list.getLength());
     list.remove(1);
-    TEST(3, list[0]);
-    TEST(1, list.getLength());
+    Core::test(3, list[0]);
+    Core::test(1, list.getLength());
     list.remove(0);
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testRemoveLast() {
     IntList list;
     list.add(4u).add(3u).add(2u);
     list.removeLast();
-    TEST(4, list[0]);
-    TEST(3, list[1]);
-    TEST(2, list.getLength());
+    Core::test(4, list[0]);
+    Core::test(3, list[1]);
+    Core::test(2, list.getLength());
     list.removeLast();
-    TEST(4, list[0]);
-    TEST(1, list.getLength());
+    Core::test(4, list[0]);
+    Core::test(1, list.getLength());
     list.removeLast();
-    TEST(0, list.getLength());
+    Core::test(0, list.getLength());
 }
 
 static void testResize() {
     IntList list;
     list.resize(5, 10);
-    TEST(5, list.getLength());
+    Core::test(5, list.getLength());
     for(size_t i = 0; i < 5; i++) {
-        TEST(10, list[i]);
+        Core::test(10, list[i]);
     }
 }
 
 static void testDefaultResize() {
     IntList list;
     list.resize(5);
-    TEST(5, list.getLength());
+    Core::test(5, list.getLength());
     for(size_t i = 0; i < 5; i++) {
-        TEST(0, list[i]);
+        Core::test(0, list[i]);
     }
 }
 
@@ -230,52 +234,52 @@ struct ListTest final {
 static void testInsert() {
     Core::List<ListTest> l;
     l.addAt(0, 1);
-    TEST(1, l.getLength());
-    TEST(1, l[0].value);
-    TEST(1, listInstances);
+    Core::test(1, l.getLength());
+    Core::test(1, l[0].value);
+    Core::test(1, listInstances);
 
     l.addAt(0, 2);
-    TEST(2, l.getLength());
-    TEST(2, l[0].value);
-    TEST(1, l[1].value);
-    TEST(2, listInstances);
+    Core::test(2, l.getLength());
+    Core::test(2, l[0].value);
+    Core::test(1, l[1].value);
+    Core::test(2, listInstances);
 
     l.addAt(0, 3);
-    TEST(3, l.getLength());
-    TEST(3, l[0].value);
-    TEST(2, l[1].value);
-    TEST(1, l[2].value);
-    TEST(3, listInstances);
+    Core::test(3, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(2, l[1].value);
+    Core::test(1, l[2].value);
+    Core::test(3, listInstances);
 
     l.addAt(1, 4);
-    TEST(4, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(2, l[2].value);
-    TEST(1, l[3].value);
-    TEST(4, listInstances);
+    Core::test(4, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(2, l[2].value);
+    Core::test(1, l[3].value);
+    Core::test(4, listInstances);
 
     l.addAt(2, 5);
-    TEST(5, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(5, l[2].value);
-    TEST(2, l[3].value);
-    TEST(1, l[4].value);
-    TEST(5, listInstances);
+    Core::test(5, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(5, l[2].value);
+    Core::test(2, l[3].value);
+    Core::test(1, l[4].value);
+    Core::test(5, listInstances);
 
     l.addAt(5, 4);
-    TEST(6, l.getLength());
-    TEST(3, l[0].value);
-    TEST(4, l[1].value);
-    TEST(5, l[2].value);
-    TEST(2, l[3].value);
-    TEST(1, l[4].value);
-    TEST(4, l[5].value);
-    TEST(6, listInstances);
+    Core::test(6, l.getLength());
+    Core::test(3, l[0].value);
+    Core::test(4, l[1].value);
+    Core::test(5, l[2].value);
+    Core::test(2, l[3].value);
+    Core::test(1, l[4].value);
+    Core::test(4, l[5].value);
+    Core::test(6, listInstances);
 
     l.clear();
-    TEST(0, listInstances);
+    Core::test(0, listInstances);
 }
 
 void testList(bool light) {

+ 54 - 53
test/modules/MathTests.cpp

@@ -1,83 +1,84 @@
-#include "../Tests.hpp"
-#include "core/Math.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Math;
+import Core.Test;
 
 constexpr float eps = 0.0001f;
 
 static void testInterpolate() {
-    TEST_FLOAT(7.5f, Core::interpolate(5.0f, 10.0f, 0.5f), eps);
-    TEST_FLOAT(-2.0, Core::interpolate(-10.0f, 10.0f, 0.4f), eps);
-    TEST_FLOAT(10.0f, Core::interpolate(-3.0f, 10.0f, 1.0f), eps);
-    TEST_FLOAT(7.0f, Core::interpolate(7.0f, 10.0f, 0.0f), eps);
-    TEST_FLOAT(6.0f, Core::interpolate(0.0f, 10.0f, 0.6f), eps);
+    Core::testFloat(7.5f, Core::interpolate(5.0f, 10.0f, 0.5f), eps);
+    Core::testFloat(-2.0, Core::interpolate(-10.0f, 10.0f, 0.4f), eps);
+    Core::testFloat(10.0f, Core::interpolate(-3.0f, 10.0f, 1.0f), eps);
+    Core::testFloat(7.0f, Core::interpolate(7.0f, 10.0f, 0.0f), eps);
+    Core::testFloat(6.0f, Core::interpolate(0.0f, 10.0f, 0.6f), eps);
 }
 
 static void testIsPowerOf2() {
-    TEST_TRUE(Core::isPowerOf2(1));
-    TEST_TRUE(Core::isPowerOf2(2));
-    TEST_FALSE(Core::isPowerOf2(3));
-    TEST_TRUE(Core::isPowerOf2(4));
-    TEST_FALSE(Core::isPowerOf2(5));
-    TEST_FALSE(Core::isPowerOf2(6));
-    TEST_FALSE(Core::isPowerOf2(7));
-    TEST_TRUE(Core::isPowerOf2(8));
-    TEST_FALSE(Core::isPowerOf2(9));
-    TEST_FALSE(Core::isPowerOf2(10));
+    Core::testTrue(Core::isPowerOf2(1));
+    Core::testTrue(Core::isPowerOf2(2));
+    Core::testFalse(Core::isPowerOf2(3));
+    Core::testTrue(Core::isPowerOf2(4));
+    Core::testFalse(Core::isPowerOf2(5));
+    Core::testFalse(Core::isPowerOf2(6));
+    Core::testFalse(Core::isPowerOf2(7));
+    Core::testTrue(Core::isPowerOf2(8));
+    Core::testFalse(Core::isPowerOf2(9));
+    Core::testFalse(Core::isPowerOf2(10));
     for(int i = 16; i < 30'000; i *= 2) {
-        TEST_TRUE(Core::isPowerOf2(i));
-        TEST_FALSE(Core::isPowerOf2(i + 1));
-        TEST_FALSE(Core::isPowerOf2(i + 2));
-        TEST_FALSE(Core::isPowerOf2(i + 3));
+        Core::testTrue(Core::isPowerOf2(i));
+        Core::testFalse(Core::isPowerOf2(i + 1));
+        Core::testFalse(Core::isPowerOf2(i + 2));
+        Core::testFalse(Core::isPowerOf2(i + 3));
     }
 }
 
 static void testRoundUpLog2() {
-    TEST(0, Core::roundUpLog2(-5));
-    TEST(0, Core::roundUpLog2(0));
-    TEST(1, Core::roundUpLog2(1));
-    TEST(1, Core::roundUpLog2(2));
-    TEST(2, Core::roundUpLog2(3));
-    TEST(2, Core::roundUpLog2(4));
-    TEST(3, Core::roundUpLog2(5));
-    TEST(4, Core::roundUpLog2(10));
-    TEST(5, Core::roundUpLog2(20));
-    TEST(16, Core::roundUpLog2(35'345));
-    TEST(31, Core::roundUpLog2(0x7FFF'FFFF));
+    Core::test(0, Core::roundUpLog2(-5));
+    Core::test(0, Core::roundUpLog2(0));
+    Core::test(1, Core::roundUpLog2(1));
+    Core::test(1, Core::roundUpLog2(2));
+    Core::test(2, Core::roundUpLog2(3));
+    Core::test(2, Core::roundUpLog2(4));
+    Core::test(3, Core::roundUpLog2(5));
+    Core::test(4, Core::roundUpLog2(10));
+    Core::test(5, Core::roundUpLog2(20));
+    Core::test(16, Core::roundUpLog2(35'345));
+    Core::test(31, Core::roundUpLog2(0x7FFF'FFFF));
 }
 
 static void testMin() {
-    TEST(-5, Core::min(-5));
-    TEST(-10, Core::min(-5, 4, 3, 2, -10));
-    TEST(4, Core::min(5, 20, 4, 30));
+    Core::test(-5, Core::min(-5));
+    Core::test(-10, Core::min(-5, 4, 3, 2, -10));
+    Core::test(4, Core::min(5, 20, 4, 30));
 }
 
 static void testMax() {
-    TEST(-5, Core::max(-5));
-    TEST(4, Core::max(-5, 4, 3, 2, -10));
-    TEST(30, Core::max(5, 20, 4, 30));
+    Core::test(-5, Core::max(-5));
+    Core::test(4, Core::max(-5, 4, 3, 2, -10));
+    Core::test(30, Core::max(5, 20, 4, 30));
 }
 
 static void testClamp() {
-    TEST(5, Core::clamp(1, 5, 10));
-    TEST(7, Core::clamp(7, 5, 10));
-    TEST(10, Core::clamp(20, 5, 10));
-    TEST(5, Core::clamp(1, 10, 5));
-    TEST(7, Core::clamp(7, 10, 5));
-    TEST(10, Core::clamp(20, 10, 5));
+    Core::test(5, Core::clamp(1, 5, 10));
+    Core::test(7, Core::clamp(7, 5, 10));
+    Core::test(10, Core::clamp(20, 5, 10));
+    Core::test(5, Core::clamp(1, 10, 5));
+    Core::test(7, Core::clamp(7, 10, 5));
+    Core::test(10, Core::clamp(20, 10, 5));
 }
 
 static void testRadianToDegree() {
-    TEST_FLOAT(45.0f, Core::radianToDegree(Core::PI * 0.25f), eps);
-    TEST_FLOAT(90.0f, Core::radianToDegree(Core::PI * 0.5f), eps);
-    TEST_FLOAT(180.0f, Core::radianToDegree(Core::PI), eps);
-    TEST_FLOAT(360.0f, Core::radianToDegree(Core::PI * 2.0f), eps);
+    Core::testFloat(45.0f, Core::radianToDegree(Core::PI * 0.25f), eps);
+    Core::testFloat(90.0f, Core::radianToDegree(Core::PI * 0.5f), eps);
+    Core::testFloat(180.0f, Core::radianToDegree(Core::PI), eps);
+    Core::testFloat(360.0f, Core::radianToDegree(Core::PI * 2.0f), eps);
 }
 
 static void testDegreeToRadian() {
-    TEST_FLOAT(Core::PI * 0.25f, Core::degreeToRadian(45.0f), eps);
-    TEST_FLOAT(Core::PI * 0.5f, Core::degreeToRadian(90.0f), eps);
-    TEST_FLOAT(Core::PI, Core::degreeToRadian(180.0f), eps);
-    TEST_FLOAT(Core::PI * 2.0f, Core::degreeToRadian(360.0f), eps);
+    Core::testFloat(Core::PI * 0.25f, Core::degreeToRadian(45.0f), eps);
+    Core::testFloat(Core::PI * 0.5f, Core::degreeToRadian(90.0f), eps);
+    Core::testFloat(Core::PI, Core::degreeToRadian(180.0f), eps);
+    Core::testFloat(Core::PI * 2.0f, Core::degreeToRadian(360.0f), eps);
 }
 
 void testMath() {

+ 35 - 29
test/modules/MatrixTests.cpp

@@ -1,6 +1,12 @@
-#include "../Tests.hpp"
-#include "core/Matrix.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Matrix;
+import Core.Test;
+import Core.Vector;
+import Core.Math;
+import Core.Quaternion;
+import Core.ToString;
+import Core.Types;
 
 using Core::Matrix;
 using V3 = Core::Vector3;
@@ -11,7 +17,7 @@ static void testInit() {
     for(int i = 0; i < 16; i++) {
         int x = i % 4;
         int y = i / 4;
-        TEST_FLOAT(x == y, data[i], 0.0f);
+        Core::testFloat(x == y, data[i], 0.0f);
     }
 }
 
@@ -28,55 +34,55 @@ static void testTranspose() {
     const float* tp = t.getValues();
     for(int x = 0; x < 4; x++) {
         for(int y = 0; y < 4; y++) {
-            TEST_FLOAT(mp[y * 4 + x], tp[x * 4 + y], 0.0f);
+            Core::testFloat(mp[y * 4 + x], tp[x * 4 + y], 0.0f);
         }
     }
     const float* mp2 = m2.getValues();
     for(int i = 0; i < 16; i++) {
-        TEST_FLOAT(mp[i], mp2[i], 0.0f);
+        Core::testFloat(mp[i], mp2[i], 0.0f);
     }
 }
 
 static void testScale() {
     Core::Matrix m;
     m.scale(V3(2.0f, 3.0f, 4.0f));
-    TEST(V3(-8.0f, 18.0f, 28.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(-8.0f, 18.0f, 28.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testUniformScale() {
     Core::Matrix m;
     m.scale(2.0f);
-    TEST(V3(-8.0f, 12.0f, 14.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(-8.0f, 12.0f, 14.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testTranslateX() {
     Core::Matrix m;
     m.translateX(5.0f);
-    TEST(V3(1.0f, 6.0f, 7.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(1.0f, 6.0f, 7.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testTranslateY() {
     Core::Matrix m;
     m.translateY(6.0f);
-    TEST(V3(-4.0f, 12.0f, 7.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(-4.0f, 12.0f, 7.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testTranslateZ() {
     Core::Matrix m;
     m.translateZ(7.0f);
-    TEST(V3(-4.0f, 6.0f, 14.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(-4.0f, 6.0f, 14.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testTranslate() {
     Core::Matrix m;
     m.translate(V3(1.0f, 2.0f, 3.0f));
-    TEST(V3(-3.0f, 8.0f, 10.0f), m * V3(-4.0f, 6.0f, 7.0f));
+    Core::test(V3(-3.0f, 8.0f, 10.0f), m * V3(-4.0f, 6.0f, 7.0f));
 }
 
 static void testTranslateTo() {
     Matrix m;
     m.translateTo(V3(6.0f, 8.0f, 9.0f));
-    TEST_STRING(
+    Core::testString(
         "[[1.00, 0.00, 0.00, 6.00], [0.00, 1.00, 0.00, 8.00], "
         "[0.00, 0.00, 1.00, 9.00], [0.00, 0.00, 0.00, 1.00]]",
         m);
@@ -91,7 +97,7 @@ static void testCombination() {
     m.translate(V3(-4.0f, 2.0f, 3.0f));
     m.scale(V3(2.0f, 3.0f, 4.0f));
     m.scale(0.5f);
-    TEST(V3(-1.0f, 9.0f, 16.0f), m * V3(1.0f, 1.0f, 1.0f));
+    Core::test(V3(-1.0f, 9.0f, 16.0f), m * V3(1.0f, 1.0f, 1.0f));
 }
 
 static void testMatrixCombination() {
@@ -107,31 +113,31 @@ static void testMatrixCombination() {
     c.translate(V3(-1.0f, -2.0f, -3.0f));
 
     c *= b * a;
-    TEST(V3(9.0f, 11.0f, 13.0f), c * V3(1.0f, 1.0f, 1.0f));
+    Core::test(V3(9.0f, 11.0f, 13.0f), c * V3(1.0f, 1.0f, 1.0f));
 }
 
 static void testRotateX() {
     Matrix m;
     m.rotateX(Core::degreeToRadian(90.0f));
-    TEST(V3(1.0f, 0.0f, 0.0f), m * V3(1.0f, 0.0f, 0.0f));
-    TEST(V3(0.0f, 0.0f, 1.0f), m * V3(0.0f, 1.0f, 0.0f));
-    TEST(V3(0.0f, -1.0f, 0.0f), m * V3(0.0f, 0.0f, 1.0f));
+    Core::test(V3(1.0f, 0.0f, 0.0f), m * V3(1.0f, 0.0f, 0.0f));
+    Core::test(V3(0.0f, 0.0f, 1.0f), m * V3(0.0f, 1.0f, 0.0f));
+    Core::test(V3(0.0f, -1.0f, 0.0f), m * V3(0.0f, 0.0f, 1.0f));
 }
 
 static void testRotateY() {
     Matrix m;
     m.rotateY(Core::degreeToRadian(90.0f));
-    TEST(V3(0.0f, 0.0f, -1.0f), m * V3(1.0f, 0.0f, 0.0f));
-    TEST(V3(0.0f, 1.0f, 0.0f), m * V3(0.0f, 1.0f, 0.0f));
-    TEST(V3(1.0f, 0.0f, 0.0f), m * V3(0.0f, 0.0f, 1.0f));
+    Core::test(V3(0.0f, 0.0f, -1.0f), m * V3(1.0f, 0.0f, 0.0f));
+    Core::test(V3(0.0f, 1.0f, 0.0f), m * V3(0.0f, 1.0f, 0.0f));
+    Core::test(V3(1.0f, 0.0f, 0.0f), m * V3(0.0f, 0.0f, 1.0f));
 }
 
 static void testRotateZ() {
     Matrix m;
     m.rotateZ(Core::degreeToRadian(90.0f));
-    TEST(V3(0.0f, 1.0f, 0.0f), m * V3(1.0f, 0.0f, 0.0f));
-    TEST(V3(-1.0f, 0.0f, 0.0f), m * V3(0.0f, 1.0f, 0.0f));
-    TEST(V3(0.0f, 0.0f, 1.0f), m * V3(0.0f, 0.0f, 1.0f));
+    Core::test(V3(0.0f, 1.0f, 0.0f), m * V3(1.0f, 0.0f, 0.0f));
+    Core::test(V3(-1.0f, 0.0f, 0.0f), m * V3(0.0f, 1.0f, 0.0f));
+    Core::test(V3(0.0f, 0.0f, 1.0f), m * V3(0.0f, 0.0f, 1.0f));
 }
 
 static void testQuaternionMatrix() {
@@ -154,7 +160,7 @@ static void testQuaternionMatrix() {
     check.translate(V3(1.0f, 2.0f, 3.0f));
 
     for(int i = 0; i < 16; i++) {
-        TEST_FLOAT(check.getValues()[i], m.getValues()[i], 0.0001f);
+        Core::testFloat(check.getValues()[i], m.getValues()[i], 0.0001f);
     }
 }
 
@@ -166,14 +172,14 @@ static void testToString() {
     m.set(3, Core::Vector4(13.0f, 14.0f, 15.0f, 16.0f));
     char buffer[1024];
     size_t n = toString(m, buffer, sizeof(buffer));
-    TEST(111, n);
-    TEST_STRING(
+    Core::test(111, n);
+    Core::testString(
         "[[1.00, 2.00, 3.00, 4.00], [5.00, 6.00, 7.00, 8.00], "
         "[9.00, 10.00, 11.00, 12.00], [13.00, 14.00, 15.00, 16.00]]",
         buffer);
     n = toString(m, buffer, 20);
-    TEST(111, n);
-    TEST_STRING("[[1.00, 2.00, 3.00,", buffer);
+    Core::test(111, n);
+    Core::testString("[[1.00, 2.00, 3.00,", buffer);
 }
 
 void testMatrix() {

+ 12 - 10
test/modules/PlaneTests.cpp

@@ -1,14 +1,16 @@
-#include "../Tests.hpp"
-#include "core/Plane.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Plane;
+import Core.Test;
+import Core.Vector;
 
 static const float eps = 0.0001f;
 using V3 = Core::Vector3;
 
 static void testToString() {
     Core::Plane p(V3(3, 6, 8), V3(7, 6, 2), V3(4, 4, 4));
-    TEST_STRING("(-0.684 x + 0.570 y + -0.456 z + 2.279)", p);
-    TEST_STRING("(0.000 x + 0.000 y + 0.000 z + 0.000)", Core::Plane());
+    Core::testString("(-0.684 x + 0.570 y + -0.456 z + 2.279)", p);
+    Core::testString("(0.000 x + 0.000 y + 0.000 z + 0.000)", Core::Plane());
 }
 
 static void testSignedDistance() {
@@ -16,11 +18,11 @@ static void testSignedDistance() {
     V3 b(7, 6, 2);
     V3 c(4, 4, 4);
     Core::Plane p(a, b, c);
-    TEST_FLOAT(0.0f, p.signedDistance(a), eps);
-    TEST_FLOAT(0.0f, p.signedDistance(b), eps);
-    TEST_FLOAT(0.0f, p.signedDistance(c), eps);
-    TEST_FLOAT(-1.13960576f, p.signedDistance(V3(5, 8, 10)), eps);
-    TEST_FLOAT(0.911684612f, p.signedDistance(V3(3, 2, 1)), eps);
+    Core::testFloat(0.0f, p.signedDistance(a), eps);
+    Core::testFloat(0.0f, p.signedDistance(b), eps);
+    Core::testFloat(0.0f, p.signedDistance(c), eps);
+    Core::testFloat(-1.13960576f, p.signedDistance(V3(5, 8, 10)), eps);
+    Core::testFloat(0.911684612f, p.signedDistance(V3(3, 2, 1)), eps);
 }
 
 void testPlane() {

+ 21 - 18
test/modules/QuaternionTests.cpp

@@ -1,25 +1,28 @@
-#include "../Tests.hpp"
-#include "core/Quaternion.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Quaternion;
+import Core.Test;
+import Core.Vector;
+import Core.Math;
 
 using Q = Core::Quaternion;
 using V3 = Core::Vector3;
 
 static void testInit() {
     Q q;
-    TEST_STRING("(0.000 i + 0.000 j + 0.000 k + 1.000)", q);
+    Core::testString("(0.000 i + 0.000 j + 0.000 k + 1.000)", q);
 }
 
 static void testAxisAndDegreesInit() {
     Q q(V3(1.0f, 2.0f, 3.0f), Core::degreeToRadian(142.0f));
-    TEST_STRING("(0.253 i + 0.505 j + 0.758 k + 0.326)", q);
+    Core::testString("(0.253 i + 0.505 j + 0.758 k + 0.326)", q);
 }
 
 static void testLerp() {
     Q q1(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(130.0f));
     Q q2(V3(1.0f, 2.0f, 4.0f), Core::degreeToRadian(260.0f));
     Q q3 = q1.lerp(0.3f, q2);
-    TEST_STRING("(0.223 i + 0.529 j + 0.810 k + 0.119)", q3);
+    Core::testString("(0.223 i + 0.529 j + 0.810 k + 0.119)", q3);
 }
 
 static void testMulSet() {
@@ -27,9 +30,9 @@ static void testMulSet() {
     Q q2(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(60.0f));
     Q q3;
     q3 *= q1;
-    TEST_STRING(q1, q3);
+    Core::testString(q1, q3);
     q3 *= q2;
-    TEST_STRING(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
+    Core::testString(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
 }
 
 static void testMul() {
@@ -37,7 +40,7 @@ static void testMul() {
     Q q2(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(60.0f));
     Q q3;
     q3 = q1 * q2;
-    TEST_STRING(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
+    Core::testString(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
 }
 
 static void testMulVector() {
@@ -49,15 +52,15 @@ static void testMulVector() {
     V3 v2(0.0f, 1.0f, 0.0f);
     V3 v3(0.0f, 0.0f, 1.0f);
 
-    TEST(V3(1.0f, 0.0f, 0.0f), q1 * v1);
-    TEST(V3(0.0f, 0.0f, 1.0f), q1 * v2);
-    TEST(V3(0.0f, -1.0f, 0.0f), q1 * v3);
-    TEST(V3(0.0f, 0.0f, -1.0f), q2 * v1);
-    TEST(V3(0.0f, 1.0f, 0.0f), q2 * v2);
-    TEST(V3(1.0f, 0.0f, 0.0f), q2 * v3);
-    TEST(V3(0.0f, 1.0f, 0.0f), q3 * v1);
-    TEST(V3(-1.0f, 0.0f, 0.0f), q3 * v2);
-    TEST(V3(0.0f, 0.0f, 1.0f), q3 * v3);
+    Core::test(V3(1.0f, 0.0f, 0.0f), q1 * v1);
+    Core::test(V3(0.0f, 0.0f, 1.0f), q1 * v2);
+    Core::test(V3(0.0f, -1.0f, 0.0f), q1 * v3);
+    Core::test(V3(0.0f, 0.0f, -1.0f), q2 * v1);
+    Core::test(V3(0.0f, 1.0f, 0.0f), q2 * v2);
+    Core::test(V3(1.0f, 0.0f, 0.0f), q2 * v3);
+    Core::test(V3(0.0f, 1.0f, 0.0f), q3 * v1);
+    Core::test(V3(-1.0f, 0.0f, 0.0f), q3 * v2);
+    Core::test(V3(0.0f, 0.0f, 1.0f), q3 * v3);
 }
 
 void testQuaternion() {

+ 94 - 87
test/modules/QueueTests.cpp

@@ -1,6 +1,13 @@
-#include "../Tests.hpp"
-#include "core/Queue.hpp"
-#include "core/Test.hpp"
+module;
+
+#include <stdio.h>
+
+module Tests;
+
+import Core.Queue;
+import Core.Test;
+import Core.Types;
+import Core.Meta;
 
 template class Core::Queue<int, 5>;
 
@@ -49,82 +56,82 @@ static void resetTester() {
 
 static void testReadAndWrite() {
     Core::Queue<int, 5> buffer;
-    TEST_FALSE(buffer.canRemove());
-    TEST(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
+    Core::test(0, buffer.getLength());
     buffer.add(4);
-    TEST(1, buffer.getLength());
-    TEST_TRUE(buffer.canRemove());
-    TEST(4, buffer[0]);
+    Core::test(1, buffer.getLength());
+    Core::testTrue(buffer.canRemove());
+    Core::test(4, buffer[0]);
     buffer.remove();
-    TEST_FALSE(buffer.canRemove());
-    TEST(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
+    Core::test(0, buffer.getLength());
 }
 
 static void testOverflow() {
     Core::Queue<int, 3> buffer;
     buffer.add(1).add(2).add(3).add(4).add(5);
-    TEST(3, buffer.getLength());
-    TEST(1, buffer[0]);
+    Core::test(3, buffer.getLength());
+    Core::test(1, buffer[0]);
     buffer.remove();
-    TEST(2, buffer.getLength());
-    TEST(2, buffer[0]);
+    Core::test(2, buffer.getLength());
+    Core::test(2, buffer[0]);
     buffer.remove();
-    TEST(1, buffer.getLength());
-    TEST(3, buffer[0]);
+    Core::test(1, buffer.getLength());
+    Core::test(3, buffer[0]);
     buffer.remove();
-    TEST_FALSE(buffer.canRemove());
-    TEST(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
+    Core::test(0, buffer.getLength());
 }
 
 static void testRefill() {
     Core::Queue<int, 3> buffer;
     buffer.add(1).add(2).add(3).add(4);
-    TEST(3, buffer.getLength());
-    TEST_TRUE(buffer.canRemove());
-    TEST(1, buffer[0]);
+    Core::test(3, buffer.getLength());
+    Core::testTrue(buffer.canRemove());
+    Core::test(1, buffer[0]);
     buffer.remove();
-    TEST(2, buffer[0]);
+    Core::test(2, buffer[0]);
     buffer.remove();
-    TEST(3, buffer[0]);
+    Core::test(3, buffer[0]);
     buffer.remove();
-    TEST(0, buffer.getLength());
-    TEST_FALSE(buffer.canRemove());
+    Core::test(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
     buffer.add(5).add(6);
-    TEST(2, buffer.getLength());
-    TEST_TRUE(buffer.canRemove());
-    TEST(5, buffer[0]);
+    Core::test(2, buffer.getLength());
+    Core::testTrue(buffer.canRemove());
+    Core::test(5, buffer[0]);
     buffer.remove();
-    TEST(6, buffer[0]);
+    Core::test(6, buffer[0]);
     buffer.remove();
-    TEST_FALSE(buffer.canRemove());
-    TEST(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
+    Core::test(0, buffer.getLength());
 }
 
 static void testClear() {
     Core::Queue<int, 3> buffer;
     buffer.add(1).add(2);
-    TEST(2, buffer.getLength());
+    Core::test(2, buffer.getLength());
     buffer.clear();
-    TEST_FALSE(buffer.canRemove());
-    TEST(0, buffer.getLength());
+    Core::testFalse(buffer.canRemove());
+    Core::test(0, buffer.getLength());
 }
 
 static void testConstructDestruct() {
     resetTester();
     Core::Queue<Tester, 3> buffer;
     buffer.add();
-    TEST(1, Tester::sum);
+    Core::test(1, Tester::sum);
     buffer.add();
-    TEST(3, Tester::sum);
+    Core::test(3, Tester::sum);
     buffer.add();
-    TEST(6, Tester::sum);
+    Core::test(6, Tester::sum);
 
     buffer.remove();
-    TEST(5, Tester::sum);
+    Core::test(5, Tester::sum);
     buffer.remove();
-    TEST(3, Tester::sum);
+    Core::test(3, Tester::sum);
     buffer.remove();
-    TEST(0, Tester::sum);
+    Core::test(0, Tester::sum);
 }
 
 static void testCopyDestruct() {
@@ -132,18 +139,18 @@ static void testCopyDestruct() {
     {
         Core::Queue<Tester, 3> buffer;
         buffer.add();
-        TEST(1, Tester::sum);
+        Core::test(1, Tester::sum);
         buffer.add();
-        TEST(3, Tester::sum);
+        Core::test(3, Tester::sum);
         buffer.add();
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
         {
             Core::Queue<Tester, 3> copy = buffer;
-            TEST(6 + 4 + 5 + 6, Tester::sum);
+            Core::test(6 + 4 + 5 + 6, Tester::sum);
         }
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
     }
-    TEST(0, Tester::sum);
+    Core::test(0, Tester::sum);
 }
 
 static void testCopyAssignmentDestruct() {
@@ -151,19 +158,19 @@ static void testCopyAssignmentDestruct() {
     {
         Core::Queue<Tester, 3> buffer;
         buffer.add();
-        TEST(1, Tester::sum);
+        Core::test(1, Tester::sum);
         buffer.add();
-        TEST(3, Tester::sum);
+        Core::test(3, Tester::sum);
         buffer.add();
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
         {
             Core::Queue<Tester, 3> copy;
             copy = buffer;
-            TEST(6 + 4 + 5 + 6, Tester::sum);
+            Core::test(6 + 4 + 5 + 6, Tester::sum);
         }
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
     }
-    TEST(0, Tester::sum);
+    Core::test(0, Tester::sum);
 }
 
 static void testMoveDestruct() {
@@ -171,19 +178,19 @@ static void testMoveDestruct() {
     {
         Core::Queue<Tester, 3> buffer;
         buffer.add();
-        TEST(1, Tester::sum);
+        Core::test(1, Tester::sum);
         buffer.add();
-        TEST(3, Tester::sum);
+        Core::test(3, Tester::sum);
         buffer.add();
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
         {
             Core::Queue<Tester, 3> move = Core::move(buffer);
-            TEST(4 + 5 + 6, Tester::sum);
-            TEST(0, buffer.getLength());
+            Core::test(4 + 5 + 6, Tester::sum);
+            Core::test(0, buffer.getLength());
         }
-        TEST(0, Tester::sum);
+        Core::test(0, Tester::sum);
     }
-    TEST(0, Tester::sum);
+    Core::test(0, Tester::sum);
 }
 
 static void testMoveAssignmentDestruct() {
@@ -191,59 +198,59 @@ static void testMoveAssignmentDestruct() {
     {
         Core::Queue<Tester, 3> buffer;
         buffer.add();
-        TEST(1, Tester::sum);
+        Core::test(1, Tester::sum);
         buffer.add();
-        TEST(3, Tester::sum);
+        Core::test(3, Tester::sum);
         buffer.add();
-        TEST(6, Tester::sum);
+        Core::test(6, Tester::sum);
         {
             Core::Queue<Tester, 3> move;
             move = Core::move(buffer);
-            TEST(4 + 5 + 6, Tester::sum);
-            TEST(0, buffer.getLength());
+            Core::test(4 + 5 + 6, Tester::sum);
+            Core::test(0, buffer.getLength());
         }
-        TEST(0, Tester::sum);
+        Core::test(0, Tester::sum);
     }
-    TEST(0, Tester::sum);
+    Core::test(0, Tester::sum);
 }
 
 static void testOverall() {
     resetTester();
     Core::Queue<Tester, 3> buffer;
     buffer.add().add().add();
-    TEST_STRING("[1, 2, 3]", buffer);
-    TEST(3, buffer.getLength());
-    TEST(6, Tester::sum);
+    Core::testString("[1, 2, 3]", buffer);
+    Core::test(3, buffer.getLength());
+    Core::test(6, Tester::sum);
 
     buffer.remove();
-    TEST_STRING("[2, 3]", buffer);
-    TEST(2, buffer.getLength());
-    TEST(5, Tester::sum);
+    Core::testString("[2, 3]", buffer);
+    Core::test(2, buffer.getLength());
+    Core::test(5, Tester::sum);
 
     buffer.add();
-    TEST_STRING("[2, 3, 4]", buffer);
-    TEST(3, buffer.getLength());
-    TEST(9, Tester::sum);
+    Core::testString("[2, 3, 4]", buffer);
+    Core::test(3, buffer.getLength());
+    Core::test(9, Tester::sum);
 
     buffer.remove();
-    TEST_STRING("[3, 4]", buffer);
-    TEST(2, buffer.getLength());
-    TEST(7, Tester::sum);
+    Core::testString("[3, 4]", buffer);
+    Core::test(2, buffer.getLength());
+    Core::test(7, Tester::sum);
 
     buffer.add();
-    TEST_STRING("[3, 4, 5]", buffer);
-    TEST(3, buffer.getLength());
-    TEST(12, Tester::sum);
+    Core::testString("[3, 4, 5]", buffer);
+    Core::test(3, buffer.getLength());
+    Core::test(12, Tester::sum);
 
     buffer.remove();
-    TEST_STRING("[4, 5]", buffer);
-    TEST(2, buffer.getLength());
-    TEST(9, Tester::sum);
+    Core::testString("[4, 5]", buffer);
+    Core::test(2, buffer.getLength());
+    Core::test(9, Tester::sum);
 
     buffer.clear();
-    TEST_STRING("[]", buffer);
-    TEST(0, buffer.getLength());
-    TEST(0, Tester::sum);
+    Core::testString("[]", buffer);
+    Core::test(0, buffer.getLength());
+    Core::test(0, Tester::sum);
 }
 
 void testQueue() {

+ 18 - 15
test/modules/RandomTests.cpp

@@ -1,6 +1,9 @@
-#include "../Tests.hpp"
-#include "core/Random.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Random;
+import Core.Test;
+import Core.Types;
+import Core.Array;
 
 using Core::Random;
 
@@ -12,7 +15,7 @@ static void testAverage(bool light) {
         sum += static_cast<float>(r.nextU32(2, 11));
     }
     sum /= static_cast<float>(limit);
-    TEST_FLOAT(6.0f, sum, 0.01f);
+    Core::testFloat(6.0f, sum, 0.01f);
 }
 
 static void testCoin(bool light) {
@@ -22,7 +25,7 @@ static void testCoin(bool light) {
     for(int i = 0; i < limit; i++) {
         c[r.nextBool()]++;
     }
-    TEST_FLOAT(
+    Core::testFloat(
         0.0f, static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
         0.003f);
 }
@@ -34,10 +37,10 @@ static void testDistribution(bool light) {
     for(size_t i = 0; i < limit; i++) {
         c[r.nextSize(1, c.getLength() - 1)]++;
     }
-    TEST(0, c[0]);
-    TEST(0, c[c.getLength() - 1]);
+    Core::test(0, c[0]);
+    Core::test(0, c[c.getLength() - 1]);
     for(size_t i = 1; i < c.getLength() - 1; i++) {
-        TEST_FLOAT(
+        Core::testFloat(
             0.01f, static_cast<float>(c[i]) / static_cast<float>(limit),
             0.001f);
     }
@@ -51,7 +54,7 @@ static void testFloatAverage(bool light) {
         sum += r.nextFloat();
     }
     sum /= static_cast<float>(limit);
-    TEST_FLOAT(0.5f, sum, 0.001f);
+    Core::testFloat(0.5f, sum, 0.001f);
 }
 
 static void testFloatCoin(bool light) {
@@ -61,7 +64,7 @@ static void testFloatCoin(bool light) {
     for(int i = 0; i < limit; i++) {
         c[r.nextFloat() < 0.5f]++;
     }
-    TEST_FLOAT(
+    Core::testFloat(
         0.0f, static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
         0.003f);
 }
@@ -73,10 +76,10 @@ static void testFloatDistribution(bool light) {
     for(int i = 0; i < limit; i++) {
         c[static_cast<size_t>(1.0f + r.nextFloat() * (c.getLength() - 2))]++;
     }
-    TEST(0, c[0]);
-    TEST(0, c[c.getLength() - 1]);
+    Core::test(0, c[0]);
+    Core::test(0, c[c.getLength() - 1]);
     for(size_t i = 1; i < c.getLength() - 1; i++) {
-        TEST_FLOAT(
+        Core::testFloat(
             0.01f, static_cast<float>(c[i]) / static_cast<float>(limit),
             0.003f);
     }
@@ -88,12 +91,12 @@ static void testRandomI32() {
     c.fill(0);
     for(int i = 0; i < 10'000; i++) {
         i32 index = r.nextI32(-2, 3) + 3;
-        if(TEST_TRUE(index >= 0)) {
+        if(Core::testTrue(index >= 0)) {
             c[static_cast<size_t>(index)]++;
         }
     }
     for(size_t i = 0; i < c.getLength(); i++) {
-        TEST(i != 0 && i != c.getLength() - 1, c[i] > 0);
+        Core::test(i != 0 && i != c.getLength() - 1, c[i] > 0);
     }
 }
 

+ 29 - 23
test/modules/ReadLineTests.cpp

@@ -1,12 +1,19 @@
+module;
+
 #include <cstdio>
+#include <source_location>
+
+#include "../../src/ErrorSimulator.hpp"
+
+module Tests;
 
-#include "../Tests.hpp"
-#include "../src/ErrorSimulator.hpp"
-#include "core/Clock.hpp"
-#include "core/ReadLine.hpp"
-#include "core/Test.hpp"
+import Core.Clock;
+import Core.ReadLine;
+import Core.Test;
 
-static void testStringError(int line, const char* s) {
+static void testStringError(
+    const char* s,
+    const std::source_location& l = std::source_location::current()) {
     char buffer[256];
     for(int i = 0; i < 200; i++) {
         if(Core::readLine(buffer, sizeof(buffer))) {
@@ -14,7 +21,7 @@ static void testStringError(int line, const char* s) {
         }
         Core::Clock::sleepMillis(10);
     }
-    if(!Core::testString(__FILE__, line, s, buffer)) {
+    if(!Core::testString(s, buffer, l)) {
         const char* p = buffer;
         printf("Invalid sequence: ");
         while(*p != 0) {
@@ -35,32 +42,31 @@ static void testStringError(int line, const char* s) {
 void testReadLine() {
 #ifdef ERROR_SIMULATOR
     failStepThrow = 1;
-    if(!TEST_TRUE(Core::startReadLine())) {
+    if(!Core::testTrue(Core::startReadLine())) {
         return;
     }
     failStepThrow = 0;
 #endif
-    if(!TEST_FALSE(Core::startReadLine())) {
+    if(!Core::testFalse(Core::startReadLine())) {
         return;
     }
-    testStringError(__LINE__, "wusi");
-    testStringError(__LINE__, "gusi");
-    testStringError(__LINE__, "abc");
-    testStringError(__LINE__, "abc");
-    testStringError(__LINE__, "abd");
-    testStringError(__LINE__, "abö");
-    testStringError(__LINE__, "ghi");
-    testStringError(__LINE__, "abcghi");
-    testStringError(__LINE__, "abcghi");
-    testStringError(__LINE__, "abö");
-    testStringError(__LINE__, "abac");
+    testStringError("wusi");
+    testStringError("gusi");
+    testStringError("abc");
+    testStringError("abc");
+    testStringError("abd");
+    testStringError("abö");
+    testStringError("ghi");
+    testStringError("abcghi");
+    testStringError("abcghi");
+    testStringError("abö");
+    testStringError("abac");
     testStringError(
-        __LINE__,
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbb");
-    testStringError(__LINE__, "abäo");
-    testStringError(__LINE__, "bäöo");
+    testStringError("abäo");
+    testStringError("bäöo");
     Core::stopReadLine();
 }

+ 46 - 41
test/modules/TerminalTests.cpp

@@ -1,10 +1,15 @@
+module;
+
 #include <stdio.h>
 
-#include "../Tests.hpp"
-#include "core/Clock.hpp"
-#include "core/Logger.hpp"
-#include "core/Terminal.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Clock;
+import Core.Logger;
+import Core.Terminal;
+import Core.Test;
+import Core.Vector;
+import Core.Types;
 
 #define KEY_CASE(key)           \
     case key: puts(#key); break
@@ -12,22 +17,22 @@
 void testTerminal(bool tty) {
     Core::IntVector2 size = Core::getTerminalSize();
     if(tty) {
-        TEST_TRUE(size[0] > 0);
-        TEST_TRUE(size[1] > 0);
-        TEST_FALSE(Core::enterRawTerminal());
-        TEST_FALSE(Core::leaveRawTerminal());
+        Core::testTrue(size[0] > 0);
+        Core::testTrue(size[1] > 0);
+        Core::testFalse(Core::enterRawTerminal());
+        Core::testFalse(Core::leaveRawTerminal());
     } else {
-        TEST(0, size[0]);
-        TEST(0, size[1]);
+        Core::test(0, size[0]);
+        Core::test(0, size[1]);
     }
 
     Core::enterAlternativeTerminal();
     Core::clearTerminal();
     Core::resetCursor();
-    LOG_ERROR("Not visible!");
+    Core::logError("Not visible!");
     Core::leaveAlternativeTerminal();
 
-    LOG_ERROR("Not visible!");
+    Core::logError("Not visible!");
     Core::moveCursorUp(2);
     Core::moveCursorDown(1);
     Core::moveCursorLeft(3);
@@ -37,13 +42,13 @@ void testTerminal(bool tty) {
     Core::hideCursor();
     Core::showCursor();
 
-    u64 u = TERMINAL_KEY_F5 | TERMINAL_KEY_SHIFT;
-    TEST_TRUE(Core::isSpecialChar(u));
+    u64 u = Core::Terminal::KEY_F5 | Core::Terminal::KEY_SHIFT;
+    Core::testTrue(Core::isSpecialChar(u));
     Core::SpecialChar s = Core::convertToSpecialChar(u);
-    TEST(TERMINAL_KEY_F5, s.key);
-    TEST_FALSE(s.alt);
-    TEST_FALSE(s.control);
-    TEST_TRUE(s.shift);
+    Core::test(Core::Terminal::KEY_F5, s.key);
+    Core::testFalse(s.alt);
+    Core::testFalse(s.control);
+    Core::testTrue(s.shift);
 }
 
 void testInteractiveTerminal(void) {
@@ -52,7 +57,7 @@ void testInteractiveTerminal(void) {
     Core::resetCursor();
     Core::hideCursor();
     puts("Hi there");
-    LOG_WARNING("This is a test");
+    Core::logWarning("This is a test");
     Core::IntVector2 v = Core::getTerminalSize();
     printf("%d %d\n", v[0], v[1]);
     Core::clearTerminal();
@@ -81,27 +86,27 @@ void testInteractiveTerminal(void) {
                 printf("ALT + ");
             }
             switch(sc.key) {
-                KEY_CASE(TERMINAL_KEY_ARROW_LEFT);
-                KEY_CASE(TERMINAL_KEY_ARROW_RIGHT);
-                KEY_CASE(TERMINAL_KEY_ARROW_UP);
-                KEY_CASE(TERMINAL_KEY_ARROW_DOWN);
-                KEY_CASE(TERMINAL_KEY_DELETE);
-                KEY_CASE(TERMINAL_KEY_F1);
-                KEY_CASE(TERMINAL_KEY_F2);
-                KEY_CASE(TERMINAL_KEY_F3);
-                KEY_CASE(TERMINAL_KEY_F4);
-                KEY_CASE(TERMINAL_KEY_F5);
-                KEY_CASE(TERMINAL_KEY_F6);
-                KEY_CASE(TERMINAL_KEY_F7);
-                KEY_CASE(TERMINAL_KEY_F8);
-                KEY_CASE(TERMINAL_KEY_F9);
-                KEY_CASE(TERMINAL_KEY_F10);
-                KEY_CASE(TERMINAL_KEY_F11);
-                KEY_CASE(TERMINAL_KEY_F12);
-                KEY_CASE(TERMINAL_KEY_PAGE_UP);
-                KEY_CASE(TERMINAL_KEY_PAGE_DOWN);
-                KEY_CASE(TERMINAL_KEY_HOME);
-                KEY_CASE(TERMINAL_KEY_END);
+                KEY_CASE(Core::Terminal::KEY_ARROW_LEFT);
+                KEY_CASE(Core::Terminal::KEY_ARROW_RIGHT);
+                KEY_CASE(Core::Terminal::KEY_ARROW_UP);
+                KEY_CASE(Core::Terminal::KEY_ARROW_DOWN);
+                KEY_CASE(Core::Terminal::KEY_DELETE);
+                KEY_CASE(Core::Terminal::KEY_F1);
+                KEY_CASE(Core::Terminal::KEY_F2);
+                KEY_CASE(Core::Terminal::KEY_F3);
+                KEY_CASE(Core::Terminal::KEY_F4);
+                KEY_CASE(Core::Terminal::KEY_F5);
+                KEY_CASE(Core::Terminal::KEY_F6);
+                KEY_CASE(Core::Terminal::KEY_F7);
+                KEY_CASE(Core::Terminal::KEY_F8);
+                KEY_CASE(Core::Terminal::KEY_F9);
+                KEY_CASE(Core::Terminal::KEY_F10);
+                KEY_CASE(Core::Terminal::KEY_F11);
+                KEY_CASE(Core::Terminal::KEY_F12);
+                KEY_CASE(Core::Terminal::KEY_PAGE_UP);
+                KEY_CASE(Core::Terminal::KEY_PAGE_DOWN);
+                KEY_CASE(Core::Terminal::KEY_HOME);
+                KEY_CASE(Core::Terminal::KEY_END);
                 default: printf("%lu\n", sc.key); break;
             }
         } else {

+ 16 - 14
test/modules/TestTests.cpp

@@ -1,19 +1,21 @@
-#include "../Tests.hpp"
-#include "core/Test.hpp"
+module Tests;
+
+import Core.Test;
+import Core.Vector;
 
 void testTest() {
-    TEST(false, true);
-    TEST(0, 1);
-    TEST(0l, 1);
-    TEST(0lu, 1);
-    TEST(0llu, 1);
-    TEST(Core::Vector2(1.0f, 2.0f), Core::Vector2(1.0f, 3.0f));
-    TEST_STRING("a", "b");
-    TEST_FALSE(true);
-    TEST_TRUE(false);
+    Core::test(false, true);
+    Core::test(0, 1);
+    Core::test(0l, 1);
+    Core::test(0lu, 1);
+    Core::test(0llu, 1);
+    Core::test(Core::Vector2(1.0f, 2.0f), Core::Vector2(1.0f, 3.0f));
+    Core::testString("a", "b");
+    Core::testFalse(true);
+    Core::testTrue(false);
     int i = 0;
-    TEST_NULL(&i);
-    TEST_NOT_NULL(nullptr);
-    TEST_FLOAT(0.0f, 1.0f, 0.1f);
+    Core::testNull(&i);
+    Core::testNotNull(nullptr);
+    Core::testFloat(0.0f, 1.0f, 0.1f);
     Core::finalizeTests();
 }

+ 38 - 31
test/modules/ThreadTests.cpp

@@ -1,7 +1,12 @@
+module;
+
 #include "../../src/ErrorSimulator.hpp"
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/Thread.hpp"
+
+module Tests;
+
+import Core.Test;
+import Core.Thread;
+import Core.Meta;
 
 static int runDone = 0;
 
@@ -17,59 +22,61 @@ static void testStart() {
     runDone = 0;
     {
         Core::Thread t;
-        TEST_FALSE(t.start(run, nullptr));
+        Core::testFalse(t.start(run, nullptr));
     }
-    TEST(1, runDone);
+    Core::test(1, runDone);
 }
 
 static void testLambda() {
     IntHolder i(0);
     Core::Thread t;
-    TEST_FALSE(t.start([](void* p) {
-        IntHolder* ip = static_cast<IntHolder*>(p);
-        ip->value = 2;
-    }, &i));
-    TEST_FALSE(t.join());
-    TEST(2, i.value);
+    Core::testFalse(t.start(
+        [](void* p) {
+            IntHolder* ip = static_cast<IntHolder*>(p);
+            ip->value = 2;
+        },
+        &i));
+    Core::testFalse(t.join());
+    Core::test(2, i.value);
 }
 
 static void testJoinWithoutStart() {
     Core::Thread t;
-    TEST_FALSE(t.join());
+    Core::testFalse(t.join());
 }
 
 static void testAutoJoin() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
 }
 
 static void testMove() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
     Core::Thread m = Core::move(t);
-    TEST_FALSE(m.join());
+    Core::testFalse(m.join());
 }
 
 static void testMoveAssignment() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
     Core::Thread m;
     m = Core::move(t);
-    TEST_FALSE(m.join());
+    Core::testFalse(m.join());
 }
 
 static void testMoveIntoActive() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
     Core::Thread m;
     t = Core::move(m);
 }
 
 static void testDoubleJoin() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
-    TEST_FALSE(t.join());
-    TEST_FALSE(t.join());
+    Core::testFalse(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.join());
+    Core::testFalse(t.join());
 }
 
 struct MutexCounter {
@@ -88,17 +95,17 @@ static void incrementMutexCounter(void* p) {
 static void testMutex() {
     MutexCounter mc;
     Core::Thread t[2];
-    TEST_FALSE(t[0].start(incrementMutexCounter, &mc));
-    TEST_FALSE(t[1].start(incrementMutexCounter, &mc));
-    TEST_FALSE(t[0].join());
-    TEST_FALSE(t[1].join());
-    TEST(200'000, mc.counter);
+    Core::testFalse(t[0].start(incrementMutexCounter, &mc));
+    Core::testFalse(t[1].start(incrementMutexCounter, &mc));
+    Core::testFalse(t[0].join());
+    Core::testFalse(t[1].join());
+    Core::test(200'000, mc.counter);
 }
 
 static void testDoubleStart() {
     Core::Thread t;
-    TEST_FALSE(t.start([](void*) {}, nullptr));
-    TEST_TRUE(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
+    Core::testTrue(t.start([](void*) {}, nullptr));
 }
 
 static void testFail() {
@@ -111,10 +118,10 @@ static void testFail() {
 
     Core::Thread t;
     failStepThrow = 1;
-    TEST_TRUE(t.start([](void*) {}, nullptr));
-    TEST_FALSE(t.start([](void*) {}, nullptr));
+    Core::testTrue(t.start([](void*) {}, nullptr));
+    Core::testFalse(t.start([](void*) {}, nullptr));
     failStepThrow = 1;
-    TEST_TRUE(t.join());
+    Core::testTrue(t.join());
 #endif
 }
 

+ 9 - 7
test/modules/UnicodeTests.cpp

@@ -1,15 +1,17 @@
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/Unicode.hpp"
+module Tests;
+
+import Core.Test;
+import Core.Unicode;
+import Core.Types;
 
 void testUnicode(void) {
     for(u32 c = 0; c < 0x10FFFF; c += 50) {
         Core::UTF8 u = Core::convertUnicodeToUTF8(c);
         for(u32 k = 1; k < u.length; k++) {
-            TEST_TRUE(Core::isUTF8Remainder(u.data[k]));
+            Core::testTrue(Core::isUTF8Remainder(u.data[k]));
         }
-        TEST_TRUE(u.length >= 1);
-        TEST(u.length, Core::getUTF8Length(u.data[0]));
-        TEST(c, Core::convertUTF8toUnicode(u));
+        Core::testTrue(u.length >= 1);
+        Core::test(u.length, Core::getUTF8Length(u.data[0]));
+        Core::test(c, Core::convertUTF8toUnicode(u));
     }
 }

+ 18 - 16
test/modules/UniquePointerTests.cpp

@@ -1,6 +1,8 @@
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/UniquePointer.hpp"
+module Tests;
+
+import Core.Test;
+import Core.UniquePointer;
+import Core.Meta;
 
 struct B final {
     static int instances;
@@ -28,43 +30,43 @@ using UniqueB = Core::UniquePointer<B>;
 static void testDestroy() {
     {
         UniqueB p(new B());
-        TEST(1, B::instances);
+        Core::test(1, B::instances);
     }
-    TEST(0, B::instances);
+    Core::test(0, B::instances);
 }
 
 static void testMoveConstructDestroys() {
     UniqueB p1(new B());
-    TEST(1, B::instances);
+    Core::test(1, B::instances);
     UniqueB p2(Core::move(p1));
-    TEST(1, B::instances);
+    Core::test(1, B::instances);
     p2 = nullptr;
-    TEST(0, B::instances);
+    Core::test(0, B::instances);
 }
 
 static void testMoveDestroys() {
     {
         UniqueB p1(new B());
         UniqueB p2(new B());
-        TEST(2, B::instances);
+        Core::test(2, B::instances);
         p1 = Core::move(p2);
-        TEST(1, B::instances);
+        Core::test(1, B::instances);
     }
-    TEST(0, B::instances);
+    Core::test(0, B::instances);
 }
 
 static void testEmpty() {
     UniqueB p;
-    TEST_TRUE(p == nullptr);
-    TEST_TRUE(static_cast<const UniqueB&>(p) == nullptr);
+    Core::testTrue(p == nullptr);
+    Core::testTrue(static_cast<const UniqueB&>(p) == nullptr);
 }
 
 static void testCall() {
     UniqueB p(new B());
-    TEST_FALSE(p->b);
+    Core::testFalse(p->b);
     p->test();
-    TEST_TRUE(p->b);
-    TEST_TRUE(static_cast<const UniqueB&>(p)->b);
+    Core::testTrue(p->b);
+    Core::testTrue(static_cast<const UniqueB&>(p)->b);
 }
 
 void testUniquePointer() {

+ 66 - 60
test/modules/UtilityTests.cpp

@@ -1,31 +1,36 @@
+module;
+
 #include <cstring>
 
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/ToString.hpp"
-#include "core/Utility.hpp"
+module Tests;
+
+import Core.Test;
+import Core.ToString;
+import Core.Utility;
+import Core.Meta;
+import Core.Types;
 
 static void testPopCount() {
-    TEST(4, Core::popCount(0xF));
-    TEST(0, Core::popCount(0x0));
-    TEST(2, Core::popCount(0x6));
-    TEST(7, Core::popCount(0x7F));
-    TEST(3, Core::popCount(0x2A));
-    TEST(32, Core::popCount(0xFFFF'FFFF));
-    TEST(64, Core::popCount(0xFFFF'FFFF'FFFF'FFFF));
-    TEST(44, Core::popCount(0xFFFF'0FFF'FFFF));
+    Core::test(4, Core::popCount(0xF));
+    Core::test(0, Core::popCount(0x0));
+    Core::test(2, Core::popCount(0x6));
+    Core::test(7, Core::popCount(0x7F));
+    Core::test(3, Core::popCount(0x2A));
+    Core::test(32, Core::popCount(0xFFFF'FFFF));
+    Core::test(64, Core::popCount(0xFFFF'FFFF'FFFF'FFFF));
+    Core::test(44, Core::popCount(0xFFFF'0FFF'FFFF));
 }
 
 static void testIf() {
-    TEST_TRUE((Core::IsSame<Core::If<true, int, double>, int>));
-    TEST_TRUE((Core::IsSame<Core::If<false, int, double>, double>));
+    Core::testTrue(Core::IsSame<Core::If<true, int, double>, int>);
+    Core::testTrue(Core::IsSame<Core::If<false, int, double>, double>);
 }
 
 static void testZeroRellocate() {
     void* buffer = Core::reallocateRaw(nullptr, 16);
-    TEST_NOT_NULL(buffer);
+    Core::testNotNull(buffer);
     buffer = Core::reallocateRaw(buffer, 0);
-    TEST_NULL(buffer);
+    Core::testNull(buffer);
 }
 
 static void testMemoryInfoList() {
@@ -45,7 +50,7 @@ static void testZeroAllocate() {
     void* a = Core::allocateRaw(n);
     memset(a, 0, n);
     void* b = Core::zeroAllocateRaw(n);
-    TEST_TRUE(memcmp(a, b, n) == 0);
+    Core::testTrue(memcmp(a, b, n) == 0);
     Core::deallocateRaw(a);
     Core::deallocateRaw(b);
 }
@@ -59,10 +64,10 @@ static void testSwap() {
     SwapTest a = {3, 20};
     SwapTest b = {7, 30};
     Core::swap(a, b);
-    TEST(7, a.i);
-    TEST(3, b.i);
-    TEST(30l, a.d);
-    TEST(20l, b.d);
+    Core::test(7, a.i);
+    Core::test(3, b.i);
+    Core::test(30l, a.d);
+    Core::test(20l, b.d);
 }
 
 static void testSort() {
@@ -72,39 +77,40 @@ static void testSort() {
     Core::bubbleSort(data, n);
     Core::bubbleSort(data, 0);
     for(size_t i = 0; i < n; i++) {
-        TEST(data[i], i);
+        Core::test(data[i], i);
     }
 }
 
 static void testToString() {
     char buffer[512];
-    TEST(10, Core::formatBuffer(buffer, sizeof(buffer), "a#a##a", 1.0, 2, 3));
-    TEST_STRING("a1.00a#a23", buffer);
-    TEST(5, Core::formatBuffer(buffer, sizeof(buffer), "aa##ab"));
-    TEST_STRING("aa#ab", buffer);
-    TEST(6, Core::formatBuffer(buffer, 3, "aaaaaa"));
-    TEST_STRING("aa", buffer);
-    TEST(4, Core::formatBuffer(buffer, 4, "a#", 456));
-    TEST_STRING("a45", buffer);
-    TEST(10, Core::formatBuffer(buffer, 4, "# # #", 456, 567, 78));
-    TEST_STRING("456", buffer);
-    TEST(10, Core::formatBuffer(buffer, 1, "# # #", 456, 567, 78));
-    TEST_STRING("", buffer);
-    TEST(10, Core::formatBuffer(nullptr, 0, "# # #", 456ll, 567l, 78ull));
+    Core::test(
+        10, Core::formatBuffer(buffer, sizeof(buffer), "a#a##a", 1.0, 2, 3));
+    Core::testString("a1.00a#a23", buffer);
+    Core::test(5, Core::formatBuffer(buffer, sizeof(buffer), "aa##ab"));
+    Core::testString("aa#ab", buffer);
+    Core::test(6, Core::formatBuffer(buffer, 3, "aaaaaa"));
+    Core::testString("aa", buffer);
+    Core::test(4, Core::formatBuffer(buffer, 4, "a#", 456));
+    Core::testString("a45", buffer);
+    Core::test(10, Core::formatBuffer(buffer, 4, "# # #", 456, 567, 78));
+    Core::testString("456", buffer);
+    Core::test(10, Core::formatBuffer(buffer, 1, "# # #", 456, 567, 78));
+    Core::testString("", buffer);
+    Core::test(10, Core::formatBuffer(nullptr, 0, "# # #", 456ll, 567l, 78ull));
 
     char c = 'a';
     short s = 4;
     unsigned char cu = 'h';
     unsigned short su = 67;
     signed char cs = 'x';
-    TEST(
+    Core::test(
         10, Core::formatBuffer(
                 buffer, sizeof(buffer), "# # # # #", c, s, cu, su, cs));
-    TEST_STRING("a 4 h 67 x", buffer);
+    Core::testString("a 4 h 67 x", buffer);
 
     unsigned char text[] = "fgsdf";
-    TEST(5, Core::toString(text, buffer, sizeof(buffer)));
-    TEST_STRING("fgsdf", buffer);
+    Core::test(5, Core::toString(text, buffer, sizeof(buffer)));
+    Core::testString("fgsdf", buffer);
 }
 
 void testUtility() {
@@ -125,9 +131,9 @@ static void outOfMemory(void*) {
 [[noreturn]] void testInvalidAllocate() {
     Core::setOutOfMemoryHandler(outOfMemory, nullptr);
     Core::allocateRaw(0xFFF'FFFF'FFFF);
-    TEST_TRUE(false);
+    Core::testTrue(false);
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testInvalidReallocate() {
@@ -135,9 +141,9 @@ static void outOfMemory(void*) {
     void* p = Core::allocateRaw(0xFF);
     Core::printMemoryReport();
     Core::reallocateRaw(p, 0xFFF'FFFF'FFFF);
-    TEST_TRUE(false);
+    Core::testTrue(false);
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testInvalidNew() {
@@ -149,12 +155,12 @@ static void outOfMemory(void*) {
 #pragma GCC diagnostic ignored "-Wpragmas"
 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
 #pragma GCC diagnostic error "-Wlarger-than=17592186044416"
-    volatile char* p = coreNewN(char, 0xFFF'FFFF'FFFF);
+    volatile char* p = Core::newWithSourceN<char>(0xFFF'FFFF'FFFF);
     p[3] = 3;
 #pragma GCC diagnostic pop
-    TEST_TRUE(false);
+    Core::testTrue(false);
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testPreCanary() {
@@ -162,36 +168,36 @@ static void outOfMemory(void*) {
     char* p = static_cast<char*>(Core::allocateRaw(16));
     p[-1] = 0;
     Core::deallocateRaw(p);
-    TEST_TRUE(false);
+    Core::testTrue(false);
 #endif
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testPreCanaryNew() {
 #ifdef CHECK_MEMORY
     volatile char* p2 = new char[3];
-    volatile char* p = coreNewN(char, 16);
+    volatile char* p = Core::newWithSourceN<char>(16);
     delete[] p2;
-    coreDeleteN(p);
-    p = coreNewN(char, 16);
+    Core::deleteWithSourceN(p);
+    p = Core::newWithSourceN<char>(16);
     p[-1] = 0;
-    coreDeleteN(p);
-    TEST_TRUE(false);
+    Core::deleteWithSourceN(p);
+    Core::testTrue(false);
 #endif
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testPreCanaryNewArray() {
 #ifdef CHECK_MEMORY
-    volatile char* p = coreNew(char);
+    volatile char* p = Core::newWithSource<char>();
     p[-1] = 0;
-    coreDelete(p);
-    TEST_TRUE(false);
+    Core::deleteWithSource(p);
+    Core::testTrue(false);
 #endif
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }
 
 [[noreturn]] void testPostCanary() {
@@ -199,8 +205,8 @@ static void outOfMemory(void*) {
     char* p = static_cast<char*>(Core::allocateRaw(16));
     p[17] = 0;
     Core::deallocateRaw(p);
-    TEST_TRUE(false);
+    Core::testTrue(false);
 #endif
     Core::finalizeTests();
-    EXIT(0);
+    Core::exitWithHandler(0);
 }

+ 98 - 87
test/modules/VectorTests.cpp

@@ -1,6 +1,12 @@
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/Vector.hpp"
+module;
+
+#include <cmath>
+
+module Tests;
+
+import Core.Test;
+import Core.Vector;
+import Core.ToString;
 
 const float eps = 0.0001f;
 
@@ -19,18 +25,18 @@ static void testInitAndRead() {
     Core::Vector2 v2(1.0f, 2.0f);
     V3 v3(3.0f, 4.0f, 5.0f);
     Core::Vector4 v4(6.0f, 7.0f, 8.0f, 9.0f);
-    TEST_FLOAT(0.0f, v1[0], 0.0f);
-    TEST_FLOAT(0.0f, v1[0], 0.0f);
-    TEST_FLOAT(0.0f, v1[0], 0.0f);
-    TEST_FLOAT(1.0f, v2[0], 0.0f);
-    TEST_FLOAT(2.0f, v2[1], 0.0f);
-    TEST_FLOAT(3.0f, v3[0], 0.0f);
-    TEST_FLOAT(4.0f, v3[1], 0.0f);
-    TEST_FLOAT(5.0f, v3[2], 0.0f);
-    TEST_FLOAT(6.0f, v4[0], 0.0f);
-    TEST_FLOAT(7.0f, v4[1], 0.0f);
-    TEST_FLOAT(8.0f, v4[2], 0.0f);
-    TEST_FLOAT(9.0f, v4[3], 0.0f);
+    Core::testFloat(0.0f, v1[0], 0.0f);
+    Core::testFloat(0.0f, v1[0], 0.0f);
+    Core::testFloat(0.0f, v1[0], 0.0f);
+    Core::testFloat(1.0f, v2[0], 0.0f);
+    Core::testFloat(2.0f, v2[1], 0.0f);
+    Core::testFloat(3.0f, v3[0], 0.0f);
+    Core::testFloat(4.0f, v3[1], 0.0f);
+    Core::testFloat(5.0f, v3[2], 0.0f);
+    Core::testFloat(6.0f, v4[0], 0.0f);
+    Core::testFloat(7.0f, v4[1], 0.0f);
+    Core::testFloat(8.0f, v4[2], 0.0f);
+    Core::testFloat(9.0f, v4[3], 0.0f);
 }
 
 static V3 angles(float lengthAngle, float widthAngle) {
@@ -41,161 +47,166 @@ static V3 angles(float lengthAngle, float widthAngle) {
 
 static void testSetAngles() {
     float root = sqrtf(2.0f) * 0.5f;
-    TEST(V3(1.0f, 0.0f, 0.0f), angles(0.0f, 0.0f));
-    TEST(V3(root, 0.0f, -root), angles(45.0f, 0.0f));
-    TEST(V3(0.0f, 0.0f, -1.0f), angles(90.0f, 0.0f));
-    TEST(V3(-root, 0.0f, -root), angles(135.0f, 0.0f));
-    TEST(V3(-1.0f, 0.0f, 0.0f), angles(180.0f, 0.0f));
-    TEST(V3(-root, 0.0f, root), angles(225.0f, 0.0f));
-    TEST(V3(0.0f, 0.0f, 1.0f), angles(270.0f, 0.0f));
-    TEST(V3(root, 0.0f, root), angles(315.0f, 0.0f));
-
-    TEST(V3(0.0f, 1.0f, 0.0f), angles(0.0f, 90.0f));
-    TEST(V3(0.0f, 1.0f, 0.0f), angles(90.0f, 90.0f));
-    TEST(V3(0.0f, 1.0f, 0.0f), angles(180.0f, 90.0f));
-    TEST(V3(0.0f, 1.0f, 0.0f), angles(270.0f, 90.0f));
-
-    TEST(V3(0.0f, -1.0f, 0.0f), angles(0.0f, -90.0f));
-    TEST(V3(0.0f, -1.0f, 0.0f), angles(90.0f, -90.0f));
-    TEST(V3(0.0f, -1.0f, 0.0f), angles(180.0f, -90.0f));
-    TEST(V3(0.0f, -1.0f, 0.0f), angles(270.0f, -90.0f));
-
-    TEST(V3(root, root, 0.0f), angles(0.0f, 45.0f));
-    TEST(V3(0.0f, root, -root), angles(90.0f, 45.0f));
-    TEST(V3(-root, root, 0.0f), angles(180.0f, 45.0f));
-    TEST(V3(0.0f, root, root), angles(270.0f, 45.0f));
-
-    TEST(V3(root, -root, 0.0f), angles(0.0f, -45.0f));
-    TEST(V3(0.0f, -root, -root), angles(90.0f, -45.0f));
-    TEST(V3(-root, -root, 0.0f), angles(180.0f, -45.0f));
-    TEST(V3(0.0f, -root, root), angles(270.0f, -45.0f));
-
-    TEST(V3(0.5f, root, -0.5f), angles(45.0f, 45.0f));
+    Core::test(V3(1.0f, 0.0f, 0.0f), angles(0.0f, 0.0f));
+    Core::test(V3(root, 0.0f, -root), angles(45.0f, 0.0f));
+    Core::test(V3(0.0f, 0.0f, -1.0f), angles(90.0f, 0.0f));
+    Core::test(V3(-root, 0.0f, -root), angles(135.0f, 0.0f));
+    Core::test(V3(-1.0f, 0.0f, 0.0f), angles(180.0f, 0.0f));
+    Core::test(V3(-root, 0.0f, root), angles(225.0f, 0.0f));
+    Core::test(V3(0.0f, 0.0f, 1.0f), angles(270.0f, 0.0f));
+    Core::test(V3(root, 0.0f, root), angles(315.0f, 0.0f));
+
+    Core::test(V3(0.0f, 1.0f, 0.0f), angles(0.0f, 90.0f));
+    Core::test(V3(0.0f, 1.0f, 0.0f), angles(90.0f, 90.0f));
+    Core::test(V3(0.0f, 1.0f, 0.0f), angles(180.0f, 90.0f));
+    Core::test(V3(0.0f, 1.0f, 0.0f), angles(270.0f, 90.0f));
+
+    Core::test(V3(0.0f, -1.0f, 0.0f), angles(0.0f, -90.0f));
+    Core::test(V3(0.0f, -1.0f, 0.0f), angles(90.0f, -90.0f));
+    Core::test(V3(0.0f, -1.0f, 0.0f), angles(180.0f, -90.0f));
+    Core::test(V3(0.0f, -1.0f, 0.0f), angles(270.0f, -90.0f));
+
+    Core::test(V3(root, root, 0.0f), angles(0.0f, 45.0f));
+    Core::test(V3(0.0f, root, -root), angles(90.0f, 45.0f));
+    Core::test(V3(-root, root, 0.0f), angles(180.0f, 45.0f));
+    Core::test(V3(0.0f, root, root), angles(270.0f, 45.0f));
+
+    Core::test(V3(root, -root, 0.0f), angles(0.0f, -45.0f));
+    Core::test(V3(0.0f, -root, -root), angles(90.0f, -45.0f));
+    Core::test(V3(-root, -root, 0.0f), angles(180.0f, -45.0f));
+    Core::test(V3(0.0f, -root, root), angles(270.0f, -45.0f));
+
+    Core::test(V3(0.5f, root, -0.5f), angles(45.0f, 45.0f));
 }
 
 static void testCross() {
-    TEST(V3(0, 0, 1), Core::cross(V3(1, 0, 0), V3(0, 1, 0)));
-    TEST(V3(0, -1, 0), Core::cross(V3(1, 0, 0), V3(0, 0, 1)));
-    TEST(V3(0, 0, -1), Core::cross(V3(0, 1, 0), V3(1, 0, 0)));
-    TEST(V3(1, 0, 0), Core::cross(V3(0, 1, 0), V3(0, 0, 1)));
-    TEST(V3(0, 1, 0), Core::cross(V3(0, 0, 1), V3(1, 0, 0)));
-    TEST(V3(-1, 0, 0), Core::cross(V3(0, 0, 1), V3(0, 1, 0)));
+    Core::test(V3(0, 0, 1), Core::cross(V3(1, 0, 0), V3(0, 1, 0)));
+    Core::test(V3(0, -1, 0), Core::cross(V3(1, 0, 0), V3(0, 0, 1)));
+    Core::test(V3(0, 0, -1), Core::cross(V3(0, 1, 0), V3(1, 0, 0)));
+    Core::test(V3(1, 0, 0), Core::cross(V3(0, 1, 0), V3(0, 0, 1)));
+    Core::test(V3(0, 1, 0), Core::cross(V3(0, 0, 1), V3(1, 0, 0)));
+    Core::test(V3(-1, 0, 0), Core::cross(V3(0, 0, 1), V3(0, 1, 0)));
 }
 
 static void testSetAdd() {
     V3 v;
     v += V3(1.0f, 2.0f, 3.0f);
-    TEST(V3(1.0f, 2.0f, 3.0f), v);
+    Core::test(V3(1.0f, 2.0f, 3.0f), v);
     v += V3(2.0f, 3.0f, 4.0f);
-    TEST(V3(3.0f, 5.0f, 7.0f), v);
+    Core::test(V3(3.0f, 5.0f, 7.0f), v);
 }
 
 static void testAdd() {
-    TEST(V3(1.0f, 2.0f, 3.0f), V3() + V3(1.0f, 2.0f, 3.0f));
-    TEST(V3(3.0f, 5.0f, 7.0f), V3(1.0f, 2.0f, 3.0f) + V3(2.0f, 3.0f, 4.0f));
+    Core::test(V3(1.0f, 2.0f, 3.0f), V3() + V3(1.0f, 2.0f, 3.0f));
+    Core::test(
+        V3(3.0f, 5.0f, 7.0f), V3(1.0f, 2.0f, 3.0f) + V3(2.0f, 3.0f, 4.0f));
 }
 
 static void testSetSub() {
     V3 v;
     v -= V3(1.0f, 2.0f, 3.0f);
-    TEST(V3(-1.0f, -2.0f, -3.0f), v);
+    Core::test(V3(-1.0f, -2.0f, -3.0f), v);
     v -= V3(2.0f, 3.0f, 4.0f);
-    TEST(V3(-3.0f, -5.0f, -7.0f), v);
+    Core::test(V3(-3.0f, -5.0f, -7.0f), v);
 }
 
 static void testSub() {
-    TEST(V3(1.0f, 2.0f, 3.0f), V3() - V3(-1.0f, -2.0f, -3.0f));
-    TEST(V3(-1.0f, -1.0f, -1.0f), V3(1.0f, 2.0f, 3.0f) - V3(2.0f, 3.0f, 4.0f));
+    Core::test(V3(1.0f, 2.0f, 3.0f), V3() - V3(-1.0f, -2.0f, -3.0f));
+    Core::test(
+        V3(-1.0f, -1.0f, -1.0f), V3(1.0f, 2.0f, 3.0f) - V3(2.0f, 3.0f, 4.0f));
 }
 
 static void testInvert() {
-    TEST(V3(-1.0f, 2.0f, 3.0f), -V3(1.0f, -2.0f, -3.0f));
+    Core::test(V3(-1.0f, 2.0f, 3.0f), -V3(1.0f, -2.0f, -3.0f));
 }
 
 static void testSetMul() {
     V3 v(1.0f, 2.0f, 3.0f);
     v *= 3.0f;
-    TEST(V3(3.0f, 6.0f, 9.0f), v);
+    Core::test(V3(3.0f, 6.0f, 9.0f), v);
     v *= -2.0f;
-    TEST(V3(-6.0f, -12.0f, -18.0f), v);
+    Core::test(V3(-6.0f, -12.0f, -18.0f), v);
 }
 
 static void testMul() {
-    TEST(V3(-3.0f, -6.0f, -9.0f), 3.0f * V3(-1.0f, -2.0f, -3.0f));
-    TEST(V3(3.0f, 6.0f, 9.0f), V3(1.0f, 2.0f, 3.0f) * 3.0);
+    Core::test(V3(-3.0f, -6.0f, -9.0f), 3.0f * V3(-1.0f, -2.0f, -3.0f));
+    Core::test(V3(3.0f, 6.0f, 9.0f), V3(1.0f, 2.0f, 3.0f) * 3.0);
 }
 
 static void testSetMulVector() {
     V3 v(1.0f, 2.0f, 3.0f);
     v *= V3(2.0f, 1.0f, 3.0f);
-    TEST(V3(2.0f, 2.0f, 9.0f), v);
+    Core::test(V3(2.0f, 2.0f, 9.0f), v);
     v *= V3(-3.0f, 4.0f, -2.0f);
-    TEST(V3(-6.0f, 8.0f, -18.0f), v);
+    Core::test(V3(-6.0f, 8.0f, -18.0f), v);
 }
 
 static void testMulVector() {
-    TEST(
+    Core::test(
         V3(-2.0f, -2.0f, -9.0f),
         V3(2.0f, 1.0f, 3.0f) * V3(-1.0f, -2.0f, -3.0f));
-    TEST(V3(2.0f, 2.0f, 9.0f), V3(1.0f, 2.0f, 3.0f) * V3(2.0f, 1.0f, 3.0f));
+    Core::test(
+        V3(2.0f, 2.0f, 9.0f), V3(1.0f, 2.0f, 3.0f) * V3(2.0f, 1.0f, 3.0f));
 }
 
 static void testSetDiv() {
     V3 v(12.0f, 24.0f, 9.0f);
     v /= 3.0f;
-    TEST(V3(4.0f, 8.0f, 3.0f), v);
+    Core::test(V3(4.0f, 8.0f, 3.0f), v);
     v /= -2.0f;
-    TEST(V3(-2.0f, -4.0f, -1.5f), v);
+    Core::test(V3(-2.0f, -4.0f, -1.5f), v);
 }
 
 static void testDiv() {
-    TEST(V3(-1.0f, -2.0f, -3.0f), V3(-3.0f, -6.0f, -9.0f) / 3.0f);
+    Core::test(V3(-1.0f, -2.0f, -3.0f), V3(-3.0f, -6.0f, -9.0f) / 3.0f);
 }
 
 static void testSetDivVector() {
-    TEST(
+    Core::test(
         V3(-6.0f, -4.0f, -2.0f),
         V3(-12.0f, -4.0f, -6.0f) / V3(2.0f, 1.0f, 3.0f));
-    TEST(
+    Core::test(
         V3(2.0f, -1.0f, 1.0f),
         V3(-6.0f, -4.0f, -2.0f) / V3(-3.0f, 4.0f, -2.0f));
 }
 
 static void testDivVector() {
-    TEST(V3(-2, -0.5f, -1), V3(2, 1, 3) / V3(-1, -2, -3));
-    TEST(V3(0.5f, 2, 1), V3(1, 2, 3) / V3(2, 1, 3));
+    Core::test(V3(-2, -0.5f, -1), V3(2, 1, 3) / V3(-1, -2, -3));
+    Core::test(V3(0.5f, 2, 1), V3(1, 2, 3) / V3(2, 1, 3));
 }
 
 static void testDot() {
-    TEST_FLOAT(9.0f, V3(-4.0f, 2.0f, -3.0f).dot(V3(-1.0f, -2.0f, -3.0f)), eps);
-    TEST_FLOAT(-22.0f, V3(2.0f, 2.0f, -4.0f).dot(V3(1.0f, -2.0f, 5.0f)), eps);
+    Core::testFloat(
+        9.0f, V3(-4.0f, 2.0f, -3.0f).dot(V3(-1.0f, -2.0f, -3.0f)), eps);
+    Core::testFloat(
+        -22.0f, V3(2.0f, 2.0f, -4.0f).dot(V3(1.0f, -2.0f, 5.0f)), eps);
 }
 
 static void testSquareLength() {
-    TEST_FLOAT(29.0f, V3(-4.0f, 2.0f, -3.0f).squareLength(), eps);
-    TEST_FLOAT(24.0f, V3(2.0f, 2.0f, -4.0f).squareLength(), eps);
+    Core::testFloat(29.0f, V3(-4.0f, 2.0f, -3.0f).squareLength(), eps);
+    Core::testFloat(24.0f, V3(2.0f, 2.0f, -4.0f).squareLength(), eps);
 }
 
 static void testLength() {
-    TEST_FLOAT(3.0f, V3(-2.0f, 2.0f, -1.0f).length(), eps);
-    TEST_FLOAT(7.0f, V3(6.0f, 2.0f, -3.0f).length(), eps);
+    Core::testFloat(3.0f, V3(-2.0f, 2.0f, -1.0f).length(), eps);
+    Core::testFloat(7.0f, V3(6.0f, 2.0f, -3.0f).length(), eps);
 }
 
 static void testNormalize() {
     V3 v1(-2.0f, 2.0f, -1.0f);
     V3 v2 = v1 * (1.0f / 3.0f);
     v1.normalize();
-    TEST(v2, v1);
+    Core::test(v2, v1);
 
     V3 v3(6.0f, 2.0f, -3.0f);
     V3 v4 = v3 * (1.0f / 7.0f);
     v3.normalize();
-    TEST(v4, v3);
+    Core::test(v4, v3);
 }
 
 static void testCast() {
-    TEST(V3(-2.5f, 2.6f, 9.0f).toInt(), I3(-2, 2, 9));
-    TEST(I3(-2.5f, 2.6f, 9.0f).toFloat(), V3(-2.0f, 2.0f, 9.0f));
+    Core::test(V3(-2.5f, 2.6f, 9.0f).toInt(), I3(-2, 2, 9));
+    Core::test(I3(-2.5f, 2.6f, 9.0f).toFloat(), V3(-2.0f, 2.0f, 9.0f));
 }
 
 static void testToString() {
@@ -203,13 +214,13 @@ static void testToString() {
     formatBuffer(
         buffer, sizeof(buffer), "# # #", Core::Vector<1, float>(),
         Core::Vector2(2.0f, 3.0f), V3(4.0f, 5.0f, 6.0f));
-    TEST_STRING("[0.00] [2.00, 3.00] [4.00, 5.00, 6.00]", buffer);
+    Core::testString("[0.00] [2.00, 3.00] [4.00, 5.00, 6.00]", buffer);
 }
 
 static void testNormalizeIntVector() {
     I3 i(1, 2, 3);
     i.normalize();
-    TEST(I3(0, 0, 1), i);
+    Core::test(I3(0, 0, 1), i);
 }
 
 void testVector() {

+ 20 - 16
test/modules/ViewTests.cpp

@@ -1,6 +1,10 @@
-#include "../Tests.hpp"
-#include "core/Test.hpp"
-#include "core/View.hpp"
+module Tests;
+
+import Core.Test;
+import Core.View;
+import Core.Quaternion;
+import Core.Matrix;
+import Core.Vector;
 
 using V3 = Core::Vector3;
 using Core::View;
@@ -8,29 +12,29 @@ using Core::View;
 static void testFromAngles() {
     View v;
     v.updateDirections(0.0f, 0.0f);
-    TEST(V3(0.0f, 1.0f, 0.0f), v.getUp());
-    TEST(V3(0.0f, -1.0f, 0.0f), v.getDown());
-    TEST(V3(0.0f, 0.0f, -1.0f), v.getLeft());
-    TEST(V3(0.0f, 0.0f, 1.0f), v.getRight());
-    TEST(V3(1.0f, 0.0f, 0.0f), v.getFront());
-    TEST(V3(-1.0f, 0.0f, 0.0f), v.getBack());
+    Core::test(V3(0.0f, 1.0f, 0.0f), v.getUp());
+    Core::test(V3(0.0f, -1.0f, 0.0f), v.getDown());
+    Core::test(V3(0.0f, 0.0f, -1.0f), v.getLeft());
+    Core::test(V3(0.0f, 0.0f, 1.0f), v.getRight());
+    Core::test(V3(1.0f, 0.0f, 0.0f), v.getFront());
+    Core::test(V3(-1.0f, 0.0f, 0.0f), v.getBack());
 }
 
 static void testFromQuaternion() {
     View v;
     v.updateDirections(Core::Quaternion());
-    TEST(V3(0.0f, 1.0f, 0.0f), v.getUp());
-    TEST(V3(0.0f, -1.0f, 0.0f), v.getDown());
-    TEST(V3(0.0f, 0.0f, -1.0f), v.getLeft());
-    TEST(V3(0.0f, 0.0f, 1.0f), v.getRight());
-    TEST(V3(1.0f, 0.0f, 0.0f), v.getFront());
-    TEST(V3(-1.0f, 0.0f, 0.0f), v.getBack());
+    Core::test(V3(0.0f, 1.0f, 0.0f), v.getUp());
+    Core::test(V3(0.0f, -1.0f, 0.0f), v.getDown());
+    Core::test(V3(0.0f, 0.0f, -1.0f), v.getLeft());
+    Core::test(V3(0.0f, 0.0f, 1.0f), v.getRight());
+    Core::test(V3(1.0f, 0.0f, 0.0f), v.getFront());
+    Core::test(V3(-1.0f, 0.0f, 0.0f), v.getBack());
 }
 
 static void testUpdateMatrix() {
     View v;
     const Core::Matrix& m = v.updateMatrix(V3(1.0f, 2.0f, 3.0f));
-    TEST_STRING(
+    Core::testString(
         "[[0.00, 0.00, 0.00, -0.00], "
         "[0.00, 0.00, 0.00, -0.00], "
         "[0.00, 0.00, 0.00, -0.00], "