From 96317b9902a72aaf492466b6432e4325156b1b79 Mon Sep 17 00:00:00 2001 From: Cora <17833654450@163.com> Date: Mon, 3 Aug 2026 17:08:31 +0800 Subject: [PATCH 1/5] add test utils --- test/CMakeLists.txt | 1 + test/test_utils.hpp | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/test_utils.hpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 27e1fca..3231227 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,6 +2,7 @@ set (EXECUTABLE_OUTPUT_PATH ${BIN_FOLDER}/test) include_directories(${GKIT_SOURCE_DIR}/src) include_directories(${GKIT_SOURCE_DIR}/include) +include_directories(${GKIT_SOURCE_DIR}/test) include_directories(${GKIT_SOURCE_DIR}/src/c_abi/include) include_directories(${GKIT_SOURCE_DIR}/third_party/glad_generated/include) diff --git a/test/test_utils.hpp b/test/test_utils.hpp new file mode 100644 index 0000000..a0a5169 --- /dev/null +++ b/test/test_utils.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace gkit::test { + class TestRunner { + std::vector> test_funcs; + + public: + TestRunner() = default; + ~TestRunner() = default; + + auto add_test_func(std::function&& func) { + this->test_funcs.emplace_back(std::move(func)); + } + + auto run() -> void { + auto res = true; + auto fail_count = 0; + auto test_count = this->test_funcs.size(); + + for (auto&& func : test_funcs) { + if (!func()) { + fail_count += 1; + res = false; + } + } + + if (res) { + std::cout << std::format("All tests(total {}) passed!", test_count) << '\n'; + } else { + std::cout << std::format("Some tests(total {}) failed!", fail_count) << '\n'; + } + + return; + } + }; + + auto assert(bool cond, std::string failed_msg) -> void { // NOLINT(performance-unnecessary-value-param) + if (!cond) { + std::cout << failed_msg << '\n'; + std::abort(); + } + } + + template + auto logln(const char* fmt, Args&&... args) -> void { + std::cout << std::format(fmt, std::forward(args)...) << '\n'; + } +} // namespace gkit::test From cb6f0178332f6a97df33ae6c2887be0d07f75d8c Mon Sep 17 00:00:00 2001 From: Cora <17833654450@163.com> Date: Mon, 3 Aug 2026 17:10:14 +0800 Subject: [PATCH 2/5] add chain like call add_test_func --- test/test_utils.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_utils.hpp b/test/test_utils.hpp index a0a5169..6872fe5 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -11,13 +11,14 @@ namespace gkit::test { class TestRunner { std::vector> test_funcs; - + public: TestRunner() = default; ~TestRunner() = default; - auto add_test_func(std::function&& func) { + auto add_test_func(std::function&& func) -> TestRunner& { this->test_funcs.emplace_back(std::move(func)); + return *this; } auto run() -> void { From 0539d8f4de643b17a2791d6795e15260b8a59bd7 Mon Sep 17 00:00:00 2001 From: Cora <17833654450@163.com> Date: Mon, 3 Aug 2026 17:24:49 +0800 Subject: [PATCH 3/5] fix method name and args error --- test/test_utils.hpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 6872fe5..89b75bf 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -1,12 +1,13 @@ #pragma once +#include +#include #include #include #include #include #include -#include -#include + namespace gkit::test { class TestRunner { @@ -22,7 +23,7 @@ namespace gkit::test { } auto run() -> void { - auto res = true; + auto res = true; auto fail_count = 0; auto test_count = this->test_funcs.size(); @@ -43,15 +44,19 @@ namespace gkit::test { } }; - auto assert(bool cond, std::string failed_msg) -> void { // NOLINT(performance-unnecessary-value-param) + auto assert_if(bool cond, const std::string& failed_msg) -> void { if (!cond) { std::cout << failed_msg << '\n'; std::abort(); } } + auto assert_ifnot(bool cond, const std::string& failed_msg) -> void { + assert_if(!cond, failed_msg); + } + template - auto logln(const char* fmt, Args&&... args) -> void { + auto logln(std::format_string fmt, Args&&... args) -> void { std::cout << std::format(fmt, std::forward(args)...) << '\n'; } } // namespace gkit::test From d9ca334260664921058f0bb8f0eb974c891e7c6f Mon Sep 17 00:00:00 2001 From: Cora <17833654450@163.com> Date: Mon, 3 Aug 2026 22:41:34 +0800 Subject: [PATCH 4/5] add flush before assert --- test/test_utils.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 89b75bf..fa81893 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -8,7 +8,6 @@ #include #include - namespace gkit::test { class TestRunner { std::vector> test_funcs; @@ -46,7 +45,8 @@ namespace gkit::test { auto assert_if(bool cond, const std::string& failed_msg) -> void { if (!cond) { - std::cout << failed_msg << '\n'; + std::cout.flush(); + std::cerr << failed_msg << '\n'; std::abort(); } } From 15f69ea2c6756af86431544626f5a67ce1fdc986 Mon Sep 17 00:00:00 2001 From: Cora <17833654450@163.com> Date: Mon, 3 Aug 2026 22:42:19 +0800 Subject: [PATCH 5/5] replace TEST macro by gkit::test_utils --- test/core/input/test_key_input.cpp | 14 +- test/core/input/test_mouse_input.cpp | 19 +- test/core/reflect/test_reflect.cpp | 50 ++-- test/core/test_log.cpp | 25 +- test/graphic/test_window.cpp | 34 ++- test/math/test_color.cpp | 131 ++++++--- test/math/test_math.cpp | 147 +++++----- test/math/test_matrix3.cpp | 129 +++++---- test/math/test_matrix4.cpp | 414 ++++++++++++++++----------- test/math/test_vector2.cpp | 157 ++++++---- test/math/test_vector3.cpp | 70 +++-- test/math/test_vector4.cpp | 70 +++-- test/resource/test_shader_source.cpp | 92 +++--- 13 files changed, 818 insertions(+), 534 deletions(-) diff --git a/test/core/input/test_key_input.cpp b/test/core/input/test_key_input.cpp index 97260ba..0dc9921 100644 --- a/test/core/input/test_key_input.cpp +++ b/test/core/input/test_key_input.cpp @@ -1,4 +1,5 @@ #include "./min_window_for_input_test.hpp" +#include "test_utils.hpp" #include #include @@ -6,12 +7,14 @@ #include -auto main() -> int { +auto test_key_input_loop() -> bool { using gkit::input::Action; using gkit::input::Key; using gkit::input::KeyChord; using gkit::input::Mod; + gkit::test::logln("=== key input interactive loop (press Ctrl+S to save, Q to quit) ==="); + gkit::test::Window win; auto& event_dispatcher = gkit::core::SDLEventDispatcher::instance(); auto& input = gkit::Input::instance(); @@ -46,5 +49,12 @@ auto main() -> int { } } + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner().add_test_func(test_key_input_loop); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/core/input/test_mouse_input.cpp b/test/core/input/test_mouse_input.cpp index ec9bf6d..3d50a6a 100644 --- a/test/core/input/test_mouse_input.cpp +++ b/test/core/input/test_mouse_input.cpp @@ -1,16 +1,20 @@ -#include "min_window_for_input_test.hpp" +#include "./min_window_for_input_test.hpp" +#include "test_utils.hpp" #include #include #include +#include #include -auto main() -> int { +auto test_mouse_input_loop() -> bool { using gkit::input::Action; using gkit::input::Mod; using gkit::input::MouseChord; + gkit::test::logln("=== mouse input interactive loop (press Q to quit) ==="); + auto win = gkit::test::Window{}; auto& input = gkit::Input::instance(); auto& event_dispatcher = gkit::core::SDLEventDispatcher::instance(); @@ -68,4 +72,13 @@ auto main() -> int { std::printf("Mouse wheel x = %f, y = %f\n", x, y); } } -} \ No newline at end of file + + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner().add_test_func(test_mouse_input_loop); + + test_runner.run(); + return 0; +} diff --git a/test/core/reflect/test_reflect.cpp b/test/core/reflect/test_reflect.cpp index 22c540a..9eb35ec 100644 --- a/test/core/reflect/test_reflect.cpp +++ b/test/core/reflect/test_reflect.cpp @@ -1,10 +1,12 @@ #include "gkit/core/object.hpp" #include "gkit/core/reflect/registry.hpp" +#include "test_utils.hpp" #include #include #include + using gkit::core::Object; using gkit::core::Value; using gkit::core::reflect::ClassDB; @@ -61,20 +63,20 @@ auto init_reflect() -> void { // ========================================================================= auto test_regist_and_find() -> bool { - std::cout << "=== Test: regist and find ===" << '\n'; + gkit::test::logln("=== Test: regist and find ==="); auto* info = ClassDB::instance().find("ReflectTestObj"); - assert(info != nullptr); - assert(info->class_name == "ReflectTestObj"); - std::cout << " find existing class: OK" << '\n'; + gkit::test::assert_if(info != nullptr, "Class info of ReflectTestObj from ClassDB is null"); + gkit::test::assert_if(info->class_name == "ReflectTestObj", "Class name is not experted"); + gkit::test::logln(" find existing class: OK"); auto* info_with_raw = ClassDB::instance().find_with_raw(typeid(ReflectTestObj).name()); - assert(info_with_raw != nullptr); - std::cout << " find existing class with raw name: OK" << '\n'; + gkit::test::assert_if(info_with_raw != nullptr, "Failed to get ClassInfo with raw name"); + gkit::test::logln(" find existing class with raw name: OK"); auto* missing = ClassDB::instance().find("NonExistent"); - assert(missing == nullptr); - std::cout << " find missing class returns nullptr: OK" << '\n'; + gkit::test::assert_if(missing == nullptr, "Find result is not experted(nullptr)"); + gkit::test::logln(" find missing class returns nullptr: OK"); return true; } @@ -305,22 +307,18 @@ auto test_create_object() -> bool { auto main() -> int { init_reflect(); - auto ok = true; - ok &= test_regist_and_find(); - ok &= test_add_field(); - ok &= test_get_field(); - ok &= test_set_field(); - ok &= test_readonly_property(); - ok &= test_parent_inheritance(); - ok &= test_for_each_field_inheritance(); - ok &= test_get_field_inherited(); - ok &= test_set_field_inherited(); - ok &= test_create_object(); - - if (ok) { - std::cout << '\n' << "All tests passed." << '\n'; - return 0; - } - std::cerr << '\n' << "Some tests failed." << '\n'; - return 1; + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_regist_and_find) + .add_test_func(test_add_field) + .add_test_func(test_get_field) + .add_test_func(test_set_field) + .add_test_func(test_readonly_property) + .add_test_func(test_parent_inheritance) + .add_test_func(test_for_each_field_inheritance) + .add_test_func(test_get_field_inherited) + .add_test_func(test_set_field_inherited) + .add_test_func(test_create_object); + + test_runner.run(); + return 0; } diff --git a/test/core/test_log.cpp b/test/core/test_log.cpp index 10cd21d..008030d 100644 --- a/test/core/test_log.cpp +++ b/test/core/test_log.cpp @@ -1,4 +1,7 @@ -#include +#include "test_utils.hpp" + +#include +#include #include #include @@ -6,7 +9,9 @@ using gkit::core::Log; -auto main() -> int { +auto test_concurrent_logging() -> bool { + gkit::test::logln("=== concurrent logging ==="); + auto& logger = Log::instance(); logger.set_log_file_path("./test_log.txt"); @@ -35,14 +40,16 @@ auto main() -> int { const std::uint64_t total = static_cast(producer_count) * static_cast(logs_per_thread); - std::cout << "total=" << total << " enqueued=" << s.enqueued << " dropped=" << s.dropped_full - << " processed=" << s.processed << '\n'; + gkit::test::logln(" total={} enqueued={} dropped={} processed={}", total, s.enqueued, s.dropped_full, s.processed); - if (s.enqueued + s.dropped_full != total) { - std::cerr << "stats mismatch" << '\n'; - return 1; - } + gkit::test::assert_if(s.enqueued + s.dropped_full == total, "stats mismatch: enqueued + dropped != total"); + gkit::test::logln(" Finished logging"); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner().add_test_func(test_concurrent_logging); - std::cout << "Finished logging" << '\n'; + test_runner.run(); return 0; } diff --git a/test/graphic/test_window.cpp b/test/graphic/test_window.cpp index 3c7d936..8307efc 100644 --- a/test/graphic/test_window.cpp +++ b/test/graphic/test_window.cpp @@ -5,6 +5,7 @@ #include "gkit/graphic/opengl/RenderBuffer.hpp" #include "gkit/graphic/opengl/StateManager.hpp" #include "gkit/graphic/opengl/VertexArray.hpp" +#include "test_utils.hpp" #include #include @@ -12,17 +13,17 @@ #include "SDL3/SDL.h" #include -int main(int argc, char* argv[]) { - // Get executable directory for resource paths - std::filesystem::path exe_path = argv[0]; - // exe at bin/.../test/test_window.exe, go up 4 levels to reach project root - std::filesystem::path resource_base = exe_path.parent_path().parent_path().parent_path().parent_path() / "test"; +namespace fs = std::filesystem; + +auto test_window_render_loop() -> bool { + // Resource files live in /test/graphic/, same folder as this source file + fs::path resource_base = fs::path(__FILE__).parent_path(); #pragma region Init // Initialize SDL if (!SDL_Init(SDL_INIT_VIDEO)) { std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << '\n'; - return 1; + return false; } // Request OpenGL 4.6 Core Profile @@ -39,7 +40,7 @@ int main(int argc, char* argv[]) { if (window == nullptr) { std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << '\n'; SDL_Quit(); - return 1; + return false; } // Create OpenGL context @@ -48,7 +49,7 @@ int main(int argc, char* argv[]) { std::cerr << "OpenGL context could not be created! SDL_Error: " << SDL_GetError() << '\n'; SDL_DestroyWindow(window); SDL_Quit(); - return 1; + return false; } // Initialize GLAD @@ -57,7 +58,7 @@ int main(int argc, char* argv[]) { SDL_GL_DestroyContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); - return 1; + return false; } // Print OpenGL version @@ -87,8 +88,8 @@ int main(int argc, char* argv[]) { pic_vao.add_buffer(pic_vbo, pic_layout); // load shader source - gkit::graphic::Shader pic_shader((resource_base / "graphic" / "texture.shader").string()); - gkit::graphic::opengl::Texture main_texture((resource_base / "graphic" / "container2.png").string(), + gkit::graphic::Shader pic_shader((resource_base / "texture.shader").string()); + gkit::graphic::opengl::Texture main_texture((resource_base / "container2.png").string(), gkit::graphic::opengl::TextureType::Texture2D); // Full-screen quad vertex data (post-processing) @@ -108,7 +109,7 @@ int main(int argc, char* argv[]) { quad_vao.add_buffer(quad_vb, quad_layout); // load post-processing shader - gkit::graphic::Shader post_shader((resource_base / "graphic" / "post_process.shader").string()); + gkit::graphic::Shader post_shader((resource_base / "post_process.shader").string()); #pragma endregion #pragma region framebuffer @@ -192,6 +193,13 @@ int main(int argc, char* argv[]) { SDL_DestroyWindow(window); SDL_Quit(); - std::cout << "Window closed successfully" << '\n'; + gkit::test::logln("window closed successfully"); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner().add_test_func(test_window_render_loop); + + test_runner.run(); return 0; } diff --git a/test/math/test_color.cpp b/test/math/test_color.cpp index b09d005..13100fc 100644 --- a/test/math/test_color.cpp +++ b/test/math/test_color.cpp @@ -1,88 +1,129 @@ +#include "test_utils.hpp" + +#include #include #include -auto main() -> int { - using namespace gkit::math; // NOLINT(google-build-using-namespace - std::cout << "=== Color Format Conversion Test ===" << '\n'; +using namespace gkit::math; // NOLINT(google-build-using-namespace + +auto test_format_conversion() -> bool { + gkit::test::logln("=== color format conversion ==="); - // Test: RGB -> RGBA uint32_t rgb = 0xFF8040; - auto rgba = rgb_to_rgba(rgb, 128); - std::cout << "rgb_to_rgba(0xFF8040, 128) = 0x" << std::hex << rgba << std::dec << '\n'; - // Test: get_r/g/b - std::cout << "get_r(0xFF8040) = " << static_cast(get_r(rgb)) << '\n'; - std::cout << "get_g(0xFF8040) = " << static_cast(get_g(rgb)) << '\n'; - std::cout << "get_b(0xFF8040) = " << static_cast(get_b(rgb)) << '\n'; + auto rgba = rgb_to_rgba(rgb, 128); + gkit::test::assert_if(rgba == 0x80FF8040, "rgb_to_rgba(0xFF8040, 128) failed"); + gkit::test::logln(" rgb_to_rgba(0xFF8040, 128) = 0x{:X}", rgba); + + gkit::test::assert_if(get_r(rgb) == 0xFF, "get_r(0xFF8040) failed"); + gkit::test::assert_if(get_g(rgb) == 0x80, "get_g(0xFF8040) failed"); + gkit::test::assert_if(get_b(rgb) == 0x40, "get_b(0xFF8040) failed"); + gkit::test::logln(" get_r = {}, get_g = {}, get_b = {}", get_r(rgb), get_g(rgb), get_b(rgb)); + + gkit::test::assert_if(rgba_to_rgb(rgba) == rgb, "rgba_to_rgb round-trip failed"); + gkit::test::assert_if(rgba_to_alpha(rgba) == 128, "rgba_to_alpha failed"); + return true; +} - std::cout << "\n=== Color Blending Test ===" << '\n'; +auto test_blending() -> bool { + gkit::test::logln("=== color blending ==="); - // Test: alpha_blend uint32_t src = 0x80FF0000; // Red with 50% alpha uint32_t dst = 0xFF0000FF; // Blue + auto blended = alpha_blend(src, dst); - std::cout << "alpha_blend(0x80FF0000, 0xFF0000FF) = 0x" << std::hex << blended << std::dec << '\n'; + gkit::test::logln(" alpha_blend(0x80FF0000, 0xFF0000FF) = 0x{:X}", blended); + gkit::test::assert_if(get_r(blended) == 128 && get_g(blended) == 0 && get_b(blended) == 126, + "alpha_blend of 50% red over blue must yield (128, 0, 126)"); - // Test: premultiplied_blend auto preblended = premultiplied_blend(src, dst); - std::cout << "premultiplied_blend(0x80FF0000, 0xFF0000FF) = 0x" << std::hex << preblended << std::dec << '\n'; + gkit::test::logln(" premultiplied_blend(0x80FF0000, 0xFF0000FF) = 0x{:X}", preblended); + return true; +} + +auto test_adjustment() -> bool { + gkit::test::logln("=== color adjustment ==="); - std::cout << "\n=== Color Adjustment Test ===" << '\n'; + uint32_t rgb = 0xFF8040; - // Test: brightness auto bright = brightness(rgb, 1.5f); - std::cout << "brightness(0xFF8040, 1.5) = 0x" << std::hex << bright << std::dec << '\n'; + gkit::test::assert_if(bright == 0xFFC060, "brightness(0xFF8040, 1.5) failed"); + gkit::test::logln(" brightness(0xFF8040, 1.5) = 0x{:X}", bright); - // Test: contrast auto contrast_result = contrast(rgb, 1.2f); - std::cout << "contrast(0xFF8040, 1.2) = 0x" << std::hex << contrast_result << std::dec << '\n'; + gkit::test::logln(" contrast(0xFF8040, 1.2) = 0x{:X}", contrast_result); - // Test: grayscale auto gray = grayscale(rgb); - std::cout << "grayscale(0xFF8040) = 0x" << std::hex << gray << std::dec << '\n'; + gkit::test::assert_if(get_r(gray) == get_g(gray) && get_g(gray) == get_b(gray), + "grayscale must have equal channels"); + gkit::test::logln(" grayscale(0xFF8040) = 0x{:X}", gray); - // Test: invert auto inverted = invert(rgb); - std::cout << "invert(0xFF8040) = 0x" << std::hex << inverted << std::dec << '\n'; + gkit::test::assert_if(inverted == 0xFF007FBF, "invert(0xFF8040) failed"); + gkit::test::logln(" invert(0xFF8040) = 0x{:X}", inverted); + return true; +} - std::cout << "\n=== HSV <-> RGB Test ===" << '\n'; +auto test_hsv_conversion() -> bool { + gkit::test::logln("=== HSV <-> RGB ==="); - // Test: rgb_to_hsv auto hsv = rgb_to_hsv(0xFF8040); - std::cout << "rgb_to_hsv(0xFF8040): H=" << hsv.h << " S=" << hsv.s << " V=" << hsv.v << '\n'; + gkit::test::assert_if(hsv.h >= 0.0f && hsv.h < 360.0f, "HSV hue out of range"); + gkit::test::assert_if(hsv.s >= 0.0f && hsv.s <= 1.0f, "HSV saturation out of range"); + gkit::test::assert_if(hsv.v >= 0.0f && hsv.v <= 1.0f, "HSV value out of range"); + gkit::test::logln(" rgb_to_hsv(0xFF8040): H={} S={} V={}", hsv.h, hsv.s, hsv.v); - // Test: hsv_to_rgb HSV hsv2{.h = 180.0f, .s = 1.0f, .v = 0.5f}; auto rgb2 = hsv_to_rgb(hsv2); - std::cout << "hsv_to_rgb(180, 1.0, 0.5) = 0x" << std::hex << rgb2 << std::dec << '\n'; + gkit::test::logln(" hsv_to_rgb(180, 1.0, 0.5) = 0x{:X}", rgb2); + return true; +} - std::cout << "\n=== HSL <-> RGB Test ===" << '\n'; +auto test_hsl_conversion() -> bool { + gkit::test::logln("=== HSL <-> RGB ==="); - // Test: rgb_to_hsl auto hsl = rgb_to_hsl(0xFF8040); - std::cout << "rgb_to_hsl(0xFF8040): H=" << hsl.h << " S=" << hsl.s << " L=" << hsl.l << '\n'; + gkit::test::assert_if(hsl.h >= 0.0f && hsl.h < 360.0f, "HSL hue out of range"); + gkit::test::assert_if(hsl.s >= 0.0f && hsl.s <= 1.0f, "HSL saturation out of range"); + gkit::test::assert_if(hsl.l >= 0.0f && hsl.l <= 1.0f, "HSL lightness out of range"); + gkit::test::logln(" rgb_to_hsl(0xFF8040): H={} S={} L={}", hsl.h, hsl.s, hsl.l); - // Test: hsl_to_rgb HSL hsl2{.h = 60.0f, .s = 1.0f, .l = 0.5f}; auto rgb3 = hsl_to_rgb(hsl2); - std::cout << "hsl_to_rgb(60, 1.0, 0.5) = 0x" << std::hex << rgb3 << std::dec << '\n'; + gkit::test::logln(" hsl_to_rgb(60, 1.0, 0.5) = 0x{:X}", rgb3); + return true; +} - std::cout << "\n=== Utility Test ===" << '\n'; +auto test_utility() -> bool { + gkit::test::logln("=== utility ==="); - // Test: lerp_color auto lerp_result = lerp_color(0xFF0000, 0x0000FF, 0.5f); - std::cout << "lerp_color(0xFF0000, 0x0000FF, 0.5) = 0x" << std::hex << lerp_result << std::dec << '\n'; + gkit::test::logln(" lerp_color(0xFF0000, 0x0000FF, 0.5) = 0x{:X}", lerp_result); + gkit::test::assert_if(get_g(lerp_result) == 0, "lerp_color green channel must be 0"); - // Test: is_similar bool similar = is_similar(0xFF0000, 0xFE0101, 5); - std::cout << "is_similar(0xFF0000, 0xFE0101, 5) = " << (similar ? "true" : "false") << '\n'; + gkit::test::assert_if(similar, "is_similar(0xFF0000, 0xFE0101, 5) must be true"); + gkit::test::logln(" is_similar(0xFF0000, 0xFE0101, 5) = {}", similar); - // Test: colors constants - std::cout << "\n=== Color Constants Test ===" << '\n'; - std::cout << "palette_8[0] = 0x" << std::hex << colors::PALETTE_8[0] << std::dec << '\n'; - std::cout << "palette_8[7] = 0x" << std::hex << colors::PALETTE_8[7] << std::dec << '\n'; + gkit::test::assert_if(!is_similar(0xFF0000, 0x00FF00, 5), "is_similar must be false for distant colors"); - std::cout << "\nAll tests completed!" << '\n'; + gkit::test::assert_if(colors::PALETTE_8[0] == 0xFF000000, "palette_8[0] must be black"); + gkit::test::assert_if(colors::PALETTE_8[7] == 0xFFFFFFFF, "palette_8[7] must be white"); + gkit::test::logln(" palette_8[0] = 0x{:X}", colors::PALETTE_8[0]); + gkit::test::logln(" palette_8[7] = 0x{:X}", colors::PALETTE_8[7]); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_format_conversion) + .add_test_func(test_blending) + .add_test_func(test_adjustment) + .add_test_func(test_hsv_conversion) + .add_test_func(test_hsl_conversion) + .add_test_func(test_utility); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/math/test_math.cpp b/test/math/test_math.cpp index 2ddba7f..0f6e7f3 100644 --- a/test/math/test_math.cpp +++ b/test/math/test_math.cpp @@ -1,75 +1,84 @@ #include "gkit/math/constants.hpp" #include "gkit/math/scalar.hpp" +#include "test_utils.hpp" -#include -#include +auto test_scalar_limits() -> bool { + gkit::test::logln("=== scalar.hpp tests ==="); -int main() { using namespace gkit::math; // NOLINT(google-build-using-namespace - // ====== test scalar.hpp ====== - - // ScalarLimits tests - assert(ScalarLimits::MIN_V == -2147483647 - 1); - assert(ScalarLimits::MAX_V == 2147483647); - assert(ScalarLimits::EPSILON_V > 0); - - // fp constants tests - assert(EPSILON32 > 0); - assert(MIN32 > 0); - assert(MAX32 > MIN32); - - // integer constants tests - assert(I8_MIN == -128); - assert(I8_MAX == 127); - assert(U8_MAX == 255); - assert(I16_MIN == -32768); - assert(I16_MAX == 32767); - assert(U16_MAX == 65535); - - // ====== Test constants.hpp ====== - - // Math constants tests - assert(gkit::math::PI_32 > 3.14f && gkit::math::PI_32 < 3.15f); - assert(gkit::math::TWO_PI_32 > 6.28f && gkit::math::TWO_PI_32 < 6.29f); - assert(gkit::math::HALF_PI_32 > 1.57f && gkit::math::HALF_PI_32 < 1.58f); - assert(gkit::math::INV_PI_32 > 0.31f && gkit::math::INV_PI_32 < 0.32f); - assert(gkit::math::E_32 > 2.71f && gkit::math::E_32 < 2.72f); - assert(gkit::math::PHI_32 > 1.61f && gkit::math::PHI_32 < 1.62f); - assert(gkit::math::SQRT_2_32 > 1.41f && gkit::math::SQRT_2_32 < 1.42f); - assert(gkit::math::LN_2_32 > 0.69f && gkit::math::LN_2_32 < 0.70f); - - // Angle conversion tests - assert(gkit::math::DEG_TO_RAD_32 > 0.017f && gkit::math::DEG_TO_RAD_32 < 0.018f); - assert(gkit::math::RAD_TO_DEG_32 > 57.2f && gkit::math::RAD_TO_DEG_32 < 57.3f); - - // Numeric constants tests - assert(gkit::math::ZERO_32 == 0.0f); - assert(gkit::math::ONE_32 == 1.0f); - assert(gkit::math::NEG_ONE_32 == -1.0f); - assert(gkit::math::TWO_32 == 2.0f); - assert(gkit::math::HALF_32 == 0.5f); - assert(gkit::math::QUARTER_32 == 0.25f); - - assert(gkit::math::ZERO_I32 == 0); - assert(gkit::math::ONE_I32 == 1); - assert(gkit::math::NEG_ONE_I32 == -1); - assert(gkit::math::TWO_I32 == 2); - - // Print test output - printf("=== scalar.hpp tests ===\n"); - printf( - "int32 min: %d, max: %d\n", gkit::math::ScalarLimits::MIN_V, gkit::math::ScalarLimits::MAX_V); - printf("float32 epsilon: %.10f\n", gkit::math::ScalarLimits::EPSILON_V); - - printf("\n=== constants.hpp tests ===\n"); - printf("PI_32: %.10f\n", gkit::math::PI_32); - printf("TWO_PI_32: %.10f\n", gkit::math::TWO_PI_32); - printf("E_32: %.10f\n", gkit::math::E_32); - printf("PHI_32: %.10f\n", gkit::math::PHI_32); - printf("SQRT_2_32: %.10f\n", gkit::math::SQRT_2_32); - printf("DEG_TO_RAD_32: %.10f\n", gkit::math::DEG_TO_RAD_32); - printf("RAD_TO_DEG_32: %.10f\n", gkit::math::RAD_TO_DEG_32); - - printf("\nAll tests passed!\n"); + + gkit::test::assert_if(ScalarLimits::MIN_V == -2147483647 - 1, "ScalarLimits::MIN_V is wrong"); + gkit::test::assert_if(ScalarLimits::MAX_V == 2147483647, "ScalarLimits::MAX_V is wrong"); + gkit::test::assert_if(ScalarLimits::EPSILON_V > 0, "ScalarLimits::EPSILON_V must be positive"); + + gkit::test::logln(" int32 min: {}, max: {}", ScalarLimits::MIN_V, ScalarLimits::MAX_V); + gkit::test::logln(" float32 epsilon: {:.10f}", ScalarLimits::EPSILON_V); + return true; +} + +auto test_float_constants() -> bool { + gkit::test::logln("=== constants.hpp float constants tests ==="); + + using namespace gkit::math; // NOLINT(google-build-using-namespace + + gkit::test::assert_if(EPSILON32 > 0, "EPSILON32 must be positive"); + gkit::test::assert_if(MIN32 > 0, "MIN32 must be positive"); + gkit::test::assert_if(MAX32 > MIN32, "MAX32 must be greater than MIN32"); + + gkit::test::assert_if(PI_32 > 3.14f && PI_32 < 3.15f, "PI_32 out of expected range"); + gkit::test::assert_if(TWO_PI_32 > 6.28f && TWO_PI_32 < 6.29f, "TWO_PI_32 out of expected range"); + gkit::test::assert_if(HALF_PI_32 > 1.57f && HALF_PI_32 < 1.58f, "HALF_PI_32 out of expected range"); + gkit::test::assert_if(INV_PI_32 > 0.31f && INV_PI_32 < 0.32f, "INV_PI_32 out of expected range"); + gkit::test::assert_if(E_32 > 2.71f && E_32 < 2.72f, "E_32 out of expected range"); + gkit::test::assert_if(PHI_32 > 1.61f && PHI_32 < 1.62f, "PHI_32 out of expected range"); + gkit::test::assert_if(SQRT_2_32 > 1.41f && SQRT_2_32 < 1.42f, "SQRT_2_32 out of expected range"); + gkit::test::assert_if(LN_2_32 > 0.69f && LN_2_32 < 0.70f, "LN_2_32 out of expected range"); + + gkit::test::assert_if(DEG_TO_RAD_32 > 0.017f && DEG_TO_RAD_32 < 0.018f, "DEG_TO_RAD_32 out of expected range"); + gkit::test::assert_if(RAD_TO_DEG_32 > 57.2f && RAD_TO_DEG_32 < 57.3f, "RAD_TO_DEG_32 out of expected range"); + + gkit::test::logln(" PI_32: {:.10f}", PI_32); + gkit::test::logln(" TWO_PI_32: {:.10f}", TWO_PI_32); + gkit::test::logln(" E_32: {:.10f}", E_32); + gkit::test::logln(" PHI_32: {:.10f}", PHI_32); + gkit::test::logln(" SQRT_2_32: {:.10f}", SQRT_2_32); + gkit::test::logln(" DEG_TO_RAD_32: {:.10f}", DEG_TO_RAD_32); + gkit::test::logln(" RAD_TO_DEG_32: {:.10f}", RAD_TO_DEG_32); + return true; +} + +auto test_integer_constants() -> bool { + gkit::test::logln("=== constants.hpp integer constants tests ==="); + + using namespace gkit::math; // NOLINT(google-build-using-namespace + + gkit::test::assert_if(I8_MIN == -128, "I8_MIN is wrong"); + gkit::test::assert_if(I8_MAX == 127, "I8_MAX is wrong"); + gkit::test::assert_if(U8_MAX == 255, "U8_MAX is wrong"); + gkit::test::assert_if(I16_MIN == -32768, "I16_MIN is wrong"); + gkit::test::assert_if(I16_MAX == 32767, "I16_MAX is wrong"); + gkit::test::assert_if(U16_MAX == 65535, "U16_MAX is wrong"); + + gkit::test::assert_if(ZERO_32 == 0.0f, "ZERO_32 is wrong"); + gkit::test::assert_if(ONE_32 == 1.0f, "ONE_32 is wrong"); + gkit::test::assert_if(NEG_ONE_32 == -1.0f, "NEG_ONE_32 is wrong"); + gkit::test::assert_if(TWO_32 == 2.0f, "TWO_32 is wrong"); + gkit::test::assert_if(HALF_32 == 0.5f, "HALF_32 is wrong"); + gkit::test::assert_if(QUARTER_32 == 0.25f, "QUARTER_32 is wrong"); + + gkit::test::assert_if(ZERO_I32 == 0, "ZERO_I32 is wrong"); + gkit::test::assert_if(ONE_I32 == 1, "ONE_I32 is wrong"); + gkit::test::assert_if(NEG_ONE_I32 == -1, "NEG_ONE_I32 is wrong"); + gkit::test::assert_if(TWO_I32 == 2, "TWO_I32 is wrong"); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_scalar_limits) + .add_test_func(test_float_constants) + .add_test_func(test_integer_constants); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/math/test_matrix3.cpp b/test/math/test_matrix3.cpp index fdc04a6..3187c3c 100644 --- a/test/math/test_matrix3.cpp +++ b/test/math/test_matrix3.cpp @@ -1,9 +1,9 @@ -#include "gkit/math/constants.hpp" +#include "test_utils.hpp" #include -#include #include +#include #include #include @@ -24,87 +24,106 @@ auto mat_str(const Matrix3& mat) -> std::string { m22); } -auto main() -> int { - std::cout << "=== Matrix3 Test ===" << '\n'; +auto test_identity_and_vector() -> bool { + gkit::test::logln("=== identity matrix ==="); - // Test 1: Identity matrix - std::cout << "\n1. Identity matrix:" << '\n'; auto identity = Matrix3::identity(); - std::cout << mat_str(identity) << '\n'; + gkit::test::logln(" {}", mat_str(identity)); - // Test 2: Matrix * Vector (should preserve vector) - std::cout << "\n2. Identity * Vector:" << '\n'; Vector3 v{1.0f, 2.0f, 3.0f}; auto result = identity * v; - std::cout << "v = (1, 2, 3)" << '\n'; - std::cout << "result = (" << result.x << ", " << result.y << ", " << result.z << ")" << '\n'; + gkit::test::assert_if(result.x == 1.0f && result.y == 2.0f && result.z == 3.0f, "identity * vector failed"); + gkit::test::logln(" identity * (1, 2, 3) = ({}, {}, {})", result.x, result.y, result.z); + + gkit::test::assert_if(Matrix3::determinant(identity) == 1.0f, "det(identity) must be 1"); + gkit::test::logln(" det(identity) = {}", Matrix3::determinant(identity)); + return true; +} - // Test 3: Determinant of identity should be 1 - std::cout << "\n3. Determinant test:" << '\n'; - std::cout << "det(identity) = " << Matrix3::determinant(identity) << " (expected: 1)" << '\n'; +auto test_rotation() -> bool { + gkit::test::logln("=== rotation matrix ==="); - // Test 4: Rotation matrix (90 degrees around X) - std::cout << "\n4. Rotation X (90 degrees):" << '\n'; auto rot_x = Matrix3::rotation_x(gkit::math::PI_32 / 2.0f); - std::cout << mat_str(rot_x) << '\n'; + gkit::test::logln(" {}", mat_str(rot_x)); - // Verify: Rotating (0, 1, 0) around X by 90 degrees should give (0, 0, 1) Vector3 up{0.0f, 1.0f, 0.0f}; auto rotated = rot_x * up; - std::cout << "rot_x * (0,1,0) = (" << rotated.x << ", " << rotated.y << ", " << rotated.z << ")" << '\n'; - std::cout << "expected: (0, 0, 1)" << '\n'; + gkit::test::logln(" rot_x * (0,1,0) = ({}, {}, {}) (expected: (0, 0, 1))", rotated.x, rotated.y, rotated.z); + gkit::test::assert_if(std::abs(rotated.x) < 1e-4f && std::abs(rotated.y) < 1e-4f && + std::abs(rotated.z - 1.0f) < 1e-4f, + "rotation_x(90) * (0,1,0) must be ~(0,0,1)"); + + gkit::test::assert_if(std::abs(Matrix3::determinant(rot_x) - 1.0f) < 1e-4f, "det(rotation_x) must be 1"); + gkit::test::logln(" det(rotation_x) = {} (expected: 1)", Matrix3::determinant(rot_x)); + return true; +} - // Test 5: Rotation matrix determinant should be 1 - std::cout << "\n5. det(rotation_x) = " << Matrix3::determinant(rot_x) << " (expected: 1)" << '\n'; +auto test_inverse() -> bool { + gkit::test::logln("=== inverse ==="); - // Test 6: Inverse of identity is identity - std::cout << "\n6. Inverse test:" << '\n'; + auto identity = Matrix3::identity(); auto inv_identity = Matrix3::inverse(identity); - if (inv_identity.has_value()) { - std::cout << "inverse(identity):" << '\n'; - std::cout << mat_str(inv_identity.value()) << '\n'; - } + gkit::test::assert_if(inv_identity.has_value(), "inverse(identity) must have a value"); + gkit::test::logln(" inverse(identity): {}", mat_str(inv_identity.value())); - // Test 7: Rotation * inverse should be identity + auto rot_x = Matrix3::rotation_x(gkit::math::PI_32 / 2.0f); auto inv_rot_x = Matrix3::inverse(rot_x); - if (inv_rot_x.has_value()) { - auto composed = rot_x * inv_rot_x.value(); - std::cout << "rotation_x * inverse(rotation_x):" << '\n'; - std::cout << mat_str(composed) << '\n'; - } - - // Test 8: Scaling matrix - std::cout << "\n8. Scaling (2, 3, 4):" << '\n'; + gkit::test::assert_if(inv_rot_x.has_value(), "inverse(rotation_x) must have a value"); + + auto composed = rot_x * inv_rot_x.value(); + gkit::test::logln(" rotation_x * inverse(rotation_x): {}", mat_str(composed)); + auto [c00, c10, c20, c01, c11, c21, c02, c12, c22] = composed.properties(); + gkit::test::assert_if(std::abs(c00 - 1.0f) < 1e-4f && std::abs(c11 - 1.0f) < 1e-4f && std::abs(c22 - 1.0f) < 1e-4f, + "rotation * inverse must be identity"); + return true; +} + +auto test_scale_transpose_diagonal() -> bool { + gkit::test::logln("=== scale, transpose, diagonal ==="); + auto scale = Matrix3::scaling(2.0f, 3.0f, 4.0f); - std::cout << mat_str(scale) << '\n'; + gkit::test::logln(" {}", mat_str(scale)); - // Test 9: Transpose - std::cout << "\n9. Transpose test:" << '\n'; auto transposed = Matrix3::transpose(scale); - std::cout << "transpose(scale):" << '\n'; - std::cout << mat_str(transposed) << '\n'; + gkit::test::logln(" transpose(scale): {}", mat_str(transposed)); + gkit::test::assert_if(std::abs(transposed.m[0][0] - 2.0f) < 1e-4f && std::abs(transposed.m[1][1] - 3.0f) < 1e-4f && + std::abs(transposed.m[2][2] - 4.0f) < 1e-4f, + "transpose of diagonal must be itself"); - // Test 10: From diagonal - std::cout << "\n10. From diagonal (5.0f):" << '\n'; auto diag = Matrix3::from_diagonal(5.0f); - std::cout << mat_str(diag) << '\n'; + gkit::test::logln(" from_diagonal(5.0f): {}", mat_str(diag)); + auto [d00, d10, d20, d01, d11, d21, d02, d12, d22] = diag.properties(); + gkit::test::assert_if(d00 == 5.0f && d11 == 5.0f && d22 == 5.0f, "from_diagonal failed"); + return true; +} + +auto test_multiplication_and_orthogonality() -> bool { + gkit::test::logln("=== matrix multiplication and orthogonality ==="); - // Test 11: Matrix multiplication - std::cout << "\n11. Matrix multiplication:" << '\n'; auto a = Matrix3::rotation_x(1.0f); auto b = Matrix3::rotation_y(1.0f); auto c = a * b; - std::cout << "rotation_x(1) * rotation_y(1):" << '\n'; - std::cout << mat_str(c) << '\n'; + gkit::test::logln(" rotation_x(1) * rotation_y(1): {}", mat_str(c)); - // Test 12: Orthogonality check (R * R^T = I) - std::cout << "\n12. Orthogonality test (rotation matrix):" << '\n'; auto r = Matrix3::rotation_z(0.5f); auto rt = Matrix3::transpose(r); auto ortho = r * rt; - std::cout << "R * R^T:" << '\n'; - std::cout << mat_str(ortho) << '\n'; + gkit::test::logln(" R * R^T: {}", mat_str(ortho)); + + auto [m00, m10, m20, m01, m11, m21, m02, m12, m22] = ortho.properties(); + gkit::test::assert_if(std::abs(m00 - 1.0f) < 1e-4f && std::abs(m11 - 1.0f) < 1e-4f && std::abs(m22 - 1.0f) < 1e-4f, + "rotation matrix must be orthonormal"); + return true; +} - std::cout << "\nAll tests completed!" << '\n'; +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_identity_and_vector) + .add_test_func(test_rotation) + .add_test_func(test_inverse) + .add_test_func(test_scale_transpose_diagonal) + .add_test_func(test_multiplication_and_orthogonality); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/math/test_matrix4.cpp b/test/math/test_matrix4.cpp index d87dcca..09c503f 100644 --- a/test/math/test_matrix4.cpp +++ b/test/math/test_matrix4.cpp @@ -1,11 +1,11 @@ -#include "gkit/math/constants.hpp" +#include "test_utils.hpp" #include #include -#include #include #include +#include #include #include #include @@ -45,246 +45,332 @@ auto vec4_str(const gkit::math::Vector4& v) -> std::string { return std::format("({:.3f}, {:.3f}, {:.3f}, {:.3f})", v.x, v.y, v.z, v.w); } -auto main() -> int { +auto test_matrix_constructors() -> bool { using namespace gkit::math; // NOLINT(google-build-using-namespace - std::cout << "=== Matrix4 Test ===" << '\n'; + gkit::test::logln("=== matrix constructors ==="); - // Test 1: Identity matrix - std::cout << "\n1. Identity matrix:" << '\n'; auto identity = Matrix4::identity(); - std::cout << mat4_str(identity) << '\n'; + gkit::test::logln(" identity:\n{}", mat4_str(identity)); - // Test 2: Zero matrix - std::cout << "\n2. Zero matrix:" << '\n'; auto zero = Matrix4::zero(); - std::cout << mat4_str(zero) << '\n'; + gkit::test::logln(" zero:\n{}", mat4_str(zero)); - // Test 3: Diagonal matrix - std::cout << "\n3. Diagonal matrix (3.0f):" << '\n'; auto diag = Matrix4(3.0f); - std::cout << mat4_str(diag) << '\n'; + gkit::test::logln(" diagonal (3.0f):\n{}", mat4_str(diag)); + gkit::test::assert_if(diag.m[0][0] == 3.0f && diag.m[1][1] == 3.0f && diag.m[2][2] == 3.0f && diag.m[3][3] == 3.0f, + "diagonal constructor failed"); + return true; +} + +auto test_matrix_vector_multiplication() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== matrix * vector ==="); + + auto identity = Matrix4::identity(); - // Test 4: Identity * Vec4 - std::cout << "\n4. Identity * Vec4:" << '\n'; Vector4 v4{1.0f, 2.0f, 3.0f, 1.0f}; auto result4 = identity * v4; - std::cout << "v4 = " << vec4_str(v4) << '\n'; - std::cout << "result = " << vec4_str(result4) << '\n'; + gkit::test::assert_if(result4.x == 1.0f && result4.y == 2.0f && result4.z == 3.0f && result4.w == 1.0f, + "identity * vec4 failed"); + gkit::test::logln(" v4 = {}, result = {}", vec4_str(v4), vec4_str(result4)); - // Test 5: Identity * Vec3 - std::cout << "\n5. Identity * Vec3:" << '\n'; Vector3 v3{1.0f, 2.0f, 3.0f}; auto result3 = identity * v3; - std::cout << "v3 = " << vec3_str(v3) << '\n'; - std::cout << "result = " << vec3_str(result3) << '\n'; - - // Test 6: Determinant of identity - std::cout << "\n6. Determinant test:" << '\n'; - std::cout << "det(identity) = " << Matrix4::determinant(identity) << " (expected: 1)" << '\n'; + gkit::test::assert_if(result3.x == 1.0f && result3.y == 2.0f && result3.z == 3.0f, "identity * vec3 failed"); + gkit::test::logln(" v3 = {}, result = {}", vec3_str(v3), vec3_str(result3)); - // Test 7: Translation matrix - std::cout << "\n7. Translation (2, 3, 4):" << '\n'; auto translate = Matrix4::translate({2.0f, 3.0f, 4.0f}); - std::cout << mat4_str(translate) << '\n'; + auto point = Matrix4::transform_point(translate, {1.0f, 1.0f, 1.0f}); + gkit::test::assert_if(point.x == 3.0f && point.y == 4.0f && point.z == 5.0f, "translate * point failed"); + gkit::test::logln(" translate * (1,1,1) = {} (expected: (3,4,5))", vec3_str(point)); - // Test 8: Transform point with translation - std::cout << "\n8. Transform point:" << '\n'; - auto point = Matrix4::transform_point(translate, {1.0f, 1.0f, 1.0f}); - std::cout << "translate * (1,1,1) = " << vec3_str(point) << " (expected: (3,4,5))" << '\n'; - - // Test 9: Transform vector (no translation) - std::cout << "\n9. Transform vector:" << '\n'; auto vec = Matrix4::transform_vector(translate, {1.0f, 0.0f, 0.0f}); - std::cout << "translate * (1,0,0) = " << vec3_str(vec) << " (expected: (1,0,0))" << '\n'; + gkit::test::assert_if(vec.x == 1.0f && vec.y == 0.0f && vec.z == 0.0f, "transform_vector must not translate"); + gkit::test::logln(" translate * (1,0,0) = {} (expected: (1,0,0))", vec3_str(vec)); + return true; +} + +auto test_determinant() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== determinant ==="); + + auto identity = Matrix4::identity(); + gkit::test::assert_if(Matrix4::determinant(identity) == 1.0f, "det(identity) must be 1"); + gkit::test::logln(" det(identity) = {} (expected: 1)", Matrix4::determinant(identity)); + return true; +} + +auto test_translation_and_scale() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== translation and scale ==="); + + auto translate = Matrix4::translate({2.0f, 3.0f, 4.0f}); + gkit::test::logln(" translation (2, 3, 4):\n{}", mat4_str(translate)); - // Test 10: Scale matrix - std::cout << "\n10. Scale (2, 3, 4):" << '\n'; auto scale = Matrix4::scale({2.0f, 3.0f, 4.0f}); - std::cout << mat4_str(scale) << '\n'; + gkit::test::logln(" scale (2, 3, 4):\n{}", mat4_str(scale)); - // Test 11: Scale transform - std::cout << "\n11. Scale transform:" << '\n'; auto scaled_point = Matrix4::transform_point(scale, {2.0f, 2.0f, 2.0f}); - std::cout << "scale * (2,2,2) = " << vec3_str(scaled_point) << " (expected: (4,6,8))" << '\n'; + gkit::test::assert_if(scaled_point.x == 4.0f && scaled_point.y == 6.0f && scaled_point.z == 8.0f, + "scale * point failed"); + gkit::test::logln(" scale * (2,2,2) = {} (expected: (4,6,8))", vec3_str(scaled_point)); + return true; +} - // Test 12: Rotation X (90 degrees) - std::cout << "\n12. Rotation X (90 degrees):" << '\n'; - auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); - std::cout << mat4_str(rot_x) << '\n'; +auto test_rotation() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== rotation ==="); - // Verify rotation - std::cout << "rot_x * (0,1,0) = " << vec3_str(Matrix4::transform_point(rot_x, {0.0f, 1.0f, 0.0f})) - << " (expected: ~ (0,0,1))" << '\n'; + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + gkit::test::logln(" rot_x (90 degrees):\n{}", mat4_str(rot_x)); + auto px = Matrix4::transform_point(rot_x, {0.0f, 1.0f, 0.0f}); + gkit::test::assert_if(std::abs(px.x) < 1e-4f && std::abs(px.y) < 1e-4f && std::abs(px.z - 1.0f) < 1e-4f, + "rot_x * (0,1,0) must be ~(0,0,1)"); + gkit::test::logln(" rot_x * (0,1,0) = {} (expected: ~ (0,0,1))", vec3_str(px)); - // Test 13: Rotation Y - std::cout << "\n13. Rotation Y (90 degrees):" << '\n'; auto rot_y = Matrix4::rotate_y(gkit::math::PI_32 / 2.0f); - std::cout << mat4_str(rot_y) << '\n'; - std::cout << "rot_y * (1,0,0) = " << vec3_str(Matrix4::transform_point(rot_y, {1.0f, 0.0f, 0.0f})) - << " (expected: ~ (0,0,-1))" << '\n'; + gkit::test::logln(" rot_y (90 degrees):\n{}", mat4_str(rot_y)); + auto py = Matrix4::transform_point(rot_y, {1.0f, 0.0f, 0.0f}); + gkit::test::assert_if(std::abs(py.x) < 1e-4f && std::abs(py.y) < 1e-4f && std::abs(py.z + 1.0f) < 1e-4f, + "rot_y * (1,0,0) must be ~(0,0,-1)"); + gkit::test::logln(" rot_y * (1,0,0) = {} (expected: ~ (0,0,-1))", vec3_str(py)); - // Test 14: Rotation Z - std::cout << "\n14. Rotation Z (90 degrees):" << '\n'; auto rot_z = Matrix4::rotate_z(gkit::math::PI_32 / 2.0f); - std::cout << mat4_str(rot_z) << '\n'; - std::cout << "rot_z * (1,0,0) = " << vec3_str(Matrix4::transform_point(rot_z, {1.0f, 0.0f, 0.0f})) - << " (expected: ~ (0,1,0))" << '\n'; + gkit::test::logln(" rot_z (90 degrees):\n{}", mat4_str(rot_z)); + auto pz = Matrix4::transform_point(rot_z, {1.0f, 0.0f, 0.0f}); + gkit::test::assert_if(std::abs(pz.x) < 1e-4f && std::abs(pz.y - 1.0f) < 1e-4f && std::abs(pz.z) < 1e-4f, + "rot_z * (1,0,0) must be ~(0,1,0)"); + gkit::test::logln(" rot_z * (1,0,0) = {} (expected: ~ (0,1,0))", vec3_str(pz)); + return true; +} + +auto test_axis_rotation() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== rotation around arbitrary axis ==="); - // Test 15: Arbitrary axis rotation - std::cout << "\n15. Rotation around (1,1,1) axis 90 degrees:" << '\n'; auto rot_axis = Matrix4::rotate(gkit::math::PI_32 / 2.0f, Vector3{1.0f, 1.0f, 1.0f}.normalize()); - std::cout << mat4_str(rot_axis) << '\n'; + gkit::test::logln(" rotation around (1,1,1) axis 90 degrees:\n{}", mat4_str(rot_axis)); + gkit::test::assert_if(Matrix4::determinant(rot_axis) > 0.99f && Matrix4::determinant(rot_axis) < 1.01f, + "axis rotation determinant must be 1"); + return true; +} - // Test 16: Transpose - std::cout << "\n16. Transpose test:" << '\n'; +auto test_transpose_and_multiplication() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== transpose and multiplication ==="); + + auto scale = Matrix4::scale({2.0f, 3.0f, 4.0f}); auto transposed = Matrix4::transpose(scale); - std::cout << "transpose(scale):" << '\n'; - std::cout << mat4_str(transposed) << '\n'; + gkit::test::logln(" transpose(scale):\n{}", mat4_str(transposed)); + gkit::test::assert_if(std::abs(transposed.m[0][0] - 2.0f) < 1e-4f && std::abs(transposed.m[1][1] - 3.0f) < 1e-4f && + std::abs(transposed.m[2][2] - 4.0f) < 1e-4f, + "transpose of diagonal must be itself"); + + auto translate = Matrix4::translate({2.0f, 3.0f, 4.0f}); + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + auto trs = translate * rot_x * scale; + gkit::test::logln(" TRS (T * R * S):\n{}", mat4_str(trs)); + return true; +} - // Test 17: Matrix multiplication - std::cout << "\n17. Matrix multiplication (T * R * S):" << '\n'; - auto trs = translate * rot_x * scale; - std::cout << mat4_str(trs) << '\n'; +auto test_inverse() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== inverse ==="); - // Test 18: Inverse of identity - std::cout << "\n18. Inverse test:" << '\n'; + auto identity = Matrix4::identity(); auto inv_identity = Matrix4::inverse(identity); - if (inv_identity.has_value()) { - std::cout << "inverse(identity) exists" << '\n'; - } + gkit::test::assert_if(inv_identity.has_value(), "inverse(identity) must have a value"); + gkit::test::logln(" inverse(identity) exists"); + + auto translate = Matrix4::translate({2.0f, 3.0f, 4.0f}); + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + auto scale = Matrix4::scale({2.0f, 3.0f, 4.0f}); + auto trs = translate * rot_x * scale; - // Test 19: Inverse of TRS - std::cout << "\n19. Inverse of TRS matrix:" << '\n'; auto inv_trs = Matrix4::inverse(trs); - if (inv_trs.has_value()) { - std::cout << "inverse(TRS) exists" << '\n'; - auto composed = trs * inv_trs.value(); - std::cout << "TRS * inverse(TRS):" << '\n'; - std::cout << mat4_str(composed) << '\n'; - } else { - std::cout << "TRS is singular (no inverse)" << '\n'; - } + gkit::test::assert_if(inv_trs.has_value(), "inverse(TRS) must have a value"); + auto composed = trs * inv_trs.value(); + gkit::test::logln(" TRS * inverse(TRS):\n{}", mat4_str(composed)); + gkit::test::assert_if(std::abs(composed.m[0][0] - 1.0f) < 1e-4f && std::abs(composed.m[1][1] - 1.0f) < 1e-4f && + std::abs(composed.m[2][2] - 1.0f) < 1e-4f && std::abs(composed.m[3][3] - 1.0f) < 1e-4f, + "TRS * inverse(TRS) must be identity"); + return true; +} + +auto test_set_identity_and_extract() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== set_identity and extract ==="); - // Test 20: Get/Set identity - std::cout << "\n20. Set identity:" << '\n'; auto mat = Matrix4::zero(); mat.set_identity(); - std::cout << "After set_identity():" << '\n'; - std::cout << mat4_str(mat) << '\n'; + gkit::test::logln(" after set_identity():\n{}", mat4_str(mat)); + gkit::test::assert_if(mat.m[0][0] == 1.0f && mat.m[1][1] == 1.0f && mat.m[2][2] == 1.0f && mat.m[3][3] == 1.0f, + "set_identity failed"); + + auto translate = Matrix4::translate({2.0f, 3.0f, 4.0f}); + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + auto scale = Matrix4::scale({2.0f, 3.0f, 4.0f}); + auto trs = translate * rot_x * scale; - // Test 21: Extract translation - std::cout << "\n21. Extract translation from TRS:" << '\n'; auto extracted_trans = Matrix4::get_translation(trs); - std::cout << "Translation = " << vec3_str(extracted_trans) << " (expected: (2,3,4))" << '\n'; + gkit::test::assert_if(extracted_trans.x == 2.0f && extracted_trans.y == 3.0f && extracted_trans.z == 4.0f, + "get_translation failed"); + gkit::test::logln(" translation = {} (expected: (2,3,4))", vec3_str(extracted_trans)); - // Test 22: Extract rotation - std::cout << "\n22. Extract rotation from TRS:" << '\n'; auto extracted_rot = Matrix4::get_rotation(trs); - std::cout << "Rotation:" << '\n'; - std::cout << mat3_str(extracted_rot) << '\n'; + gkit::test::logln(" rotation:\n{}", mat3_str(extracted_rot)); - // Test 23: Extract scale - std::cout << "\n23. Extract scale from TRS:" << '\n'; auto extracted_scale = Matrix4::get_scale(trs); - std::cout << "Scale = " << vec3_str(extracted_scale) << '\n'; + gkit::test::assert_if(std::abs(extracted_scale.x - 2.0f) < 1e-3f && std::abs(extracted_scale.y - 3.0f) < 1e-3f && + std::abs(extracted_scale.z - 4.0f) < 1e-3f, + "get_scale failed"); + gkit::test::logln(" scale = {}", vec3_str(extracted_scale)); - // Test 24: Decompose - std::cout << "\n24. Decompose TRS:" << '\n'; auto [t, r, s] = Matrix4::decompose(trs); - std::cout << "Translation = " << vec3_str(t) << '\n'; - std::cout << "Scale = " << vec3_str(s) << '\n'; + gkit::test::assert_if(std::abs(t.x - 2.0f) < 1e-3f && std::abs(t.y - 3.0f) < 1e-3f && std::abs(t.z - 4.0f) < 1e-3f, + "decompose translation failed"); + gkit::test::assert_if(std::abs(s.x - 2.0f) < 1e-3f && std::abs(s.y - 3.0f) < 1e-3f && std::abs(s.z - 4.0f) < 1e-3f, + "decompose scale failed"); + gkit::test::logln(" decompose: translation = {}, scale = {}", vec3_str(t), vec3_str(s)); - // Test 25: Compose TRS - std::cout << "\n25. Compose TRS:" << '\n'; auto composed_trs = Matrix4::compose(extracted_trans, extracted_rot, extracted_scale); - std::cout << mat4_str(composed_trs) << '\n'; + gkit::test::logln(" compose(TRS):\n{}", mat4_str(composed_trs)); + return true; +} + +auto test_projection() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== projection ==="); - // Test 26: Perspective projection - std::cout << "\n26. Perspective projection (fov=60deg, aspect=1.778, near=0.1, far=100):" << '\n'; auto persp = Matrix4::perspective(gkit::math::PI_32 / 3.0f, 16.0f / 9.0f, 0.1f, 100.0f); - std::cout << mat4_str(persp) << '\n'; + gkit::test::logln(" perspective:\n{}", mat4_str(persp)); - // Test 27: Orthographic projection - std::cout << "\n27. Orthographic projection (left=-10, right=10, bottom=-10, top=10, near=0.1, far=100):" << '\n'; auto ortho = Matrix4::orthographic(-10.0f, 10.0f, -10.0f, 10.0f, 0.1f, 100.0f); - std::cout << mat4_str(ortho) << '\n'; + gkit::test::logln(" orthographic:\n{}", mat4_str(ortho)); - // Test 28: Look-at - std::cout << "\n28. Look-at (eye=(0,0,5), target=(0,0,0), up=(0,1,0)):" << '\n'; auto look = Matrix4::look_at({0.0f, 0.0f, 5.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}); - std::cout << mat4_str(look) << '\n'; + gkit::test::logln(" look_at:\n{}", mat4_str(look)); + return true; +} - // Test 29: Is affine - std::cout << "\n29. Is affine check:" << '\n'; - std::cout << "identity.is_affine() = " << (identity.is_affine() ? "true" : "false") << " (expected: true)" << '\n'; - std::cout << "persp.is_affine() = " << (persp.is_affine() ? "true" : "false") << " (expected: false)" << '\n'; +auto test_matrix_properties() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== matrix properties ==="); - // Test 30: Is orthonormal - std::cout << "\n30. Is orthonormal check:" << '\n'; - std::cout << "rot_x.is_orthonormal() = " << (rot_x.is_orthonormal() ? "true" : "false") << " (expected: true)" - << '\n'; + auto identity = Matrix4::identity(); + gkit::test::assert_if(identity.is_affine(), "identity must be affine"); + gkit::test::logln(" identity.is_affine() = {} (expected: true)", identity.is_affine()); - // Test 31: Trace - std::cout << "\n31. Trace:" << '\n'; - std::cout << "trace(identity) = " << identity.trace() << " (expected: 4)" << '\n'; + auto persp = Matrix4::perspective(gkit::math::PI_32 / 3.0f, 16.0f / 9.0f, 0.1f, 100.0f); + gkit::test::assert_if(!persp.is_affine(), "perspective must not be affine"); + gkit::test::logln(" persp.is_affine() = {} (expected: false)", persp.is_affine()); + + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + gkit::test::assert_if(rot_x.is_orthonormal(), "rot_x must be orthonormal"); + gkit::test::logln(" rot_x.is_orthonormal() = {} (expected: true)", rot_x.is_orthonormal()); + + gkit::test::assert_if(identity.trace() == 4.0f, "trace(identity) must be 4"); + gkit::test::logln(" trace(identity) = {} (expected: 4)", identity.trace()); + return true; +} + +auto test_from_rows_lerp_shear() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== from_rows, lerp, shear ==="); - // Test 32: From rows/columns - std::cout << "\n32. From rows:" << '\n'; auto from_rows = Matrix4::from_rows( {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 2.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 3.0f, 0.0f}, {4.0f, 5.0f, 6.0f, 1.0f}); - std::cout << mat4_str(from_rows) << '\n'; + gkit::test::logln(" from_rows:\n{}", mat4_str(from_rows)); - // Test 33: Lerp - std::cout << "\n33. Lerp:" << '\n'; auto lerp_result = Matrix4::lerp(Matrix4::zero(), Matrix4::identity(), 0.5f); - std::cout << "lerp(zero, identity, 0.5):" << '\n'; - std::cout << mat4_str(lerp_result) << '\n'; + gkit::test::logln(" lerp(zero, identity, 0.5):\n{}", mat4_str(lerp_result)); + gkit::test::assert_if(std::abs(lerp_result.m[0][0] - 0.5f) < 1e-4f && + std::abs(lerp_result.m[1][1] - 0.5f) < 1e-4f && + std::abs(lerp_result.m[2][2] - 0.5f) < 1e-4f, + "lerp(zero, identity, 0.5) failed"); - // Test 34: Shear - std::cout << "\n34. Shear (1, 2, 3):" << '\n'; auto shear = Matrix4::shear({1.0f, 2.0f, 3.0f}); - std::cout << mat4_str(shear) << '\n'; + gkit::test::logln(" shear (1, 2, 3):\n{}", mat4_str(shear)); + return true; +} + +auto test_accessors() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== accessors and to_string ==="); + + auto identity = Matrix4::identity(); - // Test 35: Row/Column access - std::cout << "\n35. Row/Column access:" << '\n'; auto row0 = identity.row(0); + gkit::test::assert_if(row0.x == 1.0f && row0.y == 0.0f && row0.z == 0.0f && row0.w == 0.0f, + "identity.row(0) failed"); + gkit::test::logln(" identity.row(0) = {} (expected: (1,0,0,0))", vec4_str(row0)); + auto col0 = identity.column(0); - std::cout << "identity.row(0) = " << vec4_str(row0) << " (expected: (1,0,0,0))" << '\n'; - std::cout << "identity.column(0) = " << vec4_str(col0) << " (expected: (1,0,0,0))" << '\n'; + gkit::test::assert_if(col0.x == 1.0f && col0.y == 0.0f && col0.z == 0.0f && col0.w == 0.0f, + "identity.column(0) failed"); + gkit::test::logln(" identity.column(0) = {} (expected: (1,0,0,0))", vec4_str(col0)); - // Test 36: Data pointer - std::cout << "\n36. Data pointer:" << '\n'; const float* data_ptr = identity.data(); - std::cout << "identity.data()[0] = " << data_ptr[0] << " (expected: 1)" << '\n'; - std::cout << "identity.data()[5] = " << data_ptr[5] << " (expected: 1, diagonal element)" << '\n'; + gkit::test::assert_if(data_ptr[0] == 1.0f, "identity.data()[0] must be 1"); + gkit::test::assert_if(data_ptr[5] == 1.0f, "identity.data()[5] must be 1 (diagonal element)"); + gkit::test::logln(" identity.data()[0] = {} (expected: 1)", data_ptr[0]); + gkit::test::logln(" identity.data()[5] = {} (expected: 1)", data_ptr[5]); - // Test 37: To string - std::cout << "\n37. To string:" << '\n'; - std::cout << identity.to_string() << '\n'; + gkit::test::logln(" to_string:\n{}", identity.to_string()); + return true; +} + +auto test_compound_operators() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== compound operators ==="); - // Test 38: Compound operators - std::cout << "\n38. Compound operators:" << '\n'; auto compound = Matrix4::identity(); compound += Matrix4(1.0f); - std::cout << "identity += Matrix4(1):" << '\n'; - std::cout << mat4_str(compound) << '\n'; + gkit::test::logln(" identity += Matrix4(1):\n{}", mat4_str(compound)); + gkit::test::assert_if(compound.m[0][0] == 2.0f && compound.m[1][1] == 2.0f, "operator+= failed"); compound *= 2.0f; - std::cout << "after *= 2:" << '\n'; - std::cout << mat4_str(compound) << '\n'; + gkit::test::logln(" after *= 2:\n{}", mat4_str(compound)); + gkit::test::assert_if(compound.m[0][0] == 4.0f && compound.m[1][1] == 4.0f, "operator*= failed"); + return true; +} + +auto test_rotation_between_and_quaternion() -> bool { + using namespace gkit::math; // NOLINT(google-build-using-namespace + gkit::test::logln("=== rotation between vectors and quaternion ==="); - // Test 39: Rotation between vectors - std::cout << "\n39. Rotation between vectors:" << '\n'; auto rot_between = Matrix4::rotation_between_vectors({1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}); - std::cout << mat4_str(rot_between) << '\n'; - std::cout << "rot_between * (1,0,0) = " << vec3_str(Matrix4::transform_vector(rot_between, {1.0f, 0.0f, 0.0f})) - << '\n'; + gkit::test::logln(" rotation_between:\n{}", mat4_str(rot_between)); + auto rotated = Matrix4::transform_vector(rot_between, {1.0f, 0.0f, 0.0f}); + gkit::test::assert_if(std::abs(rotated.x) < 1e-3f && std::abs(rotated.y - 1.0f) < 1e-3f && + std::abs(rotated.z) < 1e-3f, + "rotation_between_vectors failed"); + gkit::test::logln(" rot_between * (1,0,0) = {}", vec3_str(rotated)); - // Test 40: Get quaternion - std::cout << "\n40. Get quaternion from rotation:" << '\n'; - auto quat = Matrix4::get_quaternion(rot_x); - std::cout << "Quaternion from rot_x(90deg): " << vec4_str(quat) << '\n'; + auto rot_x = Matrix4::rotate_x(gkit::math::PI_32 / 2.0f); + auto quat = Matrix4::get_quaternion(rot_x); + gkit::test::logln(" quaternion from rot_x(90deg): {}", vec4_str(quat)); + return true; +} - std::cout << "\n=== All tests completed! ===" << '\n'; +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_matrix_constructors) + .add_test_func(test_matrix_vector_multiplication) + .add_test_func(test_determinant) + .add_test_func(test_translation_and_scale) + .add_test_func(test_rotation) + .add_test_func(test_axis_rotation) + .add_test_func(test_transpose_and_multiplication) + .add_test_func(test_inverse) + .add_test_func(test_set_identity_and_extract) + .add_test_func(test_projection) + .add_test_func(test_matrix_properties) + .add_test_func(test_from_rows_lerp_shear) + .add_test_func(test_accessors) + .add_test_func(test_compound_operators) + .add_test_func(test_rotation_between_and_quaternion); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/math/test_vector2.cpp b/test/math/test_vector2.cpp index 0af9dac..9b2b2e4 100644 --- a/test/math/test_vector2.cpp +++ b/test/math/test_vector2.cpp @@ -1,10 +1,8 @@ #include "gkit/math/scalar.hpp" #include "gkit/math/vector2.hpp" +#include "test_utils.hpp" -#include #include -#include -#include #include using gkit::math::Vector2; @@ -13,107 +11,140 @@ auto vec_str(const Vector2& vec) -> std::string { return std::format("x = {:.4f}, y = {:.4f}", vec.x, vec.y); } -auto main() -> int { - // Basic constructors +auto test_constructors() -> bool { + gkit::test::logln("=== constructors ==="); + Vector2 vec1(1.0f, 2.0f); - std::cout << "vec1: " << vec_str(vec1) << '\n'; + gkit::test::logln(" vec1: {}", vec_str(vec1)); - // Fill constructor Vector2 vec_fill(5.0f); - assert(vec_fill.x == 5.0f && vec_fill.y == 5.0f); - std::cout << "vec_fill (Vector2(5.0f)): " << vec_str(vec_fill) << '\n'; + gkit::test::assert_if(vec_fill.x == 5.0f && vec_fill.y == 5.0f, "fill constructor failed"); + gkit::test::logln(" vec_fill (Vector2(5.0f)): {}", vec_str(vec_fill)); Vector2 vec2(3.0f, 4.0f); - std::cout << "vec2: " << vec_str(vec2) << '\n'; + gkit::test::logln(" vec2: {}", vec_str(vec2)); + return true; +} + +auto test_arithmetic() -> bool { + gkit::test::logln("=== arithmetic operators ==="); + + Vector2 vec1(1.0f, 2.0f); + Vector2 vec2(3.0f, 4.0f); - // Arithmetic operators Vector2 vec_add = vec1 + vec2; - std::cout << "vec1 + vec2: " << vec_str(vec_add) << '\n'; - assert(vec_add.x == 4.0f && vec_add.y == 6.0f); + gkit::test::assert_if(vec_add.x == 4.0f && vec_add.y == 6.0f, "vec1 + vec2 failed"); + gkit::test::logln(" vec1 + vec2: {}", vec_str(vec_add)); Vector2 vec_sub = vec1 - vec2; - std::cout << "vec1 - vec2: " << vec_str(vec_sub) << '\n'; - assert(vec_sub.x == -2.0f && vec_sub.y == -2.0f); + gkit::test::assert_if(vec_sub.x == -2.0f && vec_sub.y == -2.0f, "vec1 - vec2 failed"); + gkit::test::logln(" vec1 - vec2: {}", vec_str(vec_sub)); Vector2 vec_mul = vec1 * 10.0f; - std::cout << "vec1 * 10: " << vec_str(vec_mul) << '\n'; - assert(vec_mul.x == 10.0f && vec_mul.y == 20.0f); + gkit::test::assert_if(vec_mul.x == 10.0f && vec_mul.y == 20.0f, "vec1 * 10 failed"); + gkit::test::logln(" vec1 * 10: {}", vec_str(vec_mul)); Vector2 vec_div = vec1 / 10.0f; - std::cout << "vec1 / 10: " << vec_str(vec_div) << '\n'; - assert(std::abs(vec_div.x - 0.1f) < gkit::math::EPSILON32 && std::abs(vec_div.y - 0.2f) < gkit::math::EPSILON32); + gkit::test::assert_if(std::abs(vec_div.x - 0.1f) < gkit::math::EPSILON32 && + std::abs(vec_div.y - 0.2f) < gkit::math::EPSILON32, + "vec1 / 10 failed"); + gkit::test::logln(" vec1 / 10: {}", vec_str(vec_div)); + return true; +} - // Length - std::cout << "vec1.length(): " << vec1.length() << '\n'; - assert(std::abs(vec1.length() - std::sqrt(5.0f)) < gkit::math::EPSILON32); +auto test_length_and_normalize() -> bool { + gkit::test::logln("=== length and normalize ==="); - // Length squared - std::cout << "vec1.length_sq(): " << vec1.length_sq() << '\n'; - assert(std::abs(vec1.length_sq() - 5.0f) < gkit::math::EPSILON32); + Vector2 vec1(1.0f, 2.0f); + gkit::test::logln(" vec1.length(): {}", vec1.length()); + gkit::test::assert_if(std::abs(vec1.length() - std::sqrt(5.0f)) < gkit::math::EPSILON32, "vec1.length() failed"); + gkit::test::logln(" vec1.length_sq(): {}", vec1.length_sq()); + gkit::test::assert_if(std::abs(vec1.length_sq() - 5.0f) < gkit::math::EPSILON32, "vec1.length_sq() failed"); - // Normalization vec1 = vec1.normalize(); - std::cout << "vec1 after normalize: " << vec_str(vec1) << '\n'; - assert(std::abs(vec1.length() - 1.0f) < gkit::math::EPSILON32); + gkit::test::logln(" vec1 after normalize: {}", vec_str(vec1)); + gkit::test::assert_if(std::abs(vec1.length() - 1.0f) < gkit::math::EPSILON32, "normalize failed"); + + Vector2 c(3.0f, 4.0f); + Vector2 c_normalized = c.normalize(); + gkit::test::logln(" Vector2.normalize(3,4): {}", vec_str(c_normalized)); + gkit::test::assert_if(std::abs(c_normalized.length() - 1.0f) < gkit::math::EPSILON32, "normalize(3,4) failed"); + return true; +} + +auto test_dot_and_cross() -> bool { + gkit::test::logln("=== dot and cross product ==="); - // Reset for further tests Vector2 a(1.0f, 0.0f); Vector2 b(0.0f, 1.0f); - // Dot product float dot_result = Vector2::dot(a, b); - std::cout << "Vector2::dot(a, b): " << dot_result << '\n'; - assert(dot_result == 0.0f); + gkit::test::assert_if(dot_result == 0.0f, "Vector2::dot(a, b) failed"); + gkit::test::logln(" Vector2::dot(a, b): {}", dot_result); float dot_a = Vector2::dot(a, a); - assert(dot_a == 1.0f); + gkit::test::assert_if(dot_a == 1.0f, "Vector2::dot(a, a) failed"); - // Cross product float cross_result = Vector2::cross(a, b); - std::cout << "Vector2::cross(a, b): " << cross_result << '\n'; - assert(cross_result == 1.0f); + gkit::test::assert_if(cross_result == 1.0f, "Vector2::cross(a, b) failed"); + gkit::test::logln(" Vector2::cross(a, b): {}", cross_result); + return true; +} - // Normalize - Vector2 c(3.0f, 4.0f); - Vector2 c_normalized = c.normalize(); - std::cout << "Vector2.normalize(3,4): " << vec_str(c_normalized) << '\n'; - assert(std::abs(c_normalized.length() - 1.0f) < gkit::math::EPSILON32); +auto test_lerp_min_max() -> bool { + gkit::test::logln("=== lerp, min, max ==="); + + Vector2 a(1.0f, 0.0f); + Vector2 b(0.0f, 1.0f); - // Lerp Vector2 lerp_result = Vector2::lerp(a, b, 0.5f); - std::cout << "Vector2::lerp((1,0), (0,1), 0.5): " << vec_str(lerp_result) << '\n'; - assert(std::abs(lerp_result.x - 0.5f) < gkit::math::EPSILON32); - assert(std::abs(lerp_result.y - 0.5f) < gkit::math::EPSILON32); + gkit::test::assert_if(std::abs(lerp_result.x - 0.5f) < gkit::math::EPSILON32, "lerp.x failed"); + gkit::test::assert_if(std::abs(lerp_result.y - 0.5f) < gkit::math::EPSILON32, "lerp.y failed"); + gkit::test::logln(" Vector2::lerp((1,0), (0,1), 0.5): {}", vec_str(lerp_result)); - // Min / Max Vector2 min_result = Vector2::min(a, b); - std::cout << "Vector2::min((1,0), (0,1)): " << vec_str(min_result) << '\n'; - assert(min_result.x == 0.0f && min_result.y == 0.0f); + gkit::test::assert_if(min_result.x == 0.0f && min_result.y == 0.0f, "Vector2::min failed"); + gkit::test::logln(" Vector2::min((1,0), (0,1)): {}", vec_str(min_result)); Vector2 max_result = Vector2::max(a, b); - std::cout << "Vector2::max((1,0), (0,1)): " << vec_str(max_result) << '\n'; - assert(max_result.x == 1.0f && max_result.y == 1.0f); + gkit::test::assert_if(max_result.x == 1.0f && max_result.y == 1.0f, "Vector2::max failed"); + gkit::test::logln(" Vector2::max((1,0), (0,1)): {}", vec_str(max_result)); + return true; +} + +auto test_perp_reflect_distance() -> bool { + gkit::test::logln("=== perp, reflect, distance ==="); + + Vector2 a(1.0f, 0.0f); - // Perp Vector2 perp_result = Vector2::perp(a); - std::cout << "Vector2::perp((1,0)): " << vec_str(perp_result) << '\n'; - assert(perp_result.x == 0.0f && perp_result.y == 1.0f); + gkit::test::assert_if(perp_result.x == 0.0f && perp_result.y == 1.0f, "Vector2::perp failed"); + gkit::test::logln(" Vector2::perp((1,0)): {}", vec_str(perp_result)); - // Reflect Vector2 v(1.0f, 1.0f); - Vector2 n(0.0f, 1.0f); // Reflect off horizontal surface + Vector2 n(0.0f, 1.0f); Vector2 reflect_result = Vector2::reflect(v, n); - std::cout << "Vector2::reflect((1,1), (0,1)): " << vec_str(reflect_result) << '\n'; - assert(std::abs(reflect_result.x - 1.0f) < gkit::math::EPSILON32); - assert(std::abs(reflect_result.y - (-1.0f)) < gkit::math::EPSILON32); + gkit::test::assert_if(std::abs(reflect_result.x - 1.0f) < gkit::math::EPSILON32, "reflect.x failed"); + gkit::test::assert_if(std::abs(reflect_result.y - (-1.0f)) < gkit::math::EPSILON32, "reflect.y failed"); + gkit::test::logln(" Vector2::reflect((1,1), (0,1)): {}", vec_str(reflect_result)); - // Distance Vector2 p1(0.0f, 0.0f); Vector2 p2(3.0f, 4.0f); float dist = Vector2::distance(p1, p2); - std::cout << "Vector2::distance((0,0), (3,4)): " << dist << '\n'; - assert(std::abs(dist - 5.0f) < gkit::math::EPSILON32); + gkit::test::assert_if(std::abs(dist - 5.0f) < gkit::math::EPSILON32, "Vector2::distance failed"); + gkit::test::logln(" Vector2::distance((0,0), (3,4)): {}", dist); + return true; +} - std::cout << "\nAll tests passed!" << '\n'; +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_constructors) + .add_test_func(test_arithmetic) + .add_test_func(test_length_and_normalize) + .add_test_func(test_dot_and_cross) + .add_test_func(test_lerp_min_max) + .add_test_func(test_perp_reflect_distance); + + test_runner.run(); return 0; -} \ No newline at end of file +} diff --git a/test/math/test_vector3.cpp b/test/math/test_vector3.cpp index 672051c..325bfea 100644 --- a/test/math/test_vector3.cpp +++ b/test/math/test_vector3.cpp @@ -1,43 +1,79 @@ +#include "test_utils.hpp" + #include -#include #include #include using gkit::math::Vector3; -auto vec_str(Vector3& vec) -> std::string { +auto vec_str(const Vector3& vec) -> std::string { auto [x, y, z] = vec.properties(); return std::format("x = {}, y = {}, z = {}", x, y, z); } -auto main() -> int { - // constructure test +auto test_constructors() -> bool { + gkit::test::logln("=== constructors ==="); + Vector3 vec1({1.0f, 2.0f, 3.0f}); - std::cout << "vec1: " << vec_str(vec1) << '\n'; + gkit::test::logln(" vec1: {}", vec_str(vec1)); Vector3 vec2(vec1); - std::cout << "vec2(construct from vec1): " << vec_str(vec2) << '\n'; + gkit::test::logln(" vec2(construct from vec1): {}", vec_str(vec2)); + + auto vec3 = vec1; + gkit::test::logln(" vec3(construct with = from vec1): {}", vec_str(vec3)); + gkit::test::assert_if(vec2.x == 1.0f && vec2.y == 2.0f && vec2.z == 3.0f, "copy constructor failed"); + gkit::test::assert_if(vec3.x == 1.0f && vec3.y == 2.0f && vec3.z == 3.0f, "copy assignment failed"); + return true; +} + +auto test_arithmetic() -> bool { + gkit::test::logln("=== arithmetic operators ==="); + + Vector3 vec1({1.0f, 2.0f, 3.0f}); + auto vec2 = vec1; auto vec3 = vec1; - std::cout << "vec3(construct with = from vec1): " << vec_str(vec3) << '\n'; - // Arithmetic operators test auto vec4 = vec2 + vec3; - std::cout << "vec4(vec2 + vec3): " << vec_str(vec4) << '\n'; + gkit::test::assert_if(vec4.x == 2.0f && vec4.y == 4.0f && vec4.z == 6.0f, "vec2 + vec3 failed"); + gkit::test::logln(" vec4(vec2 + vec3): {}", vec_str(vec4)); auto vec5 = vec2 - vec3; - std::cout << "vec5(vec2 - vec3): " << vec_str(vec5) << '\n'; + gkit::test::assert_if(vec5.x == 0.0f && vec5.y == 0.0f && vec5.z == 0.0f, "vec2 - vec3 failed"); + gkit::test::logln(" vec5(vec2 - vec3): {}", vec_str(vec5)); auto vec6 = vec2 * 10; - std::cout << "vec6(vec2 * 10): " << vec_str(vec6) << '\n'; + gkit::test::assert_if(vec6.x == 10.0f && vec6.y == 20.0f && vec6.z == 30.0f, "vec2 * 10 failed"); + gkit::test::logln(" vec6(vec2 * 10): {}", vec_str(vec6)); auto vec7 = vec2 / 5; - std::cout << "vec7(vec2 / 5): " << vec_str(vec7) << '\n'; + gkit::test::assert_if(vec7.x == 0.2f && vec7.y == 0.4f && vec7.z == 0.6f, "vec2 / 5 failed"); + gkit::test::logln(" vec7(vec2 / 5): {}", vec_str(vec7)); + return true; +} + +auto test_length_and_normalize() -> bool { + gkit::test::logln("=== length and normalize ==="); + + Vector3 vec1({1.0f, 2.0f, 3.0f}); + gkit::test::logln(" vec1.length(): {}", vec1.length()); + gkit::test::assert_if(vec1.length() > 0.0f, "vec1.length() must be positive"); - // other methods - std::cout << "vec1.length(): " << vec1.length() << '\n'; vec1 = vec1.normalize(); - std::cout << "normalize(vec1): " << vec_str(vec1) << '\n'; - std::cout << "vec1.length() after normalize: " << vec1.length() << '\n'; -} \ No newline at end of file + gkit::test::logln(" normalize(vec1): {}", vec_str(vec1)); + gkit::test::logln(" vec1.length() after normalize: {}", vec1.length()); + gkit::test::assert_if(vec1.length() > 0.999f && vec1.length() < 1.001f, "normalize failed"); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_constructors) + .add_test_func(test_arithmetic) + .add_test_func(test_length_and_normalize); + + test_runner.run(); + return 0; +} diff --git a/test/math/test_vector4.cpp b/test/math/test_vector4.cpp index bc00b3f..22a53c6 100644 --- a/test/math/test_vector4.cpp +++ b/test/math/test_vector4.cpp @@ -1,5 +1,6 @@ +#include "test_utils.hpp" + #include -#include #include #include @@ -11,33 +12,70 @@ auto vec_str(const gkit::math::Vector4& vec) -> std::string { return std::format("x = {}, y = {}, z = {}, w = {}", x, y, z, w); } -auto main() -> int { - // constructor test +auto test_constructors() -> bool { + gkit::test::logln("=== constructors ==="); + Vector4 vec1{1.0f, 2.0f, 3.0f, 4.0f}; - std::cout << "vec1: " << vec_str(vec1) << '\n'; + gkit::test::logln(" vec1: {}", vec_str(vec1)); Vector4 vec2(vec1); - std::cout << "vec2(construct from vec1): " << vec_str(vec2) << '\n'; + gkit::test::logln(" vec2(construct from vec1): {}", vec_str(vec2)); + + auto vec3 = vec1; + gkit::test::logln(" vec3(construct with = from vec1): {}", vec_str(vec3)); + gkit::test::assert_if(vec2.x == 1.0f && vec2.y == 2.0f && vec2.z == 3.0f && vec2.w == 4.0f, + "copy constructor failed"); + gkit::test::assert_if(vec3.x == 1.0f && vec3.y == 2.0f && vec3.z == 3.0f && vec3.w == 4.0f, + "copy assignment failed"); + return true; +} + +auto test_arithmetic() -> bool { + gkit::test::logln("=== arithmetic operators ==="); + + Vector4 vec1{1.0f, 2.0f, 3.0f, 4.0f}; + auto vec2 = vec1; auto vec3 = vec1; - std::cout << "vec3(construct with = from vec1): " << vec_str(vec3) << '\n'; - // Arithmetic operators test auto vec4 = vec2 + vec3; - std::cout << "vec4(vec2 + vec3): " << vec_str(vec4) << '\n'; + gkit::test::assert_if(vec4.x == 2.0f && vec4.y == 4.0f && vec4.z == 6.0f && vec4.w == 8.0f, "vec2 + vec3 failed"); + gkit::test::logln(" vec4(vec2 + vec3): {}", vec_str(vec4)); auto vec5 = vec2 - vec3; - std::cout << "vec5(vec2 - vec3): " << vec_str(vec5) << '\n'; + gkit::test::assert_if(vec5.x == 0.0f && vec5.y == 0.0f && vec5.z == 0.0f && vec5.w == 0.0f, "vec2 - vec3 failed"); + gkit::test::logln(" vec5(vec2 - vec3): {}", vec_str(vec5)); auto vec6 = vec2 * 10.0f; - std::cout << "vec6(vec2 * 10): " << vec_str(vec6) << '\n'; + gkit::test::assert_if(vec6.x == 10.0f && vec6.y == 20.0f && vec6.z == 30.0f && vec6.w == 40.0f, "vec2 * 10 failed"); + gkit::test::logln(" vec6(vec2 * 10): {}", vec_str(vec6)); auto vec7 = vec2 / 5.0f; - std::cout << "vec7(vec2 / 5): " << vec_str(vec7) << '\n'; + gkit::test::assert_if(vec7.x == 0.2f && vec7.y == 0.4f && vec7.z == 0.6f && vec7.w == 0.8f, "vec2 / 5 failed"); + gkit::test::logln(" vec7(vec2 / 5): {}", vec_str(vec7)); + return true; +} + +auto test_length_and_normalize() -> bool { + gkit::test::logln("=== length and normalize ==="); + + Vector4 vec1{1.0f, 2.0f, 3.0f, 4.0f}; + gkit::test::logln(" vec1.length(): {}", vec1.length()); + gkit::test::assert_if(vec1.length() > 0.0f, "vec1.length() must be positive"); - // other methods - std::cout << "vec1.length(): " << vec1.length() << '\n'; vec1 = vec1.normalize(); - std::cout << "vec1 after normalize: " << vec_str(vec1) << '\n'; - std::cout << "vec1.length() after normalize: " << vec1.length() << '\n'; -} \ No newline at end of file + gkit::test::logln(" vec1 after normalize: {}", vec_str(vec1)); + gkit::test::logln(" vec1.length() after normalize: {}", vec1.length()); + gkit::test::assert_if(vec1.length() > 0.999f && vec1.length() < 1.001f, "normalize failed"); + return true; +} + +auto main() -> int { + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_constructors) + .add_test_func(test_arithmetic) + .add_test_func(test_length_and_normalize); + + test_runner.run(); + return 0; +} diff --git a/test/resource/test_shader_source.cpp b/test/resource/test_shader_source.cpp index 2cf5b5e..3497998 100644 --- a/test/resource/test_shader_source.cpp +++ b/test/resource/test_shader_source.cpp @@ -1,6 +1,6 @@ -#include +#include "test_utils.hpp" + #include -#include #include #include @@ -19,88 +19,76 @@ namespace { } // anonymous namespace -auto test_load_vertex_and_fragment() -> void { - std::cout << "=== Test: Load Vertex and Fragment Shader ===" << '\n'; +auto test_load_vertex_and_fragment() -> bool { + gkit::test::logln("=== Test: Load Vertex and Fragment Shader ==="); const auto path = resource_dir() / "basic.shader"; auto result = gkit::resource::ResourceLoader::instance().load(path); - assert(result.has_value()); + gkit::test::assert_if(result.has_value(), "load must succeed"); const auto& shader = result.value(); - assert(shader->is_loaded()); + gkit::test::assert_if(shader->is_loaded(), "shader must be loaded"); - // Print raw file content auto full = shader->source(); - std::cout << "\n--- Raw Shader Source ---\n" << full << "--- End Raw Source ---\n" << '\n'; - - // Print per-stage sources - std::cout << "--- Vertex Shader ---\n" - << shader->get_source(gkit::resource::ShaderStage::Vertex) << "--- End Vertex Shader ---\n" - << '\n'; - std::cout << "--- Fragment Shader ---\n" - << shader->get_source(gkit::resource::ShaderStage::Fragment) << "--- End Fragment Shader ---\n" - << '\n'; - - assert(full.find("#shader vertex") != std::string_view::npos); - assert(full.find("#shader fragment") != std::string_view::npos); + gkit::test::assert_if(full.find("#shader vertex") != std::string_view::npos, "source must contain vertex marker"); + gkit::test::assert_if(full.find("#shader fragment") != std::string_view::npos, + "source must contain fragment marker"); + gkit::test::logln(" raw source:\n{}", full); - // Per-stage access - assert(shader->has_stage(gkit::resource::ShaderStage::Vertex)); - assert(shader->has_stage(gkit::resource::ShaderStage::Fragment)); + gkit::test::assert_if(shader->has_stage(gkit::resource::ShaderStage::Vertex), "vertex stage must exist"); + gkit::test::assert_if(shader->has_stage(gkit::resource::ShaderStage::Fragment), "fragment stage must exist"); auto vs = shader->get_source(gkit::resource::ShaderStage::Vertex); - assert(vs.find("gl_Position") != std::string_view::npos); - assert(vs.find("aPos") != std::string_view::npos); + gkit::test::assert_if(vs.find("gl_Position") != std::string_view::npos, "vertex source must contain gl_Position"); + gkit::test::assert_if(vs.find("aPos") != std::string_view::npos, "vertex source must contain aPos"); + gkit::test::logln(" vertex shader:\n{}", vs); auto fs = shader->get_source(gkit::resource::ShaderStage::Fragment); - assert(fs.find("FragColor") != std::string_view::npos); + gkit::test::assert_if(fs.find("FragColor") != std::string_view::npos, "fragment source must contain FragColor"); + gkit::test::logln(" fragment shader:\n{}", fs); - std::cout << " Vertex and fragment stages parsed correctly: OK" << '\n'; - std::cout << "Test passed!" << '\n' << '\n'; + gkit::test::logln(" vertex and fragment stages parsed correctly"); + return true; } -auto test_cache_returns_same_instance() -> void { - std::cout << "=== Test: Cache Returns Same Instance ===" << '\n'; +auto test_cache_returns_same_instance() -> bool { + gkit::test::logln("=== Test: Cache Returns Same Instance ==="); const auto path = resource_dir() / "basic.shader"; auto first = gkit::resource::ResourceLoader::instance().load(path); auto second = gkit::resource::ResourceLoader::instance().load(path); - assert(first.has_value()); - assert(second.has_value()); + gkit::test::assert_if(first.has_value(), "first load must succeed"); + gkit::test::assert_if(second.has_value(), "second load must succeed"); + gkit::test::assert_if(first.value().get() == second.value().get(), + "loading same path twice must return cached instance"); - // Same pointer — cached instance - assert(first.value().get() == second.value().get()); - - std::cout << " Loading same path twice returns cached instance: OK" << '\n'; - std::cout << "Test passed!" << '\n' << '\n'; + gkit::test::logln(" loading same path twice returns cached instance"); + return true; } -auto test_load_missing_file() -> void { - std::cout << "=== Test: Load Missing File ===" << '\n'; +auto test_load_missing_file() -> bool { + gkit::test::logln("=== Test: Load Missing File ==="); auto result = gkit::resource::ResourceLoader::instance().load( resource_dir() / "does_not_exist.shader"); - assert(!result.has_value()); - - std::cout << " Missing file returns nullopt: OK" << '\n'; - std::cout << "Test passed!" << '\n' << '\n'; + gkit::test::assert_if(!result.has_value(), "loading missing file must return nullopt"); + gkit::test::logln(" missing file returns nullopt"); + return true; } auto main() -> int { - std::cout << "========================================" << '\n'; - std::cout << " gkit::resource::ShaderSource Tests " << '\n'; - std::cout << "========================================" << '\n' << '\n'; - - test_load_vertex_and_fragment(); - test_cache_returns_same_instance(); - test_load_missing_file(); + gkit::test::logln("========================================"); + gkit::test::logln(" gkit::resource::ShaderSource Tests "); + gkit::test::logln("========================================"); - std::cout << "========================================" << '\n'; - std::cout << " ALL TESTS PASSED! " << '\n'; - std::cout << "========================================" << '\n'; + auto test_runner = gkit::test::TestRunner() + .add_test_func(test_load_vertex_and_fragment) + .add_test_func(test_cache_returns_same_instance) + .add_test_func(test_load_missing_file); + test_runner.run(); return 0; }