diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..c98e9a02 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: 🛠️ CI + +on: [pull_request] + +jobs: + clang-format: + uses: ./.github/workflows/clang-format.yml + secrets: inherit + clang-tidy: + uses: ./.github/workflows/clang-tidy.yml + secrets: inherit + windows: + uses: ./.github/workflows/windows.yml + secrets: inherit + linux: + uses: ./.github/workflows/linux.yml + secrets: inherit + mac-armv8: + uses: ./.github/workflows/mac.yml + secrets: inherit + \ No newline at end of file diff --git a/.github/workflows/clang-format-deploy.yml b/.github/workflows/clang-format.yml similarity index 93% rename from .github/workflows/clang-format-deploy.yml rename to .github/workflows/clang-format.yml index 3b2d945e..ce7de70f 100644 --- a/.github/workflows/clang-format-deploy.yml +++ b/.github/workflows/clang-format.yml @@ -1,5 +1,6 @@ -name: clang-format Check -on: [pull_request] +name: Clang Formatting Check +on: [workflow_call] + jobs: formatting-check: name: Formatting Check diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index e971a69d..f823a018 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -1,25 +1,33 @@ -name: C++ Linter +name: cpp-linter + +on: [workflow_call] -on: [pull_request] jobs: - linux-build: - name: "Linter" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + cpp-linter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - uses: cpp-linter/cpp-linter-action@main - id: linter - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - style: '.clang-format' # Use .clang-format config file - version: 19 # Using clang-tidy19 - tidy-checks: '.clang-tidy' # Use .clang-tidy config file - # only 'update' a single comment in a pull request thread. - thread-comments: ${{ github.event_name == 'pull_request' && 'update' }} + - uses: cpp-linter/cpp-linter-action@v2 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + style: '' # Disables clang-format so it strictly runs clang-tidy + # tidy-checks: '.clang-tidy' + extensions: 'cpp,cppm,h,hpp' # common C++ file extensions + # files-changed-only: true + thread-comments: ${{ github.event_name == 'pull_request' && 'update' }} + # Ensure clang-tidy knows how to parse modern C++ headers/modules without a compile_commands.json + extra-args: '-Wall -Wextra -Werror -std=c++23 --config-file=.clang-tidy' - - name: Fail fast?! - if: steps.linter.outputs.checks-failed > 0 - run: exit 1 + - name: Failed fast?! + # Does a check if any of the given clang-tidy checks failed! + if: steps.linter.outputs.checks-failed != '0' + run: | + echo "Failed clang-tidy linters check" + echo "Total violations reported: ${{ steps.linter.outputs.checks-failed }}" + exit 1 \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f139a319..bb2ad93f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,10 +1,9 @@ -name: Linux Platform Build +name: Linux -on: [pull_request] +on: [workflow_call] jobs: - linux-build: - name: "Linux-Build" + ubuntu: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 2c52c1f0..276b90c7 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -1,10 +1,9 @@ -name: Macos Platform Build +name: MacOS -on: [pull_request] +on: [workflow_call] jobs: - macos-build: - name: "M1 Mac Build" + armv8: runs-on: macos-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3f7701c4..8b6d79c0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,10 +1,9 @@ -name: Windows Platform Build +name: Windows -on: [pull_request, workflow_dispatch] +on: [workflow_call] jobs: - windows-build: - name: "Windows-Build" + x86_64: runs-on: windows-latest steps: - uses: actions/checkout@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdc62dd..faa95f62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,17 +54,27 @@ add_library(${PROJECT_NAME} STATIC) # add_subdirectory(editor) if(${ENABLE_TESTS_ONLY}) - build_unit_test( - TEST_SOURCES + add_executable( + unit_test tests/main.test.cpp - tests/basic_add.test.cpp - tests/entity_component_system.test.cpp - tests/math.test.cpp - tests/scene.test.cpp + ) - LINK_PACKAGES - atlas + find_package(ut REQUIRED) + target_link_libraries(unit_test PRIVATE boost-ext-ut::ut atlas) + + target_sources(unit_test PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES + tests/tests.cppm + tests/basic_add.test.cppm + tests/entity_component_system.test.cppm + tests/math.test.cppm + tests/scene.test.cppm ) + + add_custom_target(run_tests ALL DEPENDS unit_test COMMAND unit_test) + endif() set_packages( @@ -200,8 +210,6 @@ target_sources(${PROJECT_NAME} PUBLIC atlas/drivers/jolt_cpp/context.cppm # drivers - # atlas/drivers/window_context.cppm - # atlas/drivers/vulkan/vulkan.cppm atlas/drivers/vulkan/utilities.cppm atlas/drivers/vulkan/instance_context.cppm atlas/drivers/vulkan/window_context.cppm @@ -219,7 +227,6 @@ target_sources(${PROJECT_NAME} PUBLIC target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) -# install(TARGETS ${PROJECT_NAME}) install( TARGETS ${PROJECT_NAME} EXPORT atlas_targets diff --git a/README.md b/README.md index abaee0cc..53cab8ec 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # The Atlas Engine -[![✅CI](https://github.com/engine3d-dev/TheAtlasEngine/actions/workflows/clang-format-deploy.yml/badge.svg)](https://github.com/engine3d-dev/TheAtlasEngine/actions/workflows/clang-format-deploy.yml) +[![✅CI](https://github.com/engine3d-dev/TheAtlasEngine/actions/workflows/ci.yml/badge.svg)](https://github.com/engine3d-dev/TheAtlasEngine/actions/workflows/ci.yml) [![GitHub stars](https://img.shields.io/github/stars/engine3d-dev/TheAtlasEngine.svg)](https://github.com/engine3d-dev/TheAtlasEngine/stargazers) [![GitHub forks](https://img.shields.io/github/forks/engine3d-dev/TheAtlasEngine.svg)](https://github.com/engine3d-dev/TheAtlasEngine/network) [![GitHub issues](https://img.shields.io/github/issues/engine3d-dev/TheAtlasEngine.svg)](https://github.com/engine3d-dev/TheAtlasEngine/issues) diff --git a/atlas/core/entry_point/main.cpp b/atlas/core/entry_point/main.cpp index 0cbc1166..0725c701 100644 --- a/atlas/core/entry_point/main.cpp +++ b/atlas/core/entry_point/main.cpp @@ -15,8 +15,9 @@ import atlas.core.event; int main() { + // We should not have our core system start up during testing environment + // execution atlas::console_log_manager manager = atlas::console_log_manager(); - if (!glfwInit()) { console_log_fatal("GLFW: Initialization failed!!"); return -1; diff --git a/conanfile.py b/conanfile.py index ee5fcc80..c4b66b59 100644 --- a/conanfile.py +++ b/conanfile.py @@ -46,7 +46,9 @@ def requirements(self): self.requires("nfd/3.0") self.requires("watcher/0.12.0") - self.requires("boost-ext-ut/2.3.1") + + if self.options.enable_tests_only: + self.requires("boost-ext-ut/2.3.1") def config_options(self): if self.settings.os == "Windows": diff --git a/tests/basic_add.test.cpp b/tests/basic_add.test.cpp deleted file mode 100644 index ce3c9a62..00000000 --- a/tests/basic_add.test.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -boost::ut::suite<"basic_add"> add_test = []() { - using namespace boost::ut; - - "unit1"_test = []() { - int x = 10; - int y = 30; - int z = (x + y); - - expect(z == 40); - }; -}; diff --git a/tests/basic_add.test.cppm b/tests/basic_add.test.cppm new file mode 100644 index 00000000..f3f7b709 --- /dev/null +++ b/tests/basic_add.test.cppm @@ -0,0 +1,18 @@ +module; +#include +export module atlas.tests:basic_add; + +export void +test_basic_add() { + boost::ut::suite<"basic_add"> add_test = []() { + using namespace boost::ut; + + "unit1"_test = []() { + int x = 10; + int y = 30; + int z = (x + y); + + expect(z == 40); + }; + }; +} \ No newline at end of file diff --git a/tests/entity_component_system.test.cpp b/tests/entity_component_system.test.cppm similarity index 73% rename from tests/entity_component_system.test.cpp rename to tests/entity_component_system.test.cppm index 49883113..14a75636 100644 --- a/tests/entity_component_system.test.cpp +++ b/tests/entity_component_system.test.cppm @@ -1,42 +1,19 @@ +module; #include -// #include #include #include #include -// #include +#include +export module atlas.tests:entity_component_system; + import atlas.core.scene; import atlas.core.scene.game_object; import atlas.core.math.types; import atlas.core.event; - -#include +import atlas.drivers.jolt_cpp.types; namespace atlas { - - template<> - struct vector3 { - vector3() = default; - - vector3(const JPH::Vec3& p_other) { - m_value = { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - operator glm::vec3() { return m_value; } - - glm::vec3 operator=(const JPH::Vec3& p_other) { - return { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - bool operator==(const glm::vec3& p_other) { - return (m_value.x == p_other.x and m_value.y == p_other.y and - m_value.z == p_other.z); - } - - private: - glm::vec3 m_value; - }; - /** * @name Mock Projectile Missle Test * @note Essentially an active component @@ -82,7 +59,10 @@ namespace atlas { struct test_velocity { glm::vec3 position; }; +}; +export void +test_entity_component_system() { boost::ut::suite<"ecs::component"> ecs_test = []() { using namespace boost::ut; @@ -97,30 +77,30 @@ namespace atlas { // expect(entity.is_alive()); - entity.add(); - expect(entity.has()); + entity.add(); + expect(entity.has()); }; "create_entity::get"_test = [&test_scene]() { atlas::game_object entity = test_scene.entity("Mock Entity 2"); - entity.add(); + entity.add(); // flecs requires reading only operations are through the get API // to write or set new parameters you can use get_mut or // set(T&&); in this case, I use set in this test case - entity.set({ .tag = "New Entity" }); + entity.set({ .tag = "New Entity" }); - const test_tag_component* get_tag = - entity.get(); + const atlas::test_tag_component* get_tag = + entity.get(); expect(get_tag->tag == "New Entity"); }; "create_entity::set"_test = [&test_scene]() { atlas::game_object entity = test_scene.entity("New Entity"); - mock_projectile projectile; + atlas::mock_projectile projectile; projectile.on_update(); - entity.set(projectile); + entity.set(projectile); - expect(entity.has()); + expect(entity.has()); // test_transform transform; // transform.position = projectile.position(); @@ -134,4 +114,4 @@ namespace atlas { // entity.get().position); }; }; -}; // namespace atlas +} \ No newline at end of file diff --git a/tests/main.test.cpp b/tests/main.test.cpp index 0cb6cfef..acf6af67 100644 --- a/tests/main.test.cpp +++ b/tests/main.test.cpp @@ -1,5 +1,8 @@ +#include -namespace engine3d {}; +import atlas.tests; int -main() {} \ No newline at end of file +main() { + initialize_tests(); +} \ No newline at end of file diff --git a/tests/math.test.cpp b/tests/math.test.cpp deleted file mode 100644 index bb5f109b..00000000 --- a/tests/math.test.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include -import atlas.core.math.types; - -namespace atlas { - /** - * @name Specialized template - * @note What this does is customizing to utilize a different mathematical - * data type - * @note We are not defining our own vector and the operations of how that - * is done - * @note We simply specialized the type we expect, then do all of that - * operation with that specified type - * - then we simply return to the default glm type once we are done - * with those computation - */ - template<> - struct vector3 { - vector3() = default; - - vector3(const JPH::Vec3& p_other) { - m_value = { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - operator glm::vec3() { return m_value; } - - glm::vec3 operator=(const JPH::Vec3& p_other) { - return { p_other.GetX(), p_other.GetY(), p_other.GetZ() }; - } - - bool operator==(const glm::vec3& p_other) { - return (m_value.x == p_other.x and m_value.y == p_other.y and - m_value.z == p_other.z); - } - - private: - glm::vec3 m_value; - }; -}; // namespace atlas - -/** - * @name MockTestTransform - * @note This is testing if even though our computation is done using jolt's - * mathematical data types - * @note We can still return vec3 - * @note Only down-side that I can see being a problem is users need to define - * specific operator overloads. - * @note TODO: Just need to add a way for getting some of these types inferenced - * (at least to the types that are customized by user) - * - Just so users are not having to pre-define them - * @note Minimal example if we have a projectile that requires physics that has - * some collider with it and Jolt requires you to provide a vec3 and users end - * up defining how projectiles work and prefer Jolt's vec3. Then they define - * that how they prefer and just convert it to a glm::vec3 - * @note Looking at the example below. I just test to see if we can actually do - * addition, and basic arithmetic as a minimal example - * @note We just check if the conversion to glm is happening correctly, checking - * for type conversion validation - */ -class mock_test_projectile { -public: - mock_test_projectile() = default; - - [[nodiscard]] glm::vec3 get_position() const { - return atlas::vector3(m_position); - } - - void on_update() { - m_position = { 0, 1, 2 }; - JPH::Vec3 my_value2 = { 1, 2, 3 }; // simulating some data - m_position += my_value2; - } - -private: - JPH::Vec3 m_position; -}; - -/** - * @name atlas::custom_vec3 test suite - * @note Tests for customizable data types that can be defined by the user. - * @note This considers that if users want to utilize a different math library - * then they can override the computation to be done with math library of their - * choice - * @note All that is required is that they just need to convert to glm::vec2, - * vec3, or vec4 and other glm types - */ - -boost::ut::suite<"::basic_vec3"> original_vec3_test = []() { - using namespace boost::ut; - - "basic_vec3_unit1_test"_test = []() { - glm::vec3 original_val = { 1, 2, 3 }; - // atlas::vec3 unit1_test = {1, 2, 3}; - atlas::vec3 unit1 = { { 1.f, 2.f, 3.f } }; - - expect(original_val == unit1); - }; -}; - -boost::ut::suite<"::custom_vec3"> vec3_test = []() { - using namespace boost::ut; - - "custom_vec3"_test = []() { - JPH::Vec3 my_value = { 0, 1, 2 }; - - atlas::vector3 vec3_unit = my_value; - glm::vec3 expected = { 0, 1, 2 }; - - expect(expected == vec3_unit); - }; - - "custom_vec3_add"_test = []() { - JPH::Vec3 my_value = { 0, 1, 2 }; - JPH::Vec3 my_value2 = { 1, 2, 3 }; - - atlas::vector3 vec3_unit = my_value; - atlas::vector3 vec3_unit2 = (my_value + my_value2); - - auto vec3_unit2_result = vec3_unit2; - - glm::vec3 expected = { 0, 1, 2 }; - - glm::vec3 expected_unit2 = { 1, 3, 5 }; - - expect(expected == vec3_unit); - expect(expected_unit2 == vec3_unit2_result); - }; - - "custom_vec3_mock_class"_test = []() { - mock_test_projectile mock; - mock.on_update(); - - glm::vec3 expected_unit2 = { 1, 3, 5 }; - - expect(expected_unit2 == mock.get_position()); - }; -}; \ No newline at end of file diff --git a/tests/math.test.cppm b/tests/math.test.cppm new file mode 100644 index 00000000..4ec9c3f3 --- /dev/null +++ b/tests/math.test.cppm @@ -0,0 +1,110 @@ +module; +#include +#include +#include +#include +export module atlas.tests:math; + +import atlas.core.math.types; +import atlas.drivers.jolt_cpp.types; + +/** + * @name MockTestTransform + * @note This is testing if even though our computation is done using jolt's + * mathematical data types + * @note We can still return vec3 + * @note Only down-side that I can see being a problem is users need to define + * specific operator overloads. + * @note TODO: Just need to add a way for getting some of these types inferenced + * (at least to the types that are customized by user) + * - Just so users are not having to pre-define them + * @note Minimal example if we have a projectile that requires physics that has + * some collider with it and Jolt requires you to provide a vec3 and users end + * up defining how projectiles work and prefer Jolt's vec3. Then they define + * that how they prefer and just convert it to a glm::vec3 + * @note Looking at the example below. I just test to see if we can actually do + * addition, and basic arithmetic as a minimal example + * @note We just check if the conversion to glm is happening correctly, checking + * for type conversion validation + */ +class mock_test_projectile { +public: + mock_test_projectile() = default; + + [[nodiscard]] glm::vec3 get_position() const { + return atlas::vector3(m_position); + } + + void on_update() { + m_position = { 0, 1, 2 }; + JPH::Vec3 my_value2 = { 1, 2, 3 }; // simulating some data + m_position += my_value2; + } + +private: + JPH::Vec3 m_position; +}; + +/** + * @name atlas::custom_vec3 test suite + * @note Tests for customizable data types that can be defined by the user. + * @note This considers that if users want to utilize a different math library + * then they can override the computation to be done with math library of their + * choice + * @note All that is required is that they just need to convert to glm::vec2, + * vec3, or vec4 and other glm types + */ + +export void +math_test() { + boost::ut::suite<"::basic_vec3"> original_vec3_test = []() { + using namespace boost::ut; + + "basic_vec3_unit1_test"_test = []() { + glm::vec3 original_val = { 1, 2, 3 }; + // atlas::vec3 unit1_test = {1, 2, 3}; + atlas::vec3 unit1 = { { 1.f, 2.f, 3.f } }; + + expect(original_val == unit1); + }; + }; + + boost::ut::suite<"::custom_vec3"> vec3_test = []() { + using namespace boost::ut; + + "custom_vec3"_test = []() { + JPH::Vec3 my_value = { 0, 1, 2 }; + + atlas::vector3 vec3_unit = my_value; + glm::vec3 expected = { 0, 1, 2 }; + + expect(expected == vec3_unit); + }; + + "custom_vec3_add"_test = []() { + JPH::Vec3 my_value = { 0, 1, 2 }; + JPH::Vec3 my_value2 = { 1, 2, 3 }; + + atlas::vector3 vec3_unit = my_value; + atlas::vector3 vec3_unit2 = (my_value + my_value2); + + auto vec3_unit2_result = vec3_unit2; + + glm::vec3 expected = { 0, 1, 2 }; + + glm::vec3 expected_unit2 = { 1, 3, 5 }; + + expect(expected == vec3_unit); + expect(expected_unit2 == vec3_unit2_result); + }; + + "custom_vec3_mock_class"_test = []() { + mock_test_projectile mock; + mock.on_update(); + + glm::vec3 expected_unit2 = { 1, 3, 5 }; + + expect(expected_unit2 == mock.get_position()); + }; + }; +} \ No newline at end of file diff --git a/tests/scene.test.cpp b/tests/scene.test.cpp deleted file mode 100644 index c5eb1ea0..00000000 --- a/tests/scene.test.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -import atlas.core.scene; -import atlas.core.scene.game_object; -import atlas.core.scene.components; -import atlas.core.event; - -boost::ut::suite<"::scene"> scene_test = []() { - using namespace boost::ut; - atlas::event::bus test_event_bus; - atlas::scene test_scene = atlas::scene("Mock Scene 1", test_event_bus); - - "create_object"_test = [&test_scene]() { - atlas::game_object test_object = test_scene.entity("Entity 1"); - test_object.add(); - - expect(test_object.has()); - }; -}; diff --git a/tests/scene.test.cppm b/tests/scene.test.cppm new file mode 100644 index 00000000..17092db4 --- /dev/null +++ b/tests/scene.test.cppm @@ -0,0 +1,24 @@ +module; +#include +export module atlas.tests:scene; + +import atlas.core.scene; +import atlas.core.scene.game_object; +import atlas.core.scene.components; +import atlas.core.event; + +export void +test_scenes() { + boost::ut::suite<"::scene"> scene_test = []() { + using namespace boost::ut; + atlas::event::bus test_event_bus; + atlas::scene test_scene = atlas::scene("Mock Scene 1", test_event_bus); + + "create_object"_test = [&test_scene]() { + atlas::game_object test_object = test_scene.entity("Entity 1"); + test_object.add(); + + expect(test_object.has()); + }; + }; +}; \ No newline at end of file diff --git a/tests/tests.cppm b/tests/tests.cppm new file mode 100644 index 00000000..04616328 --- /dev/null +++ b/tests/tests.cppm @@ -0,0 +1,16 @@ +module; + +export module atlas.tests; + +import :basic_add; +import :entity_component_system; +import :scene; +import :math; + +export void +initialize_tests() { + test_basic_add(); + math_test(); + test_entity_component_system(); + test_scenes(); +} \ No newline at end of file