diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/CMakeLists.txt | 26 | ||||
-rw-r--r-- | src/test/CollisionTest.cpp | 347 | ||||
-rw-r--r-- | src/test/Profiling.cpp | 151 | ||||
-rw-r--r-- | src/test/main.cpp | 2 |
4 files changed, 513 insertions, 13 deletions
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d310f6a..68fa01c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,15 +1,17 @@ target_sources(test_main PUBLIC + # CollisionTest.cpp main.cpp - PhysicsTest.cpp - ScriptTest.cpp - ParticleTest.cpp - AssetTest.cpp - OptionalRefTest.cpp - RenderSystemTest.cpp - EventTest.cpp - ECSTest.cpp - SceneManagerTest.cpp - ValueBrokerTest.cpp - DBTest.cpp - Vector2Test.cpp + # PhysicsTest.cpp + # ScriptTest.cpp + # ParticleTest.cpp + # AssetTest.cpp + # OptionalRefTest.cpp + # RenderSystemTest.cpp + # EventTest.cpp + # ECSTest.cpp + # SceneManagerTest.cpp + # ValueBrokerTest.cpp + # DBTest.cpp + # Vector2Test.cpp + Profiling.cpp ) diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp new file mode 100644 index 0000000..245cced --- /dev/null +++ b/src/test/CollisionTest.cpp @@ -0,0 +1,347 @@ +#include <cmath> +#include <cstddef> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/ComponentManager.h> +#include <crepe/api/Event.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/system/CollisionSystem.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/types.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class CollisionHandler : public Script { +public: + int box_id; + function<void(const CollisionEvent& ev)> test_fn = [](const CollisionEvent & ev) { }; + + CollisionHandler(int box_id) { + this->box_id = box_id; + } + + bool on_collision(const CollisionEvent& ev) { + //Log::logf("Box {} script on_collision()", box_id); + test_fn(ev); + return true; + } + + void init() { + subscribe<CollisionEvent>([this](const CollisionEvent& ev) -> bool { + return this->on_collision(ev); + }); + } + void update() { + // Retrieve component from the same GameObject this script is on + } +}; + +class CollisionTest : public Test { +public: + ComponentManager mgr; + CollisionSystem collision_sys{mgr}; + ScriptSystem script_sys{mgr}; + + GameObject world = mgr.new_object("world","",{50,50}); + GameObject game_object1 = mgr.new_object("object1", "", { 50, 50}); + GameObject game_object2 = mgr.new_object("object2", "", { 50, 30}); + + CollisionHandler * script_object1_ref = nullptr; + CollisionHandler * script_object2_ref = nullptr; + + void SetUp() override { + world.add_component<Rigidbody>(Rigidbody::Data{ + // TODO: remove unrelated properties: + .body_type = Rigidbody::BodyType::STATIC, + .bounce = false, + .offset = {0,0}, + }); + // Create a box with an inner size of 10x10 units + world.add_component<BoxCollider>(vec2{0, -100}, 100, 100); // Top + world.add_component<BoxCollider>(vec2{0, 100}, 100, 100); // Bottom + world.add_component<BoxCollider>(vec2{-100, 0}, 100, 100); // Left + world.add_component<BoxCollider>(vec2{100, 0}, 100, 100); // right + + game_object1.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 1, + .gravity_scale = 0.01, + .body_type = Rigidbody::BodyType::DYNAMIC, + .linear_velocity = {0,0}, + .constraints = {0, 0, 0}, + .use_gravity = true, + .bounce = true, + .elastisity = 1, + .offset = {0,0}, + }); + game_object1.add_component<BoxCollider>(vec2{0, 0}, 10, 10); + BehaviorScript & script_object1 = game_object1.add_component<BehaviorScript>().set_script<CollisionHandler>(1); + script_object1_ref = static_cast<CollisionHandler*>(script_object1.script.get()); + ASSERT_NE(script_object1_ref, nullptr); + + game_object2.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 1, + .gravity_scale = 0.01, + .body_type = Rigidbody::BodyType::DYNAMIC, + .linear_velocity = {0,0}, + .constraints = {0, 0, 0}, + .use_gravity = true, + .bounce = true, + .elastisity = 1, + .offset = {0,0}, + }); + game_object2.add_component<BoxCollider>(vec2{0, 0}, 10, 10); + BehaviorScript & script_object2 = game_object2.add_component<BehaviorScript>().set_script<CollisionHandler>(2); + script_object2_ref = static_cast<CollisionHandler*>(script_object2.script.get()); + ASSERT_NE(script_object2_ref, nullptr); + + // Ensure Script::init() is called on all BehaviorScript instances + script_sys.update(); + } +}; + +TEST_F(CollisionTest, collision_example) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + }; + EXPECT_FALSE(collision_happend); + collision_sys.update(); + EXPECT_FALSE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_both_no_velocity) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,30}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, -5); + EXPECT_EQ(ev.info.move_back_value.y, 0); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 5); + EXPECT_EQ(ev.info.move_back_value.y, 0); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {45,30}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, 0); + EXPECT_EQ(ev.info.move_back_value.y, -5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 0); + EXPECT_EQ(ev.info.move_back_value.y, 5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,25}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_both) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,30}; + Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); + rg1.data.linear_velocity = {10,10}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.linear_velocity = {10,10}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, -5); + EXPECT_EQ(ev.info.move_back_value.y, -5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 5); + EXPECT_EQ(ev.info.move_back_value.y, 5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {45,30}; + Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); + rg1.data.linear_velocity = {10,10}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.linear_velocity = {10,10}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, -5); + EXPECT_EQ(ev.info.move_back_value.y, -5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 2); + EXPECT_EQ(ev.info.move_back_value.x, 5); + EXPECT_EQ(ev.info.move_back_value.y, 5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,25}; + Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); + rg1.data.linear_velocity = {10,10}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.linear_velocity = {10,10}; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + + +TEST_F(CollisionTest, collision_box_box_static_both) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + // is static should not be called + FAIL(); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,30}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_x_direction) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, -5); + EXPECT_EQ(ev.info.move_back_value.y, -5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + // is static should not be called + FAIL(); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {45,30}; + Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); + rg1.data.linear_velocity = {10,10}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_y_direction) { + bool collision_happend = false; + script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, -5); + EXPECT_EQ(ev.info.move_back_value.y, -5); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + // is static should not be called + FAIL(); + }; + EXPECT_FALSE(collision_happend); + Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); + tf.position = {50,25}; + Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); + rg1.data.linear_velocity = {10,10}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp new file mode 100644 index 0000000..d7a4f2e --- /dev/null +++ b/src/test/Profiling.cpp @@ -0,0 +1,151 @@ +#include "system/ParticleSystem.h" +#include "system/PhysicsSystem.h" +#include "system/RenderSystem.h" +#include <cmath> +#include <chrono> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/ComponentManager.h> +#include <crepe/api/Event.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/system/CollisionSystem.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/types.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class TestScript : public Script { + bool oncollision(const CollisionEvent& test) { + Log::logf("Box {} script on_collision()", test.info.first.collider.game_object_id); + return true; + } + void init() { + subscribe<CollisionEvent>([this](const CollisionEvent& ev) -> bool { + return this->oncollision(ev); + }); + } + void update() { + // Retrieve component from the same GameObject this script is on + } +}; + +class Profiling : public Test { +public: + ComponentManager mgr; + // Add system used for profling tests + CollisionSystem collision_sys{mgr}; + PhysicsSystem physics_sys{mgr}; + ParticleSystem particle_sys{mgr}; + RenderSystem render_sys{mgr}; + ScriptSystem script_sys{mgr}; + + // Store individual function timings + std::map<std::string, long long> timings; + + // Min and max gameobject that should and can be created + int min_gameobject_count = 100; + int max_gameobject_count = 1000; + + void SetUp() override { + + GameObject do_not_use = mgr.new_object("DO_NOT_USE","",{0,0}); + do_not_use.add_component<Camera>(Color::WHITE); + // initialize systems here: + //calls init + script_sys.update(); + //creates window + render_sys.update(); + } + + // Helper function to time an update call and store its duration + template <typename Func> + long long time_function(const std::string& name, Func&& func) { + auto start = std::chrono::steady_clock::now(); + func(); + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); + timings[name] = duration; // Store the duration in microseconds + return duration; // Return the duration in microseconds + } + + // Run and profile all systems, return the total time in milliseconds + long long run_all_systems() { + long long total_microseconds = 0; + total_microseconds += time_function("PhysicsSystem", [&]() { physics_sys.update(); }); + total_microseconds += time_function("CollisionSystem", [&]() { collision_sys.update(); }); + total_microseconds += time_function("ParticleSystem", [&]() { particle_sys.update(); }); + total_microseconds += time_function("RenderSystem", [&]() { render_sys.update(); }); + return total_microseconds; + } + + // Print timings of all functions + void log_timings(long long total_time,int game_object_count) const { + std::stringstream ss; + ss << std::endl <<"Function timings:\n"; // Starting with a header + for (const auto& [name, duration] : timings) { + ss << name << " took " << duration / 1000.0 << " ms (" << duration << " µs). " << std::endl; + } + ss << "Total time: " << total_time / 1000.0 << "ms (" << total_time << " µs)" << std::endl; + ss << "Amount of gameobjects: " << game_object_count << std::endl; + // Use GTest INFO macro to print the accumulated log without extra newlines + GTEST_LOG_(INFO) << ss.str(); + } +}; + +TEST_F(Profiling, Profiling_example) { + int game_object_count = 0; + long long total_time = 0; + while (total_time < 16000) { + + { + //define gameobject used for testing + GameObject gameobject = mgr.new_object("gameobject","",{0,0}); + } + + game_object_count++; + total_time = run_all_systems(); + if(game_object_count >= max_gameobject_count) break; + } + log_timings(total_time,game_object_count); + EXPECT_GE(game_object_count, min_gameobject_count); +} + +TEST_F(Profiling, Profiling_small_object_no_collision) { + int game_object_count = 0; + long long total_time = 0; + while (total_time < 16000) { + + { + //define gameobject used for testing + GameObject gameobject = mgr.new_object("gameobject","",{static_cast<float>(game_object_count*2),0}); + gameobject.add_component<Rigidbody>(Rigidbody::Data{ + .body_type = Rigidbody::BodyType::STATIC, + .use_gravity = false, + }); + gameobject.add_component<BoxCollider>(vec2{0, 0}, 1, 1); + gameobject.add_component<BehaviorScript>().set_script<TestScript>(); + Color color(0, 0, 0, 0); + gameobject.add_component<Sprite>( + make_shared<Texture>("/home/jaro/crepe/asset/texture/green_square.png"), color, + FlipSettings{true, true}); + } + + render_sys.update(); + game_object_count++; + total_time = run_all_systems(); + if(game_object_count >= max_gameobject_count) break; + } + log_timings(total_time,game_object_count); + EXPECT_GE(game_object_count, min_gameobject_count); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 241015d..ec293a6 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -9,7 +9,7 @@ int main(int argc, char ** argv) { InitGoogleTest(&argc, argv); auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::ERROR; + cfg.log.level = Log::Level::DEBUG; return RUN_ALL_TESTS(); } |