diff options
Diffstat (limited to 'src/test')
27 files changed, 2966 insertions, 437 deletions
diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp index 8aa7629..f41e9de 100644 --- a/src/test/AssetTest.cpp +++ b/src/test/AssetTest.cpp @@ -7,20 +7,15 @@ using namespace std; using namespace crepe; using namespace testing; -class AssetTest : public Test { -public: - Config & cfg = Config::get_instance(); - void SetUp() override { this->cfg.asset.root_pattern = ".crepe-root"; } -}; - -TEST_F(AssetTest, Existant) { ASSERT_NO_THROW(Asset{"asset/texture/img.png"}); } +TEST(AssetTest, Existant) { ASSERT_NO_THROW(Asset {"asset/texture/img.png"}); } -TEST_F(AssetTest, Nonexistant) { ASSERT_ANY_THROW(Asset{"asset/nonexistant"}); } +TEST(AssetTest, Nonexistant) { ASSERT_ANY_THROW(Asset {"asset/nonexistant"}); } -TEST_F(AssetTest, Rootless) { +TEST(AssetTest, Rootless) { + Config & cfg = Config::get_instance(); cfg.asset.root_pattern.clear(); string arbitrary = "\\/this is / /../passed through as-is"; - Asset asset{arbitrary}; + Asset asset {arbitrary}; ASSERT_EQ(arbitrary, asset.get_path()); } diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp new file mode 100644 index 0000000..e548221 --- /dev/null +++ b/src/test/AudioTest.cpp @@ -0,0 +1,170 @@ +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <crepe/api/AudioSource.h> +#include <crepe/api/GameObject.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/ResourceManager.h> +#include <crepe/system/AudioSystem.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class AudioTest : public Test { +private: + class TestSoundContext : public SoundContext { + public: + MOCK_METHOD(SoundHandle, play, (Sound & resource), (override)); + MOCK_METHOD(void, stop, (const SoundHandle &), (override)); + MOCK_METHOD(void, set_volume, (const SoundHandle &, float), (override)); + MOCK_METHOD(void, set_loop, (const SoundHandle &, bool), (override)); + }; + + class TestAudioSystem : public AudioSystem { + public: + using AudioSystem::AudioSystem; + StrictMock<TestSoundContext> context; + virtual SoundContext & get_context() { return this->context; } + }; + +private: + Mediator mediator; + ComponentManager component_manager {mediator}; + ResourceManager resource_manager {mediator}; + +public: + TestAudioSystem system {mediator}; + TestSoundContext & context = system.context; + +private: + GameObject entity = component_manager.new_object("name"); + +public: + AudioSource & component = entity.add_component<AudioSource>("mwe/audio/bgm.ogg"); +}; + +TEST_F(AudioTest, Default) { + EXPECT_CALL(context, play(_)).Times(0); + EXPECT_CALL(context, stop(_)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); + system.fixed_update(); +} + +TEST_F(AudioTest, Play) { + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, play(_)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); + component.play(); + } + + { + InSequence seq; + + EXPECT_CALL(context, play(_)).Times(1); + EXPECT_CALL(context, set_loop(_, _)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + system.fixed_update(); + } +} + +TEST_F(AudioTest, Stop) { + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(0); + component.stop(); + } + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(1); + system.fixed_update(); + } +} + +TEST_F(AudioTest, Volume) { + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, set_volume(_, _)).Times(0); + component.volume += 0.2; + } + + { + InSequence seq; + + EXPECT_CALL(context, set_volume(_, component.volume)).Times(1); + system.fixed_update(); + } +} + +TEST_F(AudioTest, Looping) { + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, set_loop(_, _)).Times(0); + component.loop = !component.loop; + } + + { + InSequence seq; + + EXPECT_CALL(context, set_loop(_, component.loop)).Times(1); + system.fixed_update(); + } +} + +TEST_F(AudioTest, StopOnDeactivate) { + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(1); + component.active = false; + system.fixed_update(); + } +} + +TEST_F(AudioTest, PlayOnActive) { + component.active = false; + component.play_on_awake = true; + system.fixed_update(); + + { + InSequence seq; + + EXPECT_CALL(context, play(_)).Times(1); + EXPECT_CALL(context, set_loop(_, _)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + + component.active = true; + system.fixed_update(); + } +} + +TEST_F(AudioTest, PlayImmediately) { + component.play_on_awake = false; + component.play(); + + EXPECT_CALL(context, play(_)).Times(1); + EXPECT_CALL(context, set_volume(_, _)).Times(1); + EXPECT_CALL(context, set_loop(_, _)).Times(1); + + system.fixed_update(); +} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 8cb4232..ea92d96 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,9 +1,12 @@ target_sources(test_main PUBLIC main.cpp + CollisionTest.cpp PhysicsTest.cpp ScriptTest.cpp ParticleTest.cpp + AudioTest.cpp AssetTest.cpp + ResourceManagerTest.cpp OptionalRefTest.cpp RenderSystemTest.cpp EventTest.cpp @@ -11,5 +14,15 @@ target_sources(test_main PUBLIC SceneManagerTest.cpp ValueBrokerTest.cpp DBTest.cpp + Vector2Test.cpp + # LoopManagerTest.cpp + LoopTimerTest.cpp + InputTest.cpp + ScriptEventTest.cpp + ScriptSceneTest.cpp + Profiling.cpp + SaveManagerTest.cpp + ScriptSaveManagerTest.cpp + ScriptECSTest.cpp + ReplayManagerTest.cpp ) - diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp new file mode 100644 index 0000000..c571c1a --- /dev/null +++ b/src/test/CollisionTest.cpp @@ -0,0 +1,364 @@ +#include "api/BoxCollider.h" +#include "manager/Mediator.h" +#include <cmath> +#include <cstddef> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/Mediator.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: + Mediator m; + EventManager event_mgr {m}; + ComponentManager mgr {m}; + CollisionSystem collision_sys {m}; + ScriptSystem script_sys {m}; + LoopTimerManager loop_timer {m}; + + 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, + }); + // Create a box with an inner size of 10x10 units + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {0, -100}); // Top + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {0, 100}); // Bottom + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {-100, 0}); // Left + world.add_component<BoxCollider>(vec2 {100, 100}, vec2 {100, 0}); // 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}, + .elasticity_coefficient = 1, + .collision_layers = {0}, + }); + game_object1.add_component<BoxCollider>(vec2 {10, 10}, vec2 {0, 0}); + 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}, + .elasticity_coefficient = 1, + .collision_layers = {0}, + }); + game_object2.add_component<BoxCollider>(vec2 {10, 10}, vec2 {0, 0}); + 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.fixed_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.self.transform.game_object_id, 1); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + }; + EXPECT_FALSE(collision_happend); + collision_sys.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 10); + EXPECT_EQ(ev.info.resolution.y, 10); + EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, -10); + EXPECT_EQ(ev.info.resolution.y, -10); + EXPECT_EQ(ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, -5); + EXPECT_EQ(ev.info.resolution.y, 0); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, 5); + EXPECT_EQ(ev.info.resolution.y, 0); + EXPECT_EQ( + ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 0); + EXPECT_EQ(ev.info.resolution.y, -5); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, 0); + EXPECT_EQ(ev.info.resolution.y, 5); + EXPECT_EQ( + ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 10); + EXPECT_EQ(ev.info.resolution.y, 10); + EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, -10); + EXPECT_EQ(ev.info.resolution.y, -10); + EXPECT_EQ(ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, -5); + EXPECT_EQ(ev.info.resolution.y, 5); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::X_DIRECTION + ); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, 5); + EXPECT_EQ(ev.info.resolution.y, -5); + EXPECT_EQ( + ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 5); + EXPECT_EQ(ev.info.resolution.y, -5); + EXPECT_EQ( + ev.info.resolution_direction, crepe::CollisionSystem::Direction::Y_DIRECTION + ); + }; + script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { + collision_happend = true; + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, -5); + EXPECT_EQ(ev.info.resolution.y, 5); + EXPECT_EQ( + ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 10); + EXPECT_EQ(ev.info.resolution.y, 10); + EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn + = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; }; + 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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, -5); + EXPECT_EQ(ev.info.resolution.y, 5); + EXPECT_EQ( + ev.info.resolution_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.fixed_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.self.transform.game_object_id, 1); + EXPECT_EQ(ev.info.resolution.x, 5); + EXPECT_EQ(ev.info.resolution.y, -5); + EXPECT_EQ( + ev.info.resolution_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.fixed_update(); + EXPECT_TRUE(collision_happend); +} diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp index e80814c..7f2c339 100644 --- a/src/test/DBTest.cpp +++ b/src/test/DBTest.cpp @@ -1,6 +1,7 @@ -#include <crepe/facade/DB.h> #include <gtest/gtest.h> +#include <crepe/facade/DB.h> + using namespace std; using namespace crepe; using namespace testing; @@ -26,3 +27,11 @@ TEST_F(DBTest, Has) { db.set("foo", "bar"); EXPECT_EQ(db.has("foo"), true); } + +TEST_F(DBTest, MultipleKeys) { + db.set("foo", "foo"); + db.set("bar", "bar"); + + EXPECT_EQ(db.get("foo"), "foo"); + EXPECT_EQ(db.get("bar"), "bar"); +} diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index d5a5826..92436a9 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -1,23 +1,30 @@ #include <gtest/gtest.h> #define protected public +#define private public -#include <crepe/ComponentManager.h> #include <crepe/api/GameObject.h> #include <crepe/api/Metadata.h> #include <crepe/api/Transform.h> #include <crepe/api/Vector2.h> +#include <crepe/manager/ComponentManager.h> using namespace std; using namespace crepe; class ECSTest : public ::testing::Test { + Mediator m; + public: - ComponentManager mgr{}; + ComponentManager mgr {m}; + + class TestComponent : public Component { + using Component::Component; + }; }; TEST_F(ECSTest, createGameObject) { - GameObject obj = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); @@ -37,8 +44,8 @@ TEST_F(ECSTest, createGameObject) { } TEST_F(ECSTest, deleteAllGameObjects) { - GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); mgr.delete_all_components(); @@ -48,7 +55,7 @@ TEST_F(ECSTest, deleteAllGameObjects) { EXPECT_EQ(metadata.size(), 0); EXPECT_EQ(transform.size(), 0); - GameObject obj2 = mgr.new_object("body2", "person2", Vector2{1, 0}, 5, 1); + GameObject obj2 = mgr.new_object("body2", "person2", vec2 {1, 0}, 5, 1); metadata = mgr.get_components_by_type<Metadata>(); transform = mgr.get_components_by_type<Transform>(); @@ -70,8 +77,8 @@ TEST_F(ECSTest, deleteAllGameObjects) { } TEST_F(ECSTest, deleteGameObject) { - GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); mgr.delete_all_components_of_id(0); @@ -96,7 +103,7 @@ TEST_F(ECSTest, deleteGameObject) { TEST_F(ECSTest, manyGameObjects) { for (int i = 0; i < 5000; i++) { - GameObject obj = mgr.new_object("body", "person", Vector2{0, 0}, 0, i); + GameObject obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, i); } vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); @@ -128,7 +135,7 @@ TEST_F(ECSTest, manyGameObjects) { for (int i = 0; i < 10000 - 5000; i++) { string tag = "person" + to_string(i); - GameObject obj = mgr.new_object("body", tag, Vector2{0, 0}, i, 0); + GameObject obj = mgr.new_object("body", tag, vec2 {0, 0}, i, 0); } metadata = mgr.get_components_by_type<Metadata>(); @@ -136,11 +143,58 @@ TEST_F(ECSTest, manyGameObjects) { EXPECT_EQ(metadata.size(), 10000 - 5000); EXPECT_EQ(transform.size(), 10000); + + for (int i = 0; i < 10000 - 5000; i++) { + EXPECT_EQ(metadata[i].get().game_object_id, i + 5000); + EXPECT_EQ(metadata[i].get().name, "body"); + EXPECT_EQ(metadata[i].get().tag, "person" + to_string(i)); + EXPECT_EQ(metadata[i].get().parent, -1); + EXPECT_EQ(metadata[i].get().children.size(), 0); + + EXPECT_EQ(transform[i].get().game_object_id, i); + EXPECT_EQ(transform[i].get().position.x, 0); + EXPECT_EQ(transform[i].get().position.y, 0); + EXPECT_EQ(transform[i].get().rotation, 0); + EXPECT_EQ(transform[i].get().scale, i); + } + + mgr.delete_all_components(); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 0); + EXPECT_EQ(transform.size(), 0); + + for (int i = 0; i < 10000; i++) { + string name = "body" + to_string(i); + GameObject obj = mgr.new_object(name, "person", vec2 {0, 0}, 0, 0); + } + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 10000); + EXPECT_EQ(transform.size(), 10000); + + for (int i = 0; i < 10000; i++) { + EXPECT_EQ(metadata[i].get().game_object_id, i); + EXPECT_EQ(metadata[i].get().name, "body" + to_string(i)); + EXPECT_EQ(metadata[i].get().tag, "person"); + EXPECT_EQ(metadata[i].get().parent, -1); + EXPECT_EQ(metadata[i].get().children.size(), 0); + + EXPECT_EQ(transform[i].get().game_object_id, i); + EXPECT_EQ(transform[i].get().position.x, 0); + EXPECT_EQ(transform[i].get().position.y, 0); + EXPECT_EQ(transform[i].get().rotation, 0); + EXPECT_EQ(transform[i].get().scale, 0); + } } TEST_F(ECSTest, getComponentsByID) { - GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); - GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_id<Metadata>(0); vector<reference_wrapper<Transform>> transform = mgr.get_components_by_id<Transform>(1); @@ -163,19 +217,21 @@ TEST_F(ECSTest, getComponentsByID) { TEST_F(ECSTest, tooMuchComponents) { try { - GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); - obj0.add_component<Transform>(Vector2{10, 10}, 0, 1); + GameObject obj0 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + obj0.add_component<Transform>(vec2 {10, 10}, 0, 1); } catch (const exception & e) { - EXPECT_EQ(e.what(), - string("Exceeded maximum number of instances for this component type")); + EXPECT_EQ( + e.what(), string("Exceeded maximum number of instances for this component type") + ); } try { - GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); obj1.add_component<Metadata>("body", "person"); } catch (const exception & e) { - EXPECT_EQ(e.what(), - string("Exceeded maximum number of instances for this component type")); + EXPECT_EQ( + e.what(), string("Exceeded maximum number of instances for this component type") + ); } vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); @@ -187,11 +243,11 @@ TEST_F(ECSTest, tooMuchComponents) { TEST_F(ECSTest, partentChild) { { - GameObject body = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1); - GameObject right_leg = mgr.new_object("rightLeg", "person", Vector2{1, 1}, 0, 1); - GameObject left_leg = mgr.new_object("leftLeg", "person", Vector2{1, 1}, 0, 1); - GameObject right_foot = mgr.new_object("rightFoot", "person", Vector2{2, 2}, 0, 1); - GameObject left_foot = mgr.new_object("leftFoot", "person", Vector2{2, 2}, 0, 1); + GameObject body = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + GameObject right_leg = mgr.new_object("rightLeg", "person", vec2 {1, 1}, 0, 1); + GameObject left_leg = mgr.new_object("leftLeg", "person", vec2 {1, 1}, 0, 1); + GameObject right_foot = mgr.new_object("rightFoot", "person", vec2 {2, 2}, 0, 1); + GameObject left_foot = mgr.new_object("leftFoot", "person", vec2 {2, 2}, 0, 1); // Set the parent of each GameObject right_foot.set_parent(right_leg); @@ -234,3 +290,195 @@ TEST_F(ECSTest, partentChild) { EXPECT_EQ(metadata[1].get().children[0], 3); EXPECT_EQ(metadata[2].get().children[0], 4); } + +TEST_F(ECSTest, persistent) { + GameObject obj0 = mgr.new_object("obj0", "obj0", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("obj1", "obj1", vec2 {0, 0}, 0, 1); + obj1.set_persistent(); + GameObject obj2 = mgr.new_object("obj2", "obj2", vec2 {0, 0}, 0, 1); + + vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); + vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 3); + EXPECT_EQ(transform.size(), 3); + + mgr.delete_components_by_id<Metadata>(1); + mgr.delete_components<Metadata>(); + mgr.delete_all_components_of_id(1); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 1); + EXPECT_EQ(transform.size(), 3); + + mgr.delete_all_components(); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 1); + EXPECT_EQ(transform.size(), 1); + + EXPECT_EQ(metadata[0].get().game_object_id, 1); + EXPECT_EQ(metadata[0].get().name, "obj1"); + EXPECT_EQ(metadata[0].get().tag, "obj1"); + EXPECT_EQ(metadata[0].get().parent, -1); + EXPECT_EQ(metadata[0].get().children.size(), 0); + + EXPECT_EQ(transform[0].get().game_object_id, 1); + EXPECT_EQ(transform[0].get().position.x, 0); + EXPECT_EQ(transform[0].get().position.y, 0); + + GameObject obj3 = mgr.new_object("obj3", "obj3", vec2 {0, 0}, 0, 5); + GameObject obj4 = mgr.new_object("obj4", "obj4", vec2 {0, 0}, 0, 5); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 3); + EXPECT_EQ(transform.size(), 3); + + EXPECT_EQ(metadata[0].get().game_object_id, 0); + EXPECT_EQ(metadata[0].get().name, "obj3"); + + EXPECT_EQ(metadata[1].get().game_object_id, 1); + EXPECT_EQ(metadata[1].get().name, "obj1"); + + EXPECT_EQ(metadata[2].get().game_object_id, 2); + EXPECT_EQ(metadata[2].get().name, "obj4"); + + EXPECT_EQ(transform[0].get().game_object_id, 0); + EXPECT_EQ(transform[0].get().scale, 5); + + EXPECT_EQ(transform[1].get().game_object_id, 1); + EXPECT_EQ(transform[1].get().scale, 1); + + EXPECT_EQ(transform[2].get().game_object_id, 2); + EXPECT_EQ(transform[2].get().scale, 5); +} + +TEST_F(ECSTest, resetPersistent) { + GameObject obj0 = mgr.new_object("obj0", "obj0", vec2 {0, 0}, 0, 1); + GameObject obj1 = mgr.new_object("obj1", "obj1", vec2 {0, 0}, 0, 1); + obj1.set_persistent(); + GameObject obj2 = mgr.new_object("obj2", "obj2", vec2 {0, 0}, 0, 1); + + vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>(); + vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 3); + EXPECT_EQ(transform.size(), 3); + + mgr.delete_all_components(); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 1); + EXPECT_EQ(transform.size(), 1); + + vector<reference_wrapper<Metadata>> metadata_id = mgr.get_components_by_id<Metadata>(1); + + EXPECT_EQ(metadata_id.size(), 1); + EXPECT_EQ(metadata_id[0].get().game_object_id, 1); + EXPECT_EQ(metadata_id[0].get().name, "obj1"); + + mgr.set_persistent(1, false); + mgr.delete_all_components(); + + metadata = mgr.get_components_by_type<Metadata>(); + transform = mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 0); + EXPECT_EQ(transform.size(), 0); +} + +TEST_F(ECSTest, IDByName) { + GameObject foo = mgr.new_object("foo"); + GameObject bar = mgr.new_object("bar"); + + { + auto objects = mgr.get_objects_by_name(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_name("foo"); + EXPECT_EQ(objects.size(), 1); + EXPECT_TRUE(objects.contains(foo.id)); + } +} + +TEST_F(ECSTest, IDByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + GameObject bar = mgr.new_object("bar", "common tag"); + + { + auto objects = mgr.get_objects_by_tag(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_tag("common tag"); + EXPECT_EQ(objects.size(), 2); + EXPECT_TRUE(objects.contains(foo.id)); + EXPECT_TRUE(objects.contains(bar.id)); + } +} + +TEST_F(ECSTest, ComponentsByName) { + GameObject foo = mgr.new_object("foo"); + foo.add_component<TestComponent>(); + GameObject bar = mgr.new_object("bar"); + bar.add_component<TestComponent>(); + bar.add_component<TestComponent>(); + + { + auto objects = mgr.get_components_by_name<TestComponent>(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_name<TestComponent>("foo"); + EXPECT_EQ(objects.size(), 1); + } + + { + auto objects = mgr.get_components_by_name<TestComponent>("bar"); + EXPECT_EQ(objects.size(), 2); + } +} + +TEST_F(ECSTest, ComponentsByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + foo.add_component<TestComponent>(); + GameObject bar = mgr.new_object("bar", "common tag"); + bar.add_component<TestComponent>(); + bar.add_component<TestComponent>(); + + { + auto objects = mgr.get_components_by_tag<TestComponent>(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_tag<TestComponent>("common tag"); + EXPECT_EQ(objects.size(), 3); + } +} + +TEST_F(ECSTest, Snapshot) { + GameObject foo = mgr.new_object("foo"); + + foo.transform.position = {1, 1}; + + ComponentManager::Snapshot snapshot = mgr.save(); + + foo.transform.position = {0, 0}; + + mgr.restore(snapshot); + + EXPECT_EQ(foo.transform.position, (vec2 {1, 1})); +} diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index b0e6c9c..6105679 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,8 +1,6 @@ - -#include "api/Event.h" -#include "api/EventManager.h" -#include "api/IKeyListener.h" -#include "api/IMouseListener.h" +#include <crepe/api/Event.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/Mediator.h> #include <gmock/gmock.h> #include <gtest/gtest.h> using namespace std; @@ -11,72 +9,56 @@ using namespace crepe; class EventManagerTest : public ::testing::Test { protected: + Mediator mediator; + EventManager event_mgr {mediator}; void SetUp() override { // Clear any existing subscriptions or events before each test - EventManager::get_instance().clear(); + event_mgr.clear(); } void TearDown() override { // Ensure cleanup after each test - EventManager::get_instance().clear(); + event_mgr.clear(); } }; -class MockKeyListener : public IKeyListener { -public: - MOCK_METHOD(bool, on_key_pressed, (const KeyPressEvent & event), (override)); - MOCK_METHOD(bool, on_key_released, (const KeyReleaseEvent & event), (override)); -}; - -class MockMouseListener : public IMouseListener { -public: - MOCK_METHOD(bool, on_mouse_clicked, (const MouseClickEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_pressed, (const MousePressEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_released, (const MouseReleaseEvent & event), (override)); - MOCK_METHOD(bool, on_mouse_moved, (const MouseMoveEvent & event), (override)); -}; - TEST_F(EventManagerTest, EventSubscription) { - EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) { - std::cout << "Key Event Triggered" << std::endl; - return true; - }; + EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) { return true; }; // Subscribe to KeyPressEvent - EventManager::get_instance().subscribe<KeyPressEvent>(key_handler, 1); + event_mgr.subscribe<KeyPressEvent>(key_handler, 1); // Verify subscription (not directly verifiable; test by triggering event) - EventManager::get_instance().trigger_event<KeyPressEvent>( - KeyPressEvent{ + event_mgr.trigger_event<KeyPressEvent>( + KeyPressEvent { .repeat = true, .key = Keycode::A, }, - 1); - EventManager::get_instance().trigger_event<KeyPressEvent>( - KeyPressEvent{ + 1 + ); + event_mgr.trigger_event<KeyPressEvent>( + KeyPressEvent { .repeat = true, .key = Keycode::A, }, - EventManager::CHANNEL_ALL); + EventManager::CHANNEL_ALL + ); } TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { bool triggered = false; EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) { triggered = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, - EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler, EventManager::CHANNEL_ALL); - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, - EventManager::CHANNEL_ALL); + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } @@ -85,24 +67,21 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { int test_channel = 1; EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) { triggered = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, test_channel); + event_mgr.subscribe<MouseClickEvent>(mouse_handler, test_channel); - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, - EventManager::CHANNEL_ALL); + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); - EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, test_channel); + event_mgr.trigger_event<MouseClickEvent>(click_event, test_channel); } TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track handler calls bool triggered_true = false; @@ -111,28 +90,27 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Handlers EventHandler<MouseClickEvent> mouse_handler_true = [&](const MouseClickEvent & e) { triggered_true = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return true; // Stops propagation }; EventHandler<MouseClickEvent> mouse_handler_false = [&](const MouseClickEvent & e) { triggered_false = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; // Test event - MouseClickEvent click_event{ - .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); - event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); + MouseClickEvent click_event {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}; + event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); // Trigger event - event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); // Check that only the true handler was triggered EXPECT_TRUE(triggered_true); @@ -141,12 +119,12 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Reset and clear triggered_true = false; triggered_false = false; - event_manager.clear(); - event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); - event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); + event_mgr.clear(); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL); + event_mgr.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL); // Trigger event again - event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); + event_mgr.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL); // Check that both handlers were triggered EXPECT_TRUE(triggered_true); @@ -154,39 +132,39 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { } TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { - EventManager & event_manager = EventManager::get_instance(); bool triggered1 = false; bool triggered2 = false; int test_channel = 1; EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) { triggered1 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) { triggered2 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - event_manager.subscribe<MouseClickEvent>(mouse_handler1); - event_manager.subscribe<MouseClickEvent>(mouse_handler2, test_channel); - - event_manager.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); - event_manager.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, - test_channel); - event_manager.dispatch_events(); + event_mgr.subscribe<MouseClickEvent>(mouse_handler1); + event_mgr.subscribe<MouseClickEvent>(mouse_handler2, test_channel); + + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE}, + test_channel + ); + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); EXPECT_TRUE(triggered2); } TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { - EventManager & event_manager = EventManager::get_instance(); // Flags to track if handlers are triggered bool triggered1 = false; @@ -195,29 +173,30 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { // Define EventHandlers EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) { triggered1 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) { triggered2 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.mouse_pos.x, 100); + EXPECT_EQ(e.mouse_pos.y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; // Subscribe handlers - subscription_t handler1_id = event_manager.subscribe<MouseClickEvent>(mouse_handler1); - subscription_t handler2_id = event_manager.subscribe<MouseClickEvent>(mouse_handler2); + subscription_t handler1_id = event_mgr.subscribe<MouseClickEvent>(mouse_handler1); + subscription_t handler2_id = event_mgr.subscribe<MouseClickEvent>(mouse_handler2); // Queue events - event_manager.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - both handlers should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_TRUE(triggered1); // Handler 1 should be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -226,14 +205,15 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler1 - event_manager.unsubscribe(handler1_id); + event_mgr.unsubscribe(handler1_id); // Queue the same event again - event_manager.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - only handler 2 should be triggered, handler 1 should NOT - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_TRUE(triggered2); // Handler 2 should be triggered @@ -241,14 +221,15 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler2 - event_manager.unsubscribe(handler2_id); + event_mgr.unsubscribe(handler2_id); // Queue the event again - event_manager.queue_event<MouseClickEvent>( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_mgr.queue_event<MouseClickEvent>( + MouseClickEvent {.mouse_pos = {100, 200}, .button = MouseButton::LEFT_MOUSE} + ); // Dispatch events - no handler should be triggered - event_manager.dispatch_events(); + event_mgr.dispatch_events(); EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered } diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp new file mode 100644 index 0000000..a1fe59a --- /dev/null +++ b/src/test/InputTest.cpp @@ -0,0 +1,337 @@ +#include <gtest/gtest.h> + +#include <crepe/manager/ResourceManager.h> +#include <crepe/system/RenderSystem.h> +#define protected public +#define private public + +#include "api/KeyCodes.h" +#include "manager/ComponentManager.h" +#include "manager/EventManager.h" +#include "manager/Mediator.h" +#include "system/InputSystem.h" +#include <SDL2/SDL.h> +#include <SDL2/SDL_keycode.h> +#include <crepe/api/Button.h> +#include <crepe/api/Camera.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Metadata.h> +#include <crepe/api/Transform.h> +#include <crepe/api/Vector2.h> +#include <crepe/facade/SDLContext.h> +#include <gmock/gmock.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; + +class InputTest : public ::testing::Test { +public: + Mediator mediator; + ComponentManager mgr {mediator}; + SDLContext sdl_context {mediator}; + + InputSystem input_system {mediator}; + ResourceManager resman {mediator}; + RenderSystem render {mediator}; + EventManager event_manager {mediator}; + //GameObject camera; + vec2 offset = {100, 200}; + +protected: + void SetUp() override { + GameObject obj = mgr.new_object("camera", "camera", offset, 0, 1); + auto & camera = obj.add_component<Camera>( + ivec2 {500, 500}, vec2 {500, 500}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); + render.frame_update(); + //mediator.event_manager = event_manager; + //mediator.component_manager = mgr; + //event_manager.clear(); + } + void TearDown() override {} + void simulate_mouse_click(int mouse_x, int mouse_y, Uint8 mouse_button) { + SDL_Event event; + + // Simulate Mouse Button Down event + SDL_zero(event); + event.type = SDL_MOUSEBUTTONDOWN; + event.button.x = mouse_x; + event.button.y = mouse_y; + event.button.button = mouse_button; + SDL_PushEvent(&event); + + // Simulate Mouse Button Up event + SDL_zero(event); + event.type = SDL_MOUSEBUTTONUP; + event.button.x = mouse_x; + event.button.y = mouse_y; + event.button.button = mouse_button; + SDL_PushEvent(&event); + } +}; + +TEST_F(InputTest, MouseDown) { + bool mouse_triggered = false; + EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { + mouse_triggered = true; + //middle of the screen = 0,0 + EXPECT_EQ(event.mouse_pos, offset); + EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); + return false; + }; + event_manager.subscribe<MousePressEvent>(on_mouse_down); + + SDL_Event event; + SDL_zero(event); + event.type = SDL_MOUSEBUTTONDOWN; + // middle of the screen of a 500*500 camera = 250*250 + event.button.x = 250; + event.button.y = 250; + event.button.button = SDL_BUTTON_LEFT; + SDL_PushEvent(&event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(mouse_triggered); +} + +TEST_F(InputTest, MouseUp) { + bool function_triggered = false; + EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { + function_triggered = true; + EXPECT_EQ(e.mouse_pos, offset); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; + }; + event_manager.subscribe<MouseReleaseEvent>(on_mouse_release); + + SDL_Event event; + SDL_zero(event); + event.type = SDL_MOUSEBUTTONUP; + event.button.x = 250; + event.button.y = 250; + event.button.button = SDL_BUTTON_LEFT; + SDL_PushEvent(&event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, MouseMove) { + bool function_triggered = false; + EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { + function_triggered = true; + EXPECT_EQ(e.mouse_pos, offset); + EXPECT_EQ(e.mouse_delta.x, 10); + EXPECT_EQ(e.mouse_delta.y, 10); + return false; + }; + event_manager.subscribe<MouseMoveEvent>(on_mouse_move); + + SDL_Event event; + SDL_zero(event); + event.type = SDL_MOUSEMOTION; + event.motion.x = 250; + event.motion.y = 250; + event.motion.xrel = 10; + event.motion.yrel = 10; + SDL_PushEvent(&event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, KeyDown) { + bool function_triggered = false; + + // Define event handler for KeyPressEvent + EventHandler<KeyPressEvent> on_key_press = [&](const KeyPressEvent & event) { + function_triggered = true; + EXPECT_EQ(event.key, Keycode::B); // Validate the key is 'B' + EXPECT_EQ(event.repeat, true); // Validate repeat flag + return false; + }; + + event_manager.subscribe<KeyPressEvent>(on_key_press); + + // Simulate SDL_KEYDOWN event + SDL_Event test_event; + SDL_zero(test_event); + test_event.type = SDL_KEYDOWN; // Key down event + test_event.key.keysym.scancode = SDL_SCANCODE_B; // Set scancode for 'B' + test_event.key.repeat = 1; // Set repeat flag + SDL_PushEvent(&test_event); + + input_system.fixed_update(); // Process the event + event_manager.dispatch_events(); // Dispatch events to handlers + + EXPECT_TRUE(function_triggered); // Check if the handler was triggered +} + +TEST_F(InputTest, KeyUp) { + bool function_triggered = false; + EventHandler<KeyReleaseEvent> on_key_release = [&](const KeyReleaseEvent & event) { + function_triggered = true; + EXPECT_EQ(event.key, Keycode::B); + return false; + }; + event_manager.subscribe<KeyReleaseEvent>(on_key_release); + + SDL_Event event; + SDL_zero(event); + event.type = SDL_KEYUP; + event.key.keysym.scancode = SDL_SCANCODE_B; + SDL_PushEvent(&event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(function_triggered); +} + +TEST_F(InputTest, MouseClick) { + bool on_click_triggered = false; + EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { + on_click_triggered = true; + EXPECT_EQ(event.button, MouseButton::LEFT_MOUSE); + EXPECT_EQ(event.mouse_pos, offset); + return false; + }; + event_manager.subscribe<MouseClickEvent>(on_mouse_click); + + this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(on_click_triggered); +} + +TEST_F(InputTest, testButtonClick) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + bool button_clicked = false; + event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { + button_clicked = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button + = button_obj.add_component<Button>(vec2 {100, 100}, Button::Data {}, vec2 {0, 0}); + + bool hover = false; + button.active = true; + this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); + + this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(button_clicked); +} +TEST_F(InputTest, buttonPositionCamera) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {50, 50}, 0, 1); + bool button_clicked = false; + event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { + button_clicked = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button = button_obj.add_component<Button>( + vec2 {10, 10}, + Button::Data { + .world_space = false, + }, + vec2 {0, 0} + ); + + bool hover = false; + button.active = true; + this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); + + this->simulate_mouse_click(300, 300, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(button_clicked); +} +TEST_F(InputTest, buttonPositionWorld) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {50, 50}, 0, 1); + bool button_clicked = false; + event_manager.subscribe<ButtonPressEvent>([&](const ButtonPressEvent & event) { + button_clicked = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button = button_obj.add_component<Button>( + vec2 {10, 10}, + Button::Data { + .world_space = true, + }, + vec2 {0, 0} + ); + bool hover = false; + button.active = true; + this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); + + this->simulate_mouse_click(300, 300, SDL_BUTTON_LEFT); + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button_clicked); +} +TEST_F(InputTest, testButtonHover) { + GameObject button_obj = mgr.new_object("body", "person", vec2 {0, 0}, 0, 1); + bool button_hover = false; + event_manager.subscribe<ButtonEnterEvent>([&](const ButtonEnterEvent & event) { + button_hover = true; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + event_manager.subscribe<ButtonExitEvent>([&](const ButtonExitEvent & event) { + button_hover = false; + EXPECT_EQ(event.metadata.game_object_id, button_obj.id); + return false; + }); + auto & button = button_obj.add_component<Button>( + vec2 {100, 100}, + Button::Data { + .world_space = true, + }, + vec2 {0, 0} + ); + // Mouse on button + SDL_Event hover_event; + SDL_zero(hover_event); + hover_event.type = SDL_MOUSEMOTION; + hover_event.motion.x = 250; + hover_event.motion.y = 250; + hover_event.motion.xrel = 10; + hover_event.motion.yrel = 10; + SDL_PushEvent(&hover_event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_TRUE(button.hover); + EXPECT_TRUE(button_hover); + // Mouse not on button + SDL_Event event; + SDL_zero(event); + event.type = SDL_MOUSEMOTION; + event.motion.x = 500; + event.motion.y = 500; + event.motion.xrel = 10; + event.motion.yrel = 10; + SDL_PushEvent(&event); + + input_system.fixed_update(); + event_manager.dispatch_events(); + EXPECT_FALSE(button.hover); + EXPECT_FALSE(button_hover); +} diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp new file mode 100644 index 0000000..302d96c --- /dev/null +++ b/src/test/LoopManagerTest.cpp @@ -0,0 +1,78 @@ +#include <chrono> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thread> +#define private public +#define protected public +#include <crepe/api/Engine.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/LoopTimerManager.h> +using namespace std::chrono; +using namespace crepe; + +class DISABLED_LoopManagerTest : public ::testing::Test { +protected: + class TestGameLoop : public crepe::Engine { + public: + MOCK_METHOD(void, fixed_update, (), (override)); + MOCK_METHOD(void, frame_update, (), (override)); + }; + + TestGameLoop test_loop; + void SetUp() override {} +}; + +TEST_F(DISABLED_LoopManagerTest, FixedUpdate) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + + // Set expectations for the mock calls + EXPECT_CALL(test_loop, frame_update).Times(::testing::Between(55, 65)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Between(48, 52)); + + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + + // Let the loop run for exactly 1 second + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Stop the game loop + test_loop.game_running = false; + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} + +TEST_F(DISABLED_LoopManagerTest, ScaledFixedUpdate) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + + // Set expectations for the mock calls + EXPECT_CALL(test_loop, frame_update).Times(::testing::Between(55, 65)); + EXPECT_CALL(test_loop, fixed_update).Times(::testing::Between(48, 52)); + + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + + // Let the loop run for exactly 1 second + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Stop the game loop + test_loop.game_running = false; + // Wait for the loop thread to finish + loop_thread.join(); + + // Test finished +} + +TEST_F(DISABLED_LoopManagerTest, ShutDown) { + // Arrange + test_loop.loop_timer.set_target_framerate(60); + // Start the loop in a separate thread + std::thread loop_thread([&]() { test_loop.start(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + test_loop.event_manager.trigger_event<ShutDownEvent>(ShutDownEvent {}); + // Wait for the loop thread to finish + loop_thread.join(); +} diff --git a/src/test/LoopTimerTest.cpp b/src/test/LoopTimerTest.cpp new file mode 100644 index 0000000..52e412e --- /dev/null +++ b/src/test/LoopTimerTest.cpp @@ -0,0 +1,97 @@ +#include <chrono> +#include <gtest/gtest.h> +#include <thread> + +#define private public +#define protected public + +#include <crepe/manager/LoopTimerManager.h> +#include <crepe/manager/Mediator.h> + +using namespace std::chrono; +using namespace crepe; + +class LoopTimerTest : public ::testing::Test { +protected: + Mediator mediator; + LoopTimerManager loop_timer {mediator}; + + void SetUp() override { loop_timer.start(); } +}; + +TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { + // Set the target FPS to 60 (which gives a target time per frame of ~16.67 ms) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + loop_timer.enforce_frame_rate(); + + auto elapsed_time = steady_clock::now() - start_time; + auto elapsed_ms = duration_cast<milliseconds>(elapsed_time).count(); + + // For 60 FPS, the target frame time is around 16.67ms + ASSERT_NEAR(elapsed_ms, 16.7, 5); +} + +TEST_F(LoopTimerTest, SetTargetFps) { + // Set the target FPS to 120 + loop_timer.set_target_framerate(120); + + // Calculate the expected frame time (~8.33ms per frame) + duration_t expected_frame_time = std::chrono::duration<float>(1.0 / 120.0); + + ASSERT_NEAR(loop_timer.frame_target_time.count(), expected_frame_time.count(), 0.001); +} + +TEST_F(LoopTimerTest, DeltaTimeCalculation) { + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + loop_timer.update(); + auto end_time = steady_clock::now(); + + // Check the delta time + duration_t delta_time = loop_timer.get_delta_time(); + + auto elapsed_time = duration_cast<seconds>(end_time - start_time).count(); + + // Assert that delta_time is close to the elapsed time + ASSERT_NEAR(delta_time.count(), elapsed_time, 1); +} + +TEST_F(LoopTimerTest, DISABLED_getCurrentTime) { + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + + // Sleep + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + loop_timer.update(); + + auto end_time = steady_clock::now(); + + // Get the elapsed time in seconds as a double + auto elapsed_time + = std::chrono::duration_cast<elapsed_time_t>(end_time - start_time).count(); + + ASSERT_NEAR(loop_timer.get_elapsed_time().count(), elapsed_time, 5); +} +TEST_F(LoopTimerTest, getFPS) { + // Set the target FPS to 60 (which gives a target time per frame of ~16.67 ms) + loop_timer.set_target_framerate(60); + + auto start_time = steady_clock::now(); + loop_timer.enforce_frame_rate(); + + auto elapsed_time = steady_clock::now() - start_time; + loop_timer.update(); + unsigned int fps = loop_timer.get_fps(); + auto elapsed_ms = duration_cast<milliseconds>(elapsed_time).count(); + + // For 60 FPS, the target frame time is around 16.67ms + ASSERT_NEAR(elapsed_ms, 16.7, 1); + ASSERT_NEAR(fps, 60, 2); +} diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index eee022f..eee7a73 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -1,56 +1,70 @@ -#include "api/Vector2.h" -#include <crepe/ComponentManager.h> -#include <crepe/Particle.h> +#include "api/Asset.h" #include <crepe/api/Config.h> #include <crepe/api/GameObject.h> -#include <crepe/api/ParticleEmitter.h> #include <crepe/api/Rigidbody.h> #include <crepe/api/Sprite.h> #include <crepe/api/Transform.h> -#include <crepe/system/ParticleSystem.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/LoopTimerManager.h> #include <gtest/gtest.h> #include <math.h> +#define protected public +#define private public +#include <crepe/Particle.h> +#include <crepe/api/ParticleEmitter.h> +#include <crepe/system/ParticleSystem.h> using namespace std; using namespace std::chrono_literals; using namespace crepe; class ParticlesTest : public ::testing::Test { + Mediator m; + public: - ComponentManager component_manager; - ParticleSystem particle_system{component_manager}; + ComponentManager component_manager {m}; + ParticleSystem particle_system {m}; + LoopTimerManager loop_timer {m}; void SetUp() override { ComponentManager & mgr = this->component_manager; std::vector<std::reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - GameObject game_object = mgr.new_object("", "", Vector2{0, 0}, 0, 0); + GameObject game_object = mgr.new_object("", "", vec2 {0, 0}, 0, 0); Color color(0, 0, 0, 0); + auto s1 = Asset("asset/texture/img.png"); Sprite & test_sprite = game_object.add_component<Sprite>( - make_shared<Texture>("asset/texture/img.png"), color, - FlipSettings{true, true}); + s1, + Sprite::Data { + .color = color, + .flip = Sprite::FlipSettings {true, true}, + .size = {10, 10}, + } + ); - game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{ - .position = {0, 0}, - .max_particles = 100, - .emission_rate = 0, - .min_speed = 0, - .max_speed = 0, - .min_angle = 0, - .max_angle = 0, - .begin_lifespan = 0, - .end_lifespan = 0, - .force_over_time = Vector2{0, 0}, - .boundary{ - .width = 0, - .height = 0, - .offset = Vector2{0, 0}, - .reset_on_exit = false, - }, - .sprite = test_sprite, - }); + game_object.add_component<ParticleEmitter>( + test_sprite, + ParticleEmitter::Data { + .offset = {0, 0}, + .max_particles = 100, + .emission_rate = 0, + .min_speed = 0, + .max_speed = 0, + .min_angle = 0, + .max_angle = 0, + .begin_lifespan = 0, + .end_lifespan = 0, + .force_over_time = vec2 {0, 0}, + .boundary { + .width = 0, + .height = 0, + .offset = vec2 {0, 0}, + .reset_on_exit = false, + }, + } + ); } transforms = mgr.get_components_by_id<Transform>(0); Transform & transform = transforms.front().get(); @@ -60,7 +74,7 @@ public: std::vector<std::reference_wrapper<ParticleEmitter>> rigidbodies = mgr.get_components_by_id<ParticleEmitter>(0); ParticleEmitter & emitter = rigidbodies.front().get(); - emitter.data.position = {0, 0}; + emitter.data.offset = {0, 0}; emitter.data.emission_rate = 0; emitter.data.min_speed = 0; emitter.data.max_speed = 0; @@ -68,9 +82,9 @@ public: emitter.data.max_angle = 0; emitter.data.begin_lifespan = 0; emitter.data.end_lifespan = 0; - emitter.data.force_over_time = Vector2{0, 0}; - emitter.data.boundary = {0, 0, Vector2{0, 0}, false}; - for (auto & particle : emitter.data.particles) { + emitter.data.force_over_time = vec2 {0, 0}; + emitter.data.boundary = {0, 0, vec2 {0, 0}, false}; + for (auto & particle : emitter.particles) { particle.active = false; } } @@ -87,21 +101,21 @@ TEST_F(ParticlesTest, spawnParticle) { emitter.data.max_angle = 0.1; emitter.data.max_speed = 10; emitter.data.max_angle = 10; - particle_system.update(); + particle_system.fixed_update(); //check if nothing happend - EXPECT_EQ(emitter.data.particles[0].active, false); - emitter.data.emission_rate = 1; + EXPECT_EQ(emitter.particles[0].active, false); + emitter.data.emission_rate = 50; //check particle spawnes - particle_system.update(); - EXPECT_EQ(emitter.data.particles[0].active, true); - particle_system.update(); - EXPECT_EQ(emitter.data.particles[1].active, true); - particle_system.update(); - EXPECT_EQ(emitter.data.particles[2].active, true); - particle_system.update(); - EXPECT_EQ(emitter.data.particles[3].active, true); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[0].active, true); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[1].active, true); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[2].active, true); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[3].active, true); - for (auto & particle : emitter.data.particles) { + for (auto & particle : emitter.particles) { // Check velocity range EXPECT_GE(particle.velocity.x, emitter.data.min_speed); // Speed should be greater than or equal to min_speed @@ -127,13 +141,13 @@ TEST_F(ParticlesTest, moveParticleHorizontal) { emitter.data.end_lifespan = 100; emitter.data.boundary.height = 100; emitter.data.boundary.width = 100; - emitter.data.min_speed = 1; - emitter.data.max_speed = 1; + emitter.data.min_speed = 50; + emitter.data.max_speed = 50; emitter.data.max_angle = 0; - emitter.data.emission_rate = 1; + emitter.data.emission_rate = 50; for (int a = 1; a < emitter.data.boundary.width / 2; a++) { - particle_system.update(); - EXPECT_EQ(emitter.data.particles[0].position.x, a); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[0].position.x, a); } } @@ -144,14 +158,14 @@ TEST_F(ParticlesTest, moveParticleVertical) { emitter.data.end_lifespan = 100; emitter.data.boundary.height = 100; emitter.data.boundary.width = 100; - emitter.data.min_speed = 1; - emitter.data.max_speed = 1; + emitter.data.min_speed = 50; + emitter.data.max_speed = 50; emitter.data.min_angle = 90; emitter.data.max_angle = 90; - emitter.data.emission_rate = 1; + emitter.data.emission_rate = 50; for (int a = 1; a < emitter.data.boundary.width / 2; a++) { - particle_system.update(); - EXPECT_EQ(emitter.data.particles[0].position.y, a); + particle_system.fixed_update(); + EXPECT_EQ(emitter.particles[0].position.y, a); } } @@ -169,9 +183,9 @@ TEST_F(ParticlesTest, boundaryParticleReset) { emitter.data.max_angle = 90; emitter.data.emission_rate = 1; for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { - particle_system.update(); + particle_system.fixed_update(); } - EXPECT_EQ(emitter.data.particles[0].active, false); + EXPECT_EQ(emitter.particles[0].active, false); } TEST_F(ParticlesTest, boundaryParticleStop) { @@ -188,15 +202,19 @@ TEST_F(ParticlesTest, boundaryParticleStop) { emitter.data.max_angle = 90; emitter.data.emission_rate = 1; for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { - particle_system.update(); + particle_system.fixed_update(); } const double TOLERANCE = 0.01; - EXPECT_NEAR(emitter.data.particles[0].velocity.x, 0, TOLERANCE); - EXPECT_NEAR(emitter.data.particles[0].velocity.y, 0, TOLERANCE); - if (emitter.data.particles[0].velocity.x != 0) - EXPECT_NEAR(std::abs(emitter.data.particles[0].position.x), - emitter.data.boundary.height / 2, TOLERANCE); - if (emitter.data.particles[0].velocity.y != 0) - EXPECT_NEAR(std::abs(emitter.data.particles[0].position.y), - emitter.data.boundary.width / 2, TOLERANCE); + EXPECT_NEAR(emitter.particles[0].velocity.x, 0, TOLERANCE); + EXPECT_NEAR(emitter.particles[0].velocity.y, 0, TOLERANCE); + if (emitter.particles[0].velocity.x != 0) + EXPECT_NEAR( + std::abs(emitter.particles[0].position.x), emitter.data.boundary.height / 2, + TOLERANCE + ); + if (emitter.particles[0].velocity.y != 0) + EXPECT_NEAR( + std::abs(emitter.particles[0].position.y), emitter.data.boundary.width / 2, + TOLERANCE + ); } diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 1e37c26..85eb6d5 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -1,8 +1,10 @@ -#include <crepe/ComponentManager.h> #include <crepe/api/Config.h> #include <crepe/api/GameObject.h> #include <crepe/api/Rigidbody.h> #include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/LoopTimerManager.h> +#include <crepe/manager/Mediator.h> #include <crepe/system/PhysicsSystem.h> #include <gtest/gtest.h> @@ -11,25 +13,26 @@ using namespace std::chrono_literals; using namespace crepe; class PhysicsTest : public ::testing::Test { + Mediator m; + public: - ComponentManager component_manager; - PhysicsSystem system{component_manager}; + ComponentManager component_manager {m}; + PhysicsSystem system {m}; + LoopTimerManager loop_timer {m}; void SetUp() override { ComponentManager & mgr = this->component_manager; vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); if (transforms.empty()) { - auto entity = mgr.new_object("", "", Vector2{0, 0}, 0, 0); - entity.add_component<Rigidbody>(Rigidbody::Data{ + auto entity = mgr.new_object("", "", vec2 {0, 0}, 0, 0); + entity.add_component<Rigidbody>(Rigidbody::Data { .mass = 1, .gravity_scale = 1, .body_type = Rigidbody::BodyType::DYNAMIC, - .max_linear_velocity = Vector2{10, 10}, + .max_linear_velocity = 10, .max_angular_velocity = 10, .constraints = {0, 0}, - .use_gravity = true, - .bounce = false, }); } transforms = mgr.get_components_by_id<Transform>(0); @@ -54,40 +57,41 @@ TEST_F(PhysicsTest, gravity) { ASSERT_FALSE(transforms.empty()); EXPECT_EQ(transform.position.y, 0); - system.update(); - EXPECT_EQ(transform.position.y, 1); + system.fixed_update(); + EXPECT_NEAR(transform.position.y, 0.0004, 0.0001); - system.update(); - EXPECT_EQ(transform.position.y, 3); + system.fixed_update(); + EXPECT_NEAR(transform.position.y, 0.002, 0.001); } TEST_F(PhysicsTest, max_velocity) { ComponentManager & mgr = this->component_manager; vector<reference_wrapper<Rigidbody>> rigidbodies = mgr.get_components_by_id<Rigidbody>(0); Rigidbody & rigidbody = rigidbodies.front().get(); + rigidbody.data.gravity_scale = 0; ASSERT_FALSE(rigidbodies.empty()); EXPECT_EQ(rigidbody.data.linear_velocity.y, 0); rigidbody.add_force_linear({100, 100}); rigidbody.add_force_angular(100); - system.update(); - EXPECT_EQ(rigidbody.data.linear_velocity.y, 10); - EXPECT_EQ(rigidbody.data.linear_velocity.x, 10); + system.fixed_update(); + EXPECT_NEAR(rigidbody.data.linear_velocity.y, 7.07, 0.01); + EXPECT_NEAR(rigidbody.data.linear_velocity.x, 7.07, 0.01); EXPECT_EQ(rigidbody.data.angular_velocity, 10); rigidbody.add_force_linear({-100, -100}); rigidbody.add_force_angular(-100); - system.update(); - EXPECT_EQ(rigidbody.data.linear_velocity.y, -10); - EXPECT_EQ(rigidbody.data.linear_velocity.x, -10); + system.fixed_update(); + EXPECT_NEAR(rigidbody.data.linear_velocity.y, -7.07, 0.01); + EXPECT_NEAR(rigidbody.data.linear_velocity.x, -7.07, 0.01); EXPECT_EQ(rigidbody.data.angular_velocity, -10); } TEST_F(PhysicsTest, movement) { - Config::get_instance().physics.gravity = 0; ComponentManager & mgr = this->component_manager; vector<reference_wrapper<Rigidbody>> rigidbodies = mgr.get_components_by_id<Rigidbody>(0); Rigidbody & rigidbody = rigidbodies.front().get(); + rigidbody.data.gravity_scale = 0; vector<reference_wrapper<Transform>> transforms = mgr.get_components_by_id<Transform>(0); const Transform & transform = transforms.front().get(); ASSERT_FALSE(rigidbodies.empty()); @@ -95,32 +99,34 @@ TEST_F(PhysicsTest, movement) { rigidbody.add_force_linear({1, 1}); rigidbody.add_force_angular(1); - system.update(); - EXPECT_EQ(transform.position.x, 1); - EXPECT_EQ(transform.position.y, 1); - EXPECT_EQ(transform.rotation, 1); + system.fixed_update(); + EXPECT_NEAR(transform.position.x, 0.02, 0.001); + EXPECT_NEAR(transform.position.y, 0.02, 0.001); + EXPECT_NEAR(transform.rotation, 0.02, 0.001); rigidbody.data.constraints = {1, 1, 1}; - EXPECT_EQ(transform.position.x, 1); - EXPECT_EQ(transform.position.y, 1); - EXPECT_EQ(transform.rotation, 1); - - rigidbody.data.linear_damping.x = 0.5; - rigidbody.data.linear_damping.y = 0.5; - rigidbody.data.angular_damping = 0.5; - system.update(); - EXPECT_EQ(rigidbody.data.linear_velocity.x, 0.5); - EXPECT_EQ(rigidbody.data.linear_velocity.y, 0.5); - EXPECT_EQ(rigidbody.data.angular_velocity, 0.5); + EXPECT_NEAR(transform.position.x, 0.02, 0.001); + EXPECT_NEAR(transform.position.y, 0.02, 0.001); + EXPECT_NEAR(transform.rotation, 0.02, 0.001); + rigidbody.data.constraints = {0, 0, 0}; + rigidbody.data.linear_velocity_coefficient.x = 0.5; + rigidbody.data.linear_velocity_coefficient.y = 0.5; + rigidbody.data.angular_velocity_coefficient = 0.5; + system.fixed_update(); + EXPECT_NEAR(rigidbody.data.linear_velocity.x, 0.98, 0.01); + EXPECT_NEAR(rigidbody.data.linear_velocity.y, 0.98, 0.01); + EXPECT_NEAR(rigidbody.data.angular_velocity, 0.98, 0.01); rigidbody.data.constraints = {1, 1, 0}; - rigidbody.data.angular_damping = 0; + rigidbody.data.angular_velocity_coefficient = 0; rigidbody.data.max_angular_velocity = 1000; rigidbody.data.angular_velocity = 360; - system.update(); - EXPECT_EQ(transform.rotation, 1); + system.fixed_update(); + EXPECT_NEAR(transform.rotation, 7.24, 0.01); rigidbody.data.angular_velocity = -360; - system.update(); - EXPECT_EQ(transform.rotation, 1); + system.fixed_update(); + EXPECT_NEAR(transform.rotation, 0.04, 0.001); + system.fixed_update(); + EXPECT_NEAR(transform.rotation, 352.84, 0.01); } diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp new file mode 100644 index 0000000..d8bd09d --- /dev/null +++ b/src/test/Profiling.cpp @@ -0,0 +1,262 @@ +#include <chrono> +#include <cmath> +#include <crepe/api/Asset.h> +#include <crepe/manager/Mediator.h> +#include <crepe/manager/ResourceManager.h> +#include <crepe/system/ParticleSystem.h> +#include <crepe/system/PhysicsSystem.h> +#include <crepe/system/RenderSystem.h> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/ParticleEmitter.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/facade/SDLContext.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.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.self.transform.game_object_id); + return true; + } + void init() { + subscribe<CollisionEvent>([this](const CollisionEvent & ev) -> bool { + return this->oncollision(ev); + }); + } + void fixed_update() { + // Retrieve component from the same GameObject this script is on + } +}; + +class DISABLED_ProfilingTest : public Test { +public: + // Config for test + // Minimum amount to let test pass + const int min_gameobject_count = 100; + // Maximum amount to stop test + const int max_gameobject_count = 3000; + // Amount of times a test runs to calculate average + const int average = 5; + // Maximum duration to stop test + const std::chrono::microseconds duration = 16000us; + + Mediator m; + SDLContext sdl_context {m}; + ResourceManager resman {m}; + ComponentManager mgr {m}; + // Add system used for profling tests + EventManager evmgr {m}; + LoopTimerManager loopmgr {m}; + CollisionSystem collision_sys {m}; + PhysicsSystem physics_sys {m}; + ParticleSystem particle_sys {m}; + RenderSystem render_sys {m}; + ScriptSystem script_sys {m}; + + // Test data + std::map<std::string, std::chrono::microseconds> timings; + int game_object_count = 0; + std::chrono::microseconds total_time = 0us; + + void SetUp() override { + + GameObject do_not_use = mgr.new_object("DO_NOT_USE", "", {0, 0}); + do_not_use.add_component<Camera>( + ivec2 {1080, 720}, vec2 {2000, 2000}, + Camera::Data { + .bg_color = Color::WHITE, + .zoom = 1.0f, + } + ); + // initialize systems here: + //calls init + script_sys.fixed_update(); + //creates window + render_sys.frame_update(); + } + + // Helper function to time an update call and store its duration + template <typename Func> + std::chrono::microseconds time_function(const std::string & name, Func && func) { + auto start = std::chrono::steady_clock::now(); + func(); + auto end = std::chrono::steady_clock::now(); + std::chrono::microseconds duration + = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + timings[name] += duration; + return duration; + } + + // Run and profile all systems, return the total time in milliseconds + std::chrono::microseconds run_all_systems() { + std::chrono::microseconds total_microseconds = 0us; + total_microseconds + += time_function("PhysicsSystem", [&]() { physics_sys.fixed_update(); }); + total_microseconds + += time_function("CollisionSystem", [&]() { collision_sys.fixed_update(); }); + total_microseconds + += time_function("ParticleSystem", [&]() { particle_sys.fixed_update(); }); + total_microseconds + += time_function("RenderSystem", [&]() { render_sys.frame_update(); }); + return total_microseconds; + } + + // Print timings of all functions + void log_timings() const { + std::string result = "\nFunction timings:\n"; + + for (const auto & [name, duration] : timings) { + result += name + " took " + std::to_string(duration.count() / 1000.0 / average) + + " ms (" + std::to_string(duration.count() / average) + " µs).\n"; + } + + result += "Total time: " + std::to_string(this->total_time.count() / 1000.0 / average) + + " ms (" + std::to_string(this->total_time.count() / average) + " µs)\n"; + + result += "Amount of gameobjects: " + std::to_string(game_object_count) + "\n"; + + GTEST_LOG_(INFO) << result; + } + + void clear_timings() { + for (auto & [key, value] : timings) { + value = std::chrono::microseconds(0); + } + } +}; + +TEST_F(DISABLED_ProfilingTest, Profiling_1) { + while (this->total_time / this->average < this->duration) { + + { + //define gameobject used for testing + GameObject gameobject = mgr.new_object("gameobject", "", {0, 0}); + } + + this->game_object_count++; + + this->total_time = 0us; + clear_timings(); + + for (int amount = 0; amount < this->average; amount++) { + this->total_time += run_all_systems(); + } + + if (this->game_object_count >= this->max_gameobject_count) break; + } + log_timings(); + EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} + +TEST_F(DISABLED_ProfilingTest, Profiling_2) { + while (this->total_time / this->average < this->duration) { + + { + //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 { + .gravity_scale = 0.0, + .body_type = Rigidbody::BodyType::STATIC, + }); + gameobject.add_component<BoxCollider>(vec2 {0, 0}, vec2 {1, 1}); + + gameobject.add_component<BehaviorScript>().set_script<TestScript>(); + Sprite & test_sprite = gameobject.add_component<Sprite>( + Asset {"asset/texture/square.png"}, + Sprite::Data { + .color = {0, 0, 0, 0}, + .flip = {.flip_x = false, .flip_y = false}, + .sorting_in_layer = 1, + .order_in_layer = 1, + .size = {.y = 500}, + } + ); + } + + this->game_object_count++; + + this->total_time = 0us; + clear_timings(); + for (int amount = 0; amount < this->average; amount++) { + this->total_time += run_all_systems(); + } + + if (this->game_object_count >= this->max_gameobject_count) break; + } + log_timings(); + EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} + +TEST_F(DISABLED_ProfilingTest, Profiling_3) { + while (this->total_time / this->average < this->duration) { + + { + //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 { + .gravity_scale = 0, + .body_type = Rigidbody::BodyType::STATIC, + }); + gameobject.add_component<BoxCollider>(vec2 {0, 0}, vec2 {1, 1}); + gameobject.add_component<BehaviorScript>().set_script<TestScript>(); + Sprite & test_sprite = gameobject.add_component<Sprite>( + Asset {"asset/texture/square.png"}, + Sprite::Data { + .color = {0, 0, 0, 0}, + .flip = {.flip_x = false, .flip_y = false}, + .sorting_in_layer = 1, + .order_in_layer = 1, + .size = {.y = 500}, + } + ); + auto & test = gameobject.add_component<ParticleEmitter>( + test_sprite, + ParticleEmitter::Data { + .max_particles = 10, + .emission_rate = 100, + .end_lifespan = 100000, + .boundary { + .width = 1000, + .height = 1000, + .offset = vec2 {0, 0}, + .reset_on_exit = false, + }, + + } + ); + } + render_sys.frame_update(); + this->game_object_count++; + + this->total_time = 0us; + clear_timings(); + for (int amount = 0; amount < this->average; amount++) { + this->total_time += run_all_systems(); + } + + if (this->game_object_count >= this->max_gameobject_count) break; + } + log_timings(); + EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index ac479d3..bdd87ee 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -1,4 +1,7 @@ -#include "api/Camera.h" +#include "api/Asset.h" +#include "facade/SDLContext.h" +#include "manager/ResourceManager.h" +#include "types.h" #include <functional> #include <gtest/gtest.h> #include <memory> @@ -7,11 +10,11 @@ #define private public #define protected public -#include <crepe/ComponentManager.h> +#include <crepe/api/Camera.h> #include <crepe/api/Color.h> #include <crepe/api/GameObject.h> #include <crepe/api/Sprite.h> -#include <crepe/api/Texture.h> +#include <crepe/manager/ComponentManager.h> #include <crepe/system/RenderSystem.h> @@ -20,65 +23,77 @@ using namespace crepe; using namespace testing; class RenderSystemTest : public Test { + Mediator m; + public: - ComponentManager mgr{}; - RenderSystem sys{mgr}; + ComponentManager mgr {m}; + SDLContext ctx {m}; + ResourceManager resource_manager {m}; + RenderSystem sys {m}; GameObject entity1 = this->mgr.new_object("name"); GameObject entity2 = this->mgr.new_object("name"); GameObject entity3 = this->mgr.new_object("name"); GameObject entity4 = this->mgr.new_object("name"); void SetUp() override { - auto & sprite1 - = entity1.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), - Color(0, 0, 0, 0), FlipSettings{false, false}); - ASSERT_NE(sprite1.sprite_image.get(), nullptr); - sprite1.order_in_layer = 5; - sprite1.sorting_in_layer = 5; - EXPECT_EQ(sprite1.order_in_layer, 5); - EXPECT_EQ(sprite1.sorting_in_layer, 5); - auto & sprite2 - = entity2.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), - Color(0, 0, 0, 0), FlipSettings{false, false}); - ASSERT_NE(sprite2.sprite_image.get(), nullptr); - sprite2.sorting_in_layer = 2; - sprite2.order_in_layer = 1; - - EXPECT_EQ(sprite2.sorting_in_layer, 2); - EXPECT_EQ(sprite2.order_in_layer, 1); - - auto & sprite3 - = entity3.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), - Color(0, 0, 0, 0), FlipSettings{false, false}); - ASSERT_NE(sprite3.sprite_image.get(), nullptr); - sprite3.sorting_in_layer = 1; - sprite3.order_in_layer = 2; - - EXPECT_EQ(sprite3.sorting_in_layer, 1); - EXPECT_EQ(sprite3.order_in_layer, 2); - - auto & sprite4 - = entity4.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), - Color(0, 0, 0, 0), FlipSettings{false, false}); - ASSERT_NE(sprite4.sprite_image.get(), nullptr); - sprite4.sorting_in_layer = 1; - sprite4.order_in_layer = 1; - EXPECT_EQ(sprite4.sorting_in_layer, 1); - EXPECT_EQ(sprite4.order_in_layer, 1); + auto s1 = Asset("asset/texture/img.png"); + auto s2 = Asset("asset/texture/img.png"); + auto s3 = Asset("asset/texture/img.png"); + auto s4 = Asset("asset/texture/img.png"); + auto & sprite1 = entity1.add_component<Sprite>( + s1, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 5, + .order_in_layer = 5, + .size = {10, 10}, + } + ); + + EXPECT_EQ(sprite1.data.order_in_layer, 5); + EXPECT_EQ(sprite1.data.sorting_in_layer, 5); + auto & sprite2 = entity2.add_component<Sprite>( + s2, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 2, + .order_in_layer = 1, + } + ); + EXPECT_EQ(sprite2.data.sorting_in_layer, 2); + EXPECT_EQ(sprite2.data.order_in_layer, 1); + + auto & sprite3 = entity3.add_component<Sprite>( + s3, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 1, + .order_in_layer = 2, + } + ); + EXPECT_EQ(sprite3.data.sorting_in_layer, 1); + EXPECT_EQ(sprite3.data.order_in_layer, 2); + + auto & sprite4 = entity4.add_component<Sprite>( + s4, + Sprite::Data { + .color = Color(0, 0, 0, 0), + .flip = Sprite::FlipSettings {false, false}, + .sorting_in_layer = 1, + .order_in_layer = 1, + } + ); + EXPECT_EQ(sprite4.data.sorting_in_layer, 1); + EXPECT_EQ(sprite4.data.order_in_layer, 1); } }; -TEST_F(RenderSystemTest, expected_throws) { - GameObject entity1 = this->mgr.new_object("NAME"); - - // no texture img - EXPECT_ANY_THROW({ - entity1.add_component<Sprite>(make_shared<Texture>("NO_IMAGE"), Color(0, 0, 0, 0), - FlipSettings{false, false}); - }); - +TEST_F(RenderSystemTest, NoCamera) { // No camera - EXPECT_ANY_THROW({ this->sys.update(); }); + EXPECT_ANY_THROW({ this->sys.frame_update(); }); } TEST_F(RenderSystemTest, make_sprites) {} @@ -96,32 +111,35 @@ TEST_F(RenderSystemTest, sorting_sprites) { // 3. sorting_in_layer: 2, order_in_layer: 1 (entity2) // 4. sorting_in_layer: 5, order_in_layer: 5 (entity1) - EXPECT_EQ(sorted_sprites[0].get().sorting_in_layer, 1); - EXPECT_EQ(sorted_sprites[0].get().order_in_layer, 1); + EXPECT_EQ(sorted_sprites[0].get().data.sorting_in_layer, 1); + EXPECT_EQ(sorted_sprites[0].get().data.order_in_layer, 1); - EXPECT_EQ(sorted_sprites[1].get().sorting_in_layer, 1); - EXPECT_EQ(sorted_sprites[1].get().order_in_layer, 2); + EXPECT_EQ(sorted_sprites[1].get().data.sorting_in_layer, 1); + EXPECT_EQ(sorted_sprites[1].get().data.order_in_layer, 2); - EXPECT_EQ(sorted_sprites[2].get().sorting_in_layer, 2); - EXPECT_EQ(sorted_sprites[2].get().order_in_layer, 1); + EXPECT_EQ(sorted_sprites[2].get().data.sorting_in_layer, 2); + EXPECT_EQ(sorted_sprites[2].get().data.order_in_layer, 1); - EXPECT_EQ(sorted_sprites[3].get().sorting_in_layer, 5); - EXPECT_EQ(sorted_sprites[3].get().order_in_layer, 5); + EXPECT_EQ(sorted_sprites[3].get().data.sorting_in_layer, 5); + EXPECT_EQ(sorted_sprites[3].get().data.order_in_layer, 5); for (size_t i = 1; i < sorted_sprites.size(); ++i) { const Sprite & prev = sorted_sprites[i - 1].get(); const Sprite & curr = sorted_sprites[i].get(); - if (prev.sorting_in_layer == curr.sorting_in_layer) { - EXPECT_LE(prev.order_in_layer, curr.order_in_layer); + if (prev.data.sorting_in_layer == curr.data.sorting_in_layer) { + EXPECT_LE(prev.data.order_in_layer, curr.data.order_in_layer); } else { - EXPECT_LE(prev.sorting_in_layer, curr.sorting_in_layer); + EXPECT_LE(prev.data.sorting_in_layer, curr.data.sorting_in_layer); } } } TEST_F(RenderSystemTest, Update) { - entity1.add_component<Camera>(Color::WHITE); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); { vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>(); ASSERT_EQ(sprites.size(), 4); @@ -131,7 +149,7 @@ TEST_F(RenderSystemTest, Update) { EXPECT_EQ(sprites[2].get().game_object_id, 2); EXPECT_EQ(sprites[3].get().game_object_id, 3); } - this->sys.update(); + this->sys.frame_update(); { vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>(); ASSERT_EQ(sprites.size(), 4); @@ -149,7 +167,11 @@ TEST_F(RenderSystemTest, Camera) { EXPECT_NE(cameras.size(), 1); } { - entity1.add_component<Camera>(Color::WHITE); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); + auto cameras = this->mgr.get_components_by_type<Camera>(); EXPECT_EQ(cameras.size(), 1); } @@ -157,18 +179,22 @@ TEST_F(RenderSystemTest, Camera) { //TODO improve with newer version } TEST_F(RenderSystemTest, Color) { - entity1.add_component<Camera>(Color::WHITE); + entity1.add_component<Camera>( + ivec2 {100, 100}, vec2 {100, 100}, + Camera::Data {.bg_color = Color::WHITE, .zoom = 1.0f} + ); + auto & sprite = this->mgr.get_components_by_id<Sprite>(entity1.id).front().get(); - ASSERT_NE(sprite.sprite_image.get(), nullptr); - - sprite.color = Color::GREEN; - EXPECT_EQ(sprite.color.r, Color::GREEN.r); - EXPECT_EQ(sprite.color.g, Color::GREEN.g); - EXPECT_EQ(sprite.color.b, Color::GREEN.b); - EXPECT_EQ(sprite.color.a, Color::GREEN.a); - this->sys.update(); - EXPECT_EQ(sprite.color.r, Color::GREEN.r); - EXPECT_EQ(sprite.color.g, Color::GREEN.g); - EXPECT_EQ(sprite.color.b, Color::GREEN.b); - EXPECT_EQ(sprite.color.a, Color::GREEN.a); + //ASSERT_NE(sprite.texture.texture.get(), nullptr); + + sprite.data.color = Color::GREEN; + EXPECT_EQ(sprite.data.color.r, Color::GREEN.r); + EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); + EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); + EXPECT_EQ(sprite.data.color.a, Color::GREEN.a); + this->sys.frame_update(); + EXPECT_EQ(sprite.data.color.r, Color::GREEN.r); + EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); + EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); + EXPECT_EQ(sprite.data.color.a, Color::GREEN.a); } diff --git a/src/test/ReplayManagerTest.cpp b/src/test/ReplayManagerTest.cpp new file mode 100644 index 0000000..b2619eb --- /dev/null +++ b/src/test/ReplayManagerTest.cpp @@ -0,0 +1,38 @@ +#include <gtest/gtest.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Scene.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ReplayManager.h> +#include <crepe/system/ReplaySystem.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ReplayManagerTest : public Test { + Mediator mediator; + +public: + ComponentManager component_manager {mediator}; + ReplayManager replay_manager {mediator}; + ReplaySystem replay_system {mediator}; + + GameObject entity = component_manager.new_object("foo"); + Transform & entity_transform + = component_manager.get_components_by_id<Transform>(entity.id).back(); + Metadata & entity_metadata + = component_manager.get_components_by_id<Metadata>(entity.id).back(); +}; + +TEST_F(ReplayManagerTest, Default) { + // replay_manager.record_start(); + + // replay_system.fixed_update(); + // entity_transform.position += {1, 1}; + // replay_system.fixed_update(); + // entity_transform.position += {1, 1}; + // replay_system.fixed_update(); + + // recording_t recording = replay_manager.record_end(); +} diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp new file mode 100644 index 0000000..e5a7fad --- /dev/null +++ b/src/test/ResourceManagerTest.cpp @@ -0,0 +1,84 @@ +#include "manager/Mediator.h" +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/api/GameObject.h> +#include <crepe/manager/ResourceManager.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ResourceManagerTest : public Test { + Mediator mediator; + +public: + ResourceManager resource_manager {mediator}; + + class Unrelated : public Resource { + using Resource::Resource; + }; + + Asset asset_a {"asset/texture/img.png"}; + Asset asset_b {"asset/texture/ERROR.png"}; + + class TestResource : public Resource { + public: + static unsigned instances; + + public: + const unsigned instance; + TestResource(const Asset & src, Mediator & mediator) + : Resource(src, mediator), + instance(this->instances++) {} + ~TestResource() { this->instances--; } + bool operator==(const TestResource & other) const { + return this->instance == other.instance; + } + }; + +private: + void SetUp() override { TestResource::instances = 0; } +}; +unsigned ResourceManagerTest::TestResource::instances = 0; + +TEST_F(ResourceManagerTest, Default) { + TestResource & res_1 = resource_manager.get<TestResource>(asset_a); + TestResource & res_2 = resource_manager.get<TestResource>(asset_a); + TestResource & res_3 = resource_manager.get<TestResource>(asset_b); + TestResource & res_4 = resource_manager.get<TestResource>(asset_b); + + ASSERT_EQ(res_1, res_2); + ASSERT_EQ(res_3, res_4); + EXPECT_NE(res_1, res_3); + + EXPECT_EQ(TestResource::instances, 2); + + resource_manager.clear(); +} + +TEST_F(ResourceManagerTest, Persistent) { + resource_manager.set_persistent(asset_a, true); + EXPECT_EQ(TestResource::instances, 0); + + resource_manager.get<TestResource>(asset_a); + resource_manager.get<TestResource>(asset_a); + resource_manager.get<TestResource>(asset_b); + resource_manager.get<TestResource>(asset_b); + EXPECT_EQ(TestResource::instances, 2); + + resource_manager.clear(); + EXPECT_EQ(TestResource::instances, 1); + + resource_manager.clear_all(); + EXPECT_EQ(TestResource::instances, 0); +} + +TEST_F(ResourceManagerTest, UnmatchedType) { + EXPECT_NO_THROW({ resource_manager.get<TestResource>(asset_a); }); + + EXPECT_THROW({ resource_manager.get<Unrelated>(asset_a); }, runtime_error); +} diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp new file mode 100644 index 0000000..fd53200 --- /dev/null +++ b/src/test/SaveManagerTest.cpp @@ -0,0 +1,51 @@ +#include <gtest/gtest.h> + +#include <crepe/ValueBroker.h> +#include <crepe/facade/DB.h> +#include <crepe/manager/SaveManager.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class SaveManagerTest : public Test { + Mediator m; + class TestSaveManager : public SaveManager { + using SaveManager::SaveManager; + + // in-memory database for testing + DB db {}; + virtual DB & get_db() override { return this->db; } + }; + +public: + TestSaveManager mgr {m}; +}; + +TEST_F(SaveManagerTest, ReadWrite) { + ASSERT_FALSE(mgr.has("foo")); + mgr.set<string>("foo", "bar"); + ASSERT_TRUE(mgr.has("foo")); + + string value = mgr.get<string>("foo"); + EXPECT_EQ(value, "bar"); +} + +TEST_F(SaveManagerTest, DefaultValue) { + ValueBroker value = mgr.get<int>("foo", 3); + + ASSERT_EQ(value.get(), 3); + value.set(5); + EXPECT_EQ(value.get(), 5); +} + +TEST_F(SaveManagerTest, MultipleKeys) { + ValueBroker foo = mgr.get<int>("foo", 1); + ValueBroker bar = mgr.get<int>("bar", 2); + + EXPECT_EQ(foo.get(), 1); + EXPECT_EQ(bar.get(), 2); + + EXPECT_EQ(mgr.get<int>("foo"), 1); + EXPECT_EQ(mgr.get<int>("bar"), 2); +} diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp index 1efcfb2..e58ce36 100644 --- a/src/test/SceneManagerTest.cpp +++ b/src/test/SceneManagerTest.cpp @@ -1,24 +1,23 @@ -#include <crepe/ComponentManager.h> +#include <gtest/gtest.h> + #include <crepe/api/GameObject.h> #include <crepe/api/Metadata.h> #include <crepe/api/Scene.h> -#include <crepe/api/SceneManager.h> #include <crepe/api/Transform.h> #include <crepe/api/Vector2.h> -#include <gtest/gtest.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/SceneManager.h> +#include <crepe/types.h> using namespace std; using namespace crepe; class ConcreteScene1 : public Scene { public: - using Scene::Scene; - void load_scene() { - auto & mgr = this->component_manager; - GameObject object1 = mgr.new_object("scene_1", "tag_scene_1", Vector2{0, 0}, 0, 1); - GameObject object2 = mgr.new_object("scene_1", "tag_scene_1", Vector2{1, 0}, 0, 1); - GameObject object3 = mgr.new_object("scene_1", "tag_scene_1", Vector2{2, 0}, 0, 1); + GameObject object1 = new_object("scene_1", "tag_scene_1", vec2 {0, 0}, 0, 1); + GameObject object2 = new_object("scene_1", "tag_scene_1", vec2 {1, 0}, 0, 1); + GameObject object3 = new_object("scene_1", "tag_scene_1", vec2 {2, 0}, 0, 1); } string get_name() const { return "scene1"; } @@ -26,23 +25,36 @@ public: class ConcreteScene2 : public Scene { public: - using Scene::Scene; - void load_scene() { - auto & mgr = this->component_manager; - GameObject object1 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 0}, 0, 1); - GameObject object2 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 1}, 0, 1); - GameObject object3 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 2}, 0, 1); - GameObject object4 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 3}, 0, 1); + GameObject object1 = new_object("scene_2", "tag_scene_2", vec2 {0, 0}, 0, 1); + GameObject object2 = new_object("scene_2", "tag_scene_2", vec2 {0, 1}, 0, 1); + GameObject object3 = new_object("scene_2", "tag_scene_2", vec2 {0, 2}, 0, 1); + GameObject object4 = new_object("scene_2", "tag_scene_2", vec2 {0, 3}, 0, 1); } string get_name() const { return "scene2"; } }; +class ConcreteScene3 : public Scene { +public: + ConcreteScene3(const string & name) : name(name) {} + + void load_scene() { + GameObject object1 = new_object("scene_3", "tag_scene_3", vec2 {0, 0}, 0, 1); + } + + string get_name() const { return name; } + +private: + const string name; +}; + class SceneManagerTest : public ::testing::Test { + Mediator m; + public: - ComponentManager component_mgr{}; - SceneManager scene_mgr{component_mgr}; + ComponentManager component_mgr {m}; + SceneManager scene_mgr {m}; }; TEST_F(SceneManagerTest, loadScene) { @@ -124,3 +136,25 @@ TEST_F(SceneManagerTest, loadScene) { EXPECT_EQ(transform[3].get().position.x, 0); EXPECT_EQ(transform[3].get().position.y, 3); } + +TEST_F(SceneManagerTest, perfectForwarding) { + scene_mgr.add_scene<ConcreteScene3>("scene3"); + + scene_mgr.load_next_scene(); + + vector<reference_wrapper<Metadata>> metadata + = component_mgr.get_components_by_type<Metadata>(); + vector<reference_wrapper<Transform>> transform + = component_mgr.get_components_by_type<Transform>(); + + EXPECT_EQ(metadata.size(), 1); + EXPECT_EQ(transform.size(), 1); + + EXPECT_EQ(metadata[0].get().game_object_id, 0); + EXPECT_EQ(metadata[0].get().name, "scene_3"); + EXPECT_EQ(metadata[0].get().tag, "tag_scene_3"); + EXPECT_EQ(metadata[0].get().parent, -1); + EXPECT_EQ(metadata[0].get().children.size(), 0); + EXPECT_EQ(transform[0].get().position.x, 0); + EXPECT_EQ(transform[0].get().position.y, 0); +} diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp new file mode 100644 index 0000000..1ec33ba --- /dev/null +++ b/src/test/ScriptECSTest.cpp @@ -0,0 +1,41 @@ +#include <gtest/gtest.h> + +#define protected public + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Metadata.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/system/ScriptSystem.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptECSTest : public ScriptTest { +public: + class TestComponent : public Component { + using Component::Component; + }; +}; + +TEST_F(ScriptECSTest, GetOwnComponent) { + MyScript & script = this->script; + Metadata & metadata = script.get_component<Metadata>(); + + EXPECT_EQ(metadata.name, OBJ_NAME); +} + +TEST_F(ScriptECSTest, GetOwnComponents) { + const unsigned COUNT = 4; + + for (unsigned i = 0; i < COUNT; i++) entity.add_component<TestComponent>(); + + MyScript & script = this->script; + RefVector<TestComponent> components = script.get_components<TestComponent>(); + + EXPECT_EQ(components.size(), COUNT); +} diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp new file mode 100644 index 0000000..8b4a72d --- /dev/null +++ b/src/test/ScriptEventTest.cpp @@ -0,0 +1,50 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Script.h> +#include <crepe/api/Vector2.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/system/ScriptSystem.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptEventTest : public ScriptTest { +public: + EventManager & event_manager = mediator.event_manager; + + struct MyEvent : public Event {}; +}; + +TEST_F(ScriptEventTest, Default) { + BehaviorScript & behaviorscript = this->behaviorscript; + MyScript & script = this->script; + EventManager & evmgr = this->event_manager; + + unsigned event_count = 0; + script.subscribe<MyEvent>([&](const MyEvent &) { + event_count++; + return true; + }); + + system.fixed_update(); + behaviorscript.active = false; + EXPECT_EQ(0, event_count); + + evmgr.trigger_event<MyEvent>(); + EXPECT_EQ(0, event_count); + + behaviorscript.active = true; + evmgr.trigger_event<MyEvent>(); + EXPECT_EQ(1, event_count); +} diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp new file mode 100644 index 0000000..e2debae --- /dev/null +++ b/src/test/ScriptSaveManagerTest.cpp @@ -0,0 +1,35 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include <crepe/facade/DB.h> +#include <crepe/manager/SaveManager.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptSaveManagerTest : public ScriptTest { +public: + class TestSaveManager : public SaveManager { + using SaveManager::SaveManager; + + // in-memory database for testing + DB db {}; + virtual DB & get_db() override { return this->db; } + }; + + TestSaveManager save_mgr {mediator}; +}; + +TEST_F(ScriptSaveManagerTest, GetSaveManager) { + MyScript & script = this->script; + + SaveManager & mgr = script.get_save_manager(); + + EXPECT_EQ(&mgr, &save_mgr); +} diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp new file mode 100644 index 0000000..7d01f14 --- /dev/null +++ b/src/test/ScriptSceneTest.cpp @@ -0,0 +1,30 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include "ScriptTest.h" +#include <crepe/manager/SceneManager.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptSceneTest : public ScriptTest { +public: + SceneManager scene_manager {mediator}; + + class MyScene : public Scene {}; +}; + +TEST_F(ScriptSceneTest, Default) { + BehaviorScript & behaviorscript = this->behaviorscript; + MyScript & script = this->script; + + const char * non_default_value = "foo"; + ASSERT_NE(non_default_value, scene_manager.next_scene); + + script.set_next_scene(non_default_value); + EXPECT_EQ(non_default_value, scene_manager.next_scene); +} diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 78d5061..40aa25c 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -1,129 +1,95 @@ +#include <gmock/gmock.h> #include <gtest/gtest.h> // stupid hack to allow access to private/protected members under test #define private public #define protected public -#include <crepe/ComponentManager.h> -#include <crepe/api/BehaviorScript.h> -#include <crepe/api/Event.h> -#include <crepe/api/EventManager.h> -#include <crepe/api/GameObject.h> -#include <crepe/api/Script.h> -#include <crepe/api/Vector2.h> -#include <crepe/system/ScriptSystem.h> +#include "ScriptTest.h" using namespace std; using namespace crepe; using namespace testing; -class MyEvent : public Event {}; - -class ScriptTest : public Test { -public: - ComponentManager component_manager{}; - ScriptSystem system{component_manager}; - EventManager & event_manager = EventManager::get_instance(); - - class MyScript : public Script { - // NOTE: default (private) visibility of init and update shouldn't cause - // issues! - void init() { - this->init_count++; - - subscribe<MyEvent>([this](const MyEvent &) { - this->event_count++; - return true; - }); - - // init should never be called more than once - EXPECT_LE(this->init_count, 1); - } - void update() { this->update_count++; } - - public: - unsigned init_count = 0; - unsigned update_count = 0; - unsigned event_count = 0; - }; - - OptionalRef<BehaviorScript> behaviorscript; - OptionalRef<MyScript> script; - - void SetUp() override { - auto & mgr = this->component_manager; - GameObject entity = mgr.new_object("name"); - BehaviorScript & component = entity.add_component<BehaviorScript>(); - - this->behaviorscript = component; - ASSERT_TRUE(this->behaviorscript); - EXPECT_EQ(component.script.get(), nullptr); - component.set_script<MyScript>(); - ASSERT_NE(component.script.get(), nullptr); - - this->script = *(MyScript *) component.script.get(); - ASSERT_TRUE(this->script); - - // sanity - MyScript & script = this->script; - ASSERT_EQ(script.init_count, 0); - ASSERT_EQ(script.update_count, 0); - ASSERT_EQ(script.event_count, 0); - } -}; +void ScriptTest::SetUp() { + auto & mgr = this->component_manager; + BehaviorScript & component = entity.add_component<BehaviorScript>(); + + this->behaviorscript = component; + ASSERT_TRUE(this->behaviorscript); + EXPECT_EQ(component.script.get(), nullptr); + component.set_script<NiceMock<MyScript>>(); + ASSERT_NE(component.script.get(), nullptr); + + this->script = *(MyScript *) component.script.get(); + ASSERT_TRUE(this->script); +} TEST_F(ScriptTest, Default) { MyScript & script = this->script; - EXPECT_EQ(0, script.init_count); - EXPECT_EQ(0, script.update_count); - EXPECT_EQ(0, script.event_count); + EXPECT_CALL(script, init()).Times(0); + EXPECT_CALL(script, fixed_update(_)).Times(0); + EXPECT_CALL(script, frame_update(_)).Times(0); } TEST_F(ScriptTest, UpdateOnce) { MyScript & script = this->script; - system.update(); - EXPECT_EQ(1, script.init_count); - EXPECT_EQ(1, script.update_count); - EXPECT_EQ(0, script.event_count); + { + InSequence seq; + + EXPECT_CALL(script, init()).Times(1); + EXPECT_CALL(script, fixed_update(_)).Times(1); + system.fixed_update(); + } + + { + InSequence seq; + + EXPECT_CALL(script, init()).Times(0); + EXPECT_CALL(script, fixed_update(_)).Times(1); + system.fixed_update(); + } + + { + InSequence seq; + + EXPECT_CALL(script, frame_update(_)).Times(1); + system.frame_update(); + } } TEST_F(ScriptTest, UpdateInactive) { BehaviorScript & behaviorscript = this->behaviorscript; MyScript & script = this->script; - behaviorscript.active = false; - system.update(); - EXPECT_EQ(0, script.init_count); - EXPECT_EQ(0, script.update_count); - EXPECT_EQ(0, script.event_count); - - behaviorscript.active = true; - system.update(); - EXPECT_EQ(1, script.init_count); - EXPECT_EQ(1, script.update_count); - EXPECT_EQ(0, script.event_count); + { + InSequence seq; + + EXPECT_CALL(script, init()).Times(0); + EXPECT_CALL(script, fixed_update(_)).Times(0); + behaviorscript.active = false; + system.fixed_update(); + } + + { + InSequence seq; + + EXPECT_CALL(script, init()).Times(1); + EXPECT_CALL(script, fixed_update(_)).Times(1); + behaviorscript.active = true; + system.fixed_update(); + } } -TEST_F(ScriptTest, EventInactive) { - BehaviorScript & behaviorscript = this->behaviorscript; +TEST_F(ScriptTest, SaveManager) { MyScript & script = this->script; - EventManager & evmgr = this->event_manager; - - system.update(); - behaviorscript.active = false; - EXPECT_EQ(1, script.init_count); - EXPECT_EQ(1, script.update_count); - EXPECT_EQ(0, script.event_count); - - evmgr.trigger_event<MyEvent>(); - EXPECT_EQ(1, script.init_count); - EXPECT_EQ(1, script.update_count); - EXPECT_EQ(0, script.event_count); - - behaviorscript.active = true; - evmgr.trigger_event<MyEvent>(); - EXPECT_EQ(1, script.init_count); - EXPECT_EQ(1, script.update_count); - EXPECT_EQ(1, script.event_count); + + EXPECT_EQ(&script.get_save_manager(), &this->save_manager); +} + +TEST_F(ScriptTest, LoopTimerManager) { + MyScript & script = this->script; + + EXPECT_EQ(&script.get_loop_timer(), &this->loop_timer); } diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h new file mode 100644 index 0000000..f953aab --- /dev/null +++ b/src/test/ScriptTest.h @@ -0,0 +1,40 @@ +#pragma once + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/LoopTimerManager.h> +#include <crepe/manager/SaveManager.h> +#include <crepe/system/ScriptSystem.h> + +class ScriptTest : public testing::Test { +protected: + crepe::Mediator mediator; + static constexpr const char * OBJ_NAME = "foo"; + +public: + crepe::ComponentManager component_manager {mediator}; + crepe::ScriptSystem system {mediator}; + crepe::EventManager event_mgr {mediator}; + crepe::LoopTimerManager loop_timer {mediator}; + crepe::SaveManager save_manager {mediator}; + crepe::GameObject entity = component_manager.new_object(OBJ_NAME); + + class MyScript : public crepe::Script { + // NOTE: explicitly stating `public:` is not required on actual scripts + + public: + MOCK_METHOD(void, init, (), (override)); + MOCK_METHOD(void, fixed_update, (crepe::duration_t), (override)); + MOCK_METHOD(void, frame_update, (crepe::duration_t), (override)); + }; + + crepe::OptionalRef<crepe::BehaviorScript> behaviorscript; + crepe::OptionalRef<MyScript> script; + + virtual void SetUp(); +}; diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp index e6bb058..5928c37 100644 --- a/src/test/ValueBrokerTest.cpp +++ b/src/test/ValueBrokerTest.cpp @@ -13,7 +13,7 @@ public: int write_count = 0; int value = 0; - ValueBroker<int> broker{ + ValueBroker<int> broker { [this](const int & target) -> void { this->write_count++; this->value = target; @@ -23,7 +23,7 @@ public: return this->value; }, }; - Proxy<int> proxy{broker}; + Proxy<int> proxy {broker}; void SetUp() override { ASSERT_EQ(read_count, 0); diff --git a/src/test/Vector2Test.cpp b/src/test/Vector2Test.cpp new file mode 100644 index 0000000..b17f95a --- /dev/null +++ b/src/test/Vector2Test.cpp @@ -0,0 +1,542 @@ +#include <gtest/gtest.h> + +#include <crepe/api/Vector2.h> +#include <crepe/types.h> + +using namespace crepe; + +class Vector2Test : public ::testing::Test { +public: + Vector2<int> int_vec1; + Vector2<int> int_vec2; + Vector2<double> double_vec1; + Vector2<double> double_vec2; + Vector2<long> long_vec1; + Vector2<long> long_vec2; + Vector2<float> float_vec1; + Vector2<float> float_vec2; + + void SetUp() override { + int_vec1 = {1, 2}; + int_vec2 = {3, 4}; + double_vec1 = {1.0, 2.0}; + double_vec2 = {3.0, 4.0}; + long_vec1 = {1, 2}; + long_vec2 = {3, 4}; + float_vec1 = {1.0f, 2.0f}; + float_vec2 = {3.0f, 4.0f}; + } +}; + +TEST_F(Vector2Test, Subtract) { + Vector2<int> result = int_vec1 - int_vec2; + EXPECT_EQ(result.x, -2); + EXPECT_EQ(result.y, -2); + + Vector2<double> result2 = double_vec1 - double_vec2; + EXPECT_FLOAT_EQ(result2.x, -2.0); + EXPECT_FLOAT_EQ(result2.y, -2.0); + + Vector2<long> result3 = long_vec1 - long_vec2; + EXPECT_EQ(result3.x, -2); + EXPECT_EQ(result3.y, -2); + + Vector2<float> result4 = float_vec1 - float_vec2; + EXPECT_FLOAT_EQ(result4.x, -2.0f); + EXPECT_FLOAT_EQ(result4.y, -2.0f); +} + +TEST_F(Vector2Test, SubtractScalar) { + Vector2<int> result = int_vec1 - 1; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 1); + + Vector2<double> result2 = double_vec1 - 1.0; + EXPECT_FLOAT_EQ(result2.x, 0.0); + EXPECT_FLOAT_EQ(result2.y, 1.0); + + Vector2<long> result3 = long_vec1 - 1; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 1); + + Vector2<float> result4 = float_vec1 - 1.0f; + EXPECT_FLOAT_EQ(result4.x, 0.0f); + EXPECT_FLOAT_EQ(result4.y, 1.0f); +} + +TEST_F(Vector2Test, Add) { + Vector2<int> result = int_vec1 + int_vec2; + EXPECT_EQ(result.x, 4); + EXPECT_EQ(result.y, 6); + + Vector2<double> result2 = double_vec1 + double_vec2; + EXPECT_FLOAT_EQ(result2.x, 4.0); + EXPECT_FLOAT_EQ(result2.y, 6.0); + + Vector2<long> result3 = long_vec1 + long_vec2; + EXPECT_EQ(result3.x, 4); + EXPECT_EQ(result3.y, 6); + + Vector2<float> result4 = float_vec1 + float_vec2; + EXPECT_FLOAT_EQ(result4.x, 4.0f); + EXPECT_FLOAT_EQ(result4.y, 6.0f); +} + +TEST_F(Vector2Test, AddScalar) { + Vector2<int> result = int_vec1 + 1; + EXPECT_EQ(result.x, 2); + EXPECT_EQ(result.y, 3); + + Vector2<double> result2 = double_vec1 + 1.0; + EXPECT_FLOAT_EQ(result2.x, 2.0); + EXPECT_FLOAT_EQ(result2.y, 3.0); + + Vector2<long> result3 = long_vec1 + 1; + EXPECT_EQ(result3.x, 2); + EXPECT_EQ(result3.y, 3); + + Vector2<float> result4 = float_vec1 + 1.0f; + EXPECT_FLOAT_EQ(result4.x, 2.0f); + EXPECT_FLOAT_EQ(result4.y, 3.0f); +} + +TEST_F(Vector2Test, Multiply) { + Vector2<int> result = int_vec1 * int_vec2; + EXPECT_EQ(result.x, 3); + EXPECT_EQ(result.y, 8); + + Vector2<double> result2 = double_vec1 * double_vec2; + EXPECT_FLOAT_EQ(result2.x, 3.0); + EXPECT_FLOAT_EQ(result2.y, 8.0); + + Vector2<long> result3 = long_vec1 * long_vec2; + EXPECT_EQ(result3.x, 3); + EXPECT_EQ(result3.y, 8); + + Vector2<float> result4 = float_vec1 * float_vec2; + EXPECT_FLOAT_EQ(result4.x, 3.0f); + EXPECT_FLOAT_EQ(result4.y, 8.0f); +} + +TEST_F(Vector2Test, MultiplyScalar) { + Vector2<int> result = int_vec1 * 2; + EXPECT_EQ(result.x, 2); + EXPECT_EQ(result.y, 4); + + Vector2<double> result2 = double_vec1 * 2.0; + EXPECT_FLOAT_EQ(result2.x, 2.0); + EXPECT_FLOAT_EQ(result2.y, 4.0); + + Vector2<long> result3 = long_vec1 * 2; + EXPECT_EQ(result3.x, 2); + EXPECT_EQ(result3.y, 4); + + Vector2<float> result4 = float_vec1 * 2.0f; + EXPECT_FLOAT_EQ(result4.x, 2.0f); + EXPECT_FLOAT_EQ(result4.y, 4.0f); +} + +TEST_F(Vector2Test, Divide) { + Vector2<int> result = int_vec1 / int_vec2; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 0); + + Vector2<double> result2 = double_vec1 / double_vec2; + EXPECT_FLOAT_EQ(result2.x, 0.33333333333333331); + EXPECT_FLOAT_EQ(result2.y, 0.5); + + Vector2<long> result3 = long_vec1 / long_vec2; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 0); + + Vector2<float> result4 = float_vec1 / float_vec2; + EXPECT_FLOAT_EQ(result4.x, 0.333333343f); + EXPECT_FLOAT_EQ(result4.y, 0.5f); +} + +TEST_F(Vector2Test, DivideScalar) { + Vector2<int> result = int_vec1 / 2; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 1); + + Vector2<double> result2 = double_vec1 / 2.0; + EXPECT_FLOAT_EQ(result2.x, 0.5); + EXPECT_FLOAT_EQ(result2.y, 1.0); + + Vector2<long> result3 = long_vec1 / 2; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 1); + + Vector2<float> result4 = float_vec1 / 2.0f; + EXPECT_FLOAT_EQ(result4.x, 0.5f); + EXPECT_FLOAT_EQ(result4.y, 1.0f); +} + +TEST_F(Vector2Test, AddChain) { + Vector2<int> result = int_vec1; + result += int_vec2; + EXPECT_EQ(result.x, 4); + EXPECT_EQ(result.y, 6); + + Vector2<double> result2 = double_vec1; + result2 += double_vec2; + EXPECT_FLOAT_EQ(result2.x, 4.0); + EXPECT_FLOAT_EQ(result2.y, 6.0); + + Vector2<long> result3 = long_vec1; + result3 += long_vec2; + EXPECT_EQ(result3.x, 4); + EXPECT_EQ(result3.y, 6); + + Vector2<float> result4 = float_vec1; + result4 += float_vec2; + EXPECT_FLOAT_EQ(result4.x, 4.0f); + EXPECT_FLOAT_EQ(result4.y, 6.0f); +} + +TEST_F(Vector2Test, AddScalarChain) { + Vector2<int> result = int_vec1; + result += 1; + EXPECT_EQ(result.x, 2); + EXPECT_EQ(result.y, 3); + + Vector2<double> result2 = double_vec1; + result2 += 1.0; + EXPECT_FLOAT_EQ(result2.x, 2.0); + EXPECT_FLOAT_EQ(result2.y, 3.0); + + Vector2<long> result3 = long_vec1; + result3 += 1; + EXPECT_EQ(result3.x, 2); + EXPECT_EQ(result3.y, 3); + + Vector2<float> result4 = float_vec1; + result4 += 1.0f; + EXPECT_FLOAT_EQ(result4.x, 2.0f); + EXPECT_FLOAT_EQ(result4.y, 3.0f); +} + +TEST_F(Vector2Test, SubtractChain) { + Vector2<int> result = int_vec1; + result -= int_vec2; + EXPECT_EQ(result.x, -2); + EXPECT_EQ(result.y, -2); + + Vector2<double> result2 = double_vec1; + result2 -= double_vec2; + EXPECT_FLOAT_EQ(result2.x, -2.0); + EXPECT_FLOAT_EQ(result2.y, -2.0); + + Vector2<long> result3 = long_vec1; + result3 -= long_vec2; + EXPECT_EQ(result3.x, -2); + EXPECT_EQ(result3.y, -2); + + Vector2<float> result4 = float_vec1; + result4 -= float_vec2; + EXPECT_FLOAT_EQ(result4.x, -2.0f); + EXPECT_FLOAT_EQ(result4.y, -2.0f); +} + +TEST_F(Vector2Test, SubtractScalarChain) { + Vector2<int> result = int_vec1; + result -= 1; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 1); + + Vector2<double> result2 = double_vec1; + result2 -= 1.0; + EXPECT_FLOAT_EQ(result2.x, 0.0); + EXPECT_FLOAT_EQ(result2.y, 1.0); + + Vector2<long> result3 = long_vec1; + result3 -= 1; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 1); + + Vector2<float> result4 = float_vec1; + result4 -= 1.0f; + EXPECT_FLOAT_EQ(result4.x, 0.0f); + EXPECT_FLOAT_EQ(result4.y, 1.0f); +} + +TEST_F(Vector2Test, MultiplyChain) { + Vector2<int> result = int_vec1; + result *= int_vec2; + EXPECT_EQ(result.x, 3); + EXPECT_EQ(result.y, 8); + + Vector2<double> result2 = double_vec1; + result2 *= double_vec2; + EXPECT_FLOAT_EQ(result2.x, 3.0); + EXPECT_FLOAT_EQ(result2.y, 8.0); + + Vector2<long> result3 = long_vec1; + result3 *= long_vec2; + EXPECT_EQ(result3.x, 3); + EXPECT_EQ(result3.y, 8); + + Vector2<float> result4 = float_vec1; + result4 *= float_vec2; + EXPECT_FLOAT_EQ(result4.x, 3.0f); + EXPECT_FLOAT_EQ(result4.y, 8.0f); +} + +TEST_F(Vector2Test, MultiplyScalarChain) { + Vector2<int> result = int_vec1; + result *= 2; + EXPECT_EQ(result.x, 2); + EXPECT_EQ(result.y, 4); + + Vector2<double> result2 = double_vec1; + result2 *= 2.0; + EXPECT_FLOAT_EQ(result2.x, 2.0); + EXPECT_FLOAT_EQ(result2.y, 4.0); + + Vector2<long> result3 = long_vec1; + result3 *= 2; + EXPECT_EQ(result3.x, 2); + EXPECT_EQ(result3.y, 4); + + Vector2<float> result4 = float_vec1; + result4 *= 2.0f; + EXPECT_FLOAT_EQ(result4.x, 2.0f); + EXPECT_FLOAT_EQ(result4.y, 4.0f); +} + +TEST_F(Vector2Test, DivideChain) { + Vector2<int> result = int_vec1; + result /= int_vec2; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 0); + + Vector2<double> result2 = double_vec1; + result2 /= double_vec2; + EXPECT_FLOAT_EQ(result2.x, 0.33333333333333331); + EXPECT_FLOAT_EQ(result2.y, 0.5); + + Vector2<long> result3 = long_vec1; + result3 /= long_vec2; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 0); + + Vector2<float> result4 = float_vec1; + result4 /= float_vec2; + EXPECT_FLOAT_EQ(result4.x, 0.333333343f); + EXPECT_FLOAT_EQ(result4.y, 0.5f); +} + +TEST_F(Vector2Test, DivideScalarChain) { + Vector2<int> result = int_vec1; + result /= 2; + EXPECT_EQ(result.x, 0); + EXPECT_EQ(result.y, 1); + + Vector2<double> result2 = double_vec1; + result2 /= 2.0; + EXPECT_FLOAT_EQ(result2.x, 0.5); + EXPECT_FLOAT_EQ(result2.y, 1.0); + + Vector2<long> result3 = long_vec1; + result3 /= 2; + EXPECT_EQ(result3.x, 0); + EXPECT_EQ(result3.y, 1); + + Vector2<float> result4 = float_vec1; + result4 /= 2.0f; + EXPECT_FLOAT_EQ(result4.x, 0.5f); + EXPECT_FLOAT_EQ(result4.y, 1.0f); +} + +TEST_F(Vector2Test, Negatation) { + Vector2<int> result = -int_vec1; + EXPECT_EQ(result.x, -1); + EXPECT_EQ(result.y, -2); + + Vector2<double> result2 = -double_vec1; + EXPECT_FLOAT_EQ(result2.x, -1.0); + EXPECT_FLOAT_EQ(result2.y, -2.0); + + Vector2<long> result3 = -long_vec1; + EXPECT_EQ(result3.x, -1); + EXPECT_EQ(result3.y, -2); + + Vector2<float> result4 = -float_vec1; + EXPECT_FLOAT_EQ(result4.x, -1.0f); + EXPECT_FLOAT_EQ(result4.y, -2.0f); +} + +TEST_F(Vector2Test, Equals) { + EXPECT_TRUE(int_vec1 == int_vec1); + EXPECT_FALSE(int_vec1 == int_vec2); + EXPECT_TRUE(double_vec1 == double_vec1); + EXPECT_FALSE(double_vec1 == double_vec2); + EXPECT_TRUE(long_vec1 == long_vec1); + EXPECT_FALSE(long_vec1 == long_vec2); +} + +TEST_F(Vector2Test, NotEquals) { + EXPECT_FALSE(int_vec1 != int_vec1); + EXPECT_TRUE(int_vec1 != int_vec2); + EXPECT_FALSE(double_vec1 != double_vec1); + EXPECT_TRUE(double_vec1 != double_vec2); + EXPECT_FALSE(long_vec1 != long_vec1); + EXPECT_TRUE(long_vec1 != long_vec2); +} + +TEST_F(Vector2Test, Truncate) { + Vector2<int> vec = {3, 4}; + vec.truncate(3); + EXPECT_EQ(vec.x, 0); + EXPECT_EQ(vec.y, 0); + + Vector2<double> vec2 = {3.0, 4.0}; + vec2.truncate(3.0); + EXPECT_FLOAT_EQ(vec2.x, 1.8); + EXPECT_FLOAT_EQ(vec2.y, 2.4); + + Vector2<long> vec3 = {3, 4}; + vec3.truncate(3); + EXPECT_EQ(vec3.x, 0); + EXPECT_EQ(vec3.y, 0); + + Vector2<float> vec4 = {3.0f, 4.0f}; + vec4.truncate(3.0f); + EXPECT_FLOAT_EQ(vec4.x, 1.8f); + EXPECT_FLOAT_EQ(vec4.y, 2.4f); +} + +TEST_F(Vector2Test, Normalize) { + Vector2<int> vec = {3, 4}; + vec.normalize(); + EXPECT_EQ(vec.x, 0); + EXPECT_EQ(vec.y, 0); + + Vector2<double> vec2 = {3.0, 4.0}; + vec2.normalize(); + EXPECT_FLOAT_EQ(vec2.x, 0.6); + EXPECT_FLOAT_EQ(vec2.y, 0.8); + + Vector2<long> vec3 = {3, 4}; + vec3.normalize(); + EXPECT_EQ(vec3.x, 0); + EXPECT_EQ(vec3.y, 0); + + Vector2<float> vec4 = {3.0f, 4.0f}; + vec4.normalize(); + EXPECT_FLOAT_EQ(vec4.x, 0.6f); + EXPECT_FLOAT_EQ(vec4.y, 0.8f); +} + +TEST_F(Vector2Test, Length) { + Vector2<int> vec = {3, 4}; + EXPECT_EQ(vec.length(), 5); + + Vector2<double> vec2 = {3.0, 4.0}; + EXPECT_FLOAT_EQ(vec2.length(), 5.0); + + Vector2<long> vec3 = {3, 4}; + EXPECT_EQ(vec3.length(), 5); + + Vector2<float> vec4 = {3.0f, 4.0f}; + EXPECT_FLOAT_EQ(vec4.length(), 5.0f); +} + +TEST_F(Vector2Test, LengthSquared) { + Vector2<int> vec = {3, 4}; + EXPECT_EQ(vec.length_squared(), 25); + + Vector2<double> vec2 = {3.0, 4.0}; + EXPECT_FLOAT_EQ(vec2.length_squared(), 25.0); + + Vector2<long> vec3 = {3, 4}; + EXPECT_EQ(vec3.length_squared(), 25); + + Vector2<float> vec4 = {3.0f, 4.0f}; + EXPECT_FLOAT_EQ(vec4.length_squared(), 25.0f); +} + +TEST_F(Vector2Test, Dot) { + Vector2<int> vec1 = {3, 4}; + Vector2<int> vec2 = {5, 6}; + EXPECT_EQ(vec1.dot(vec2), 39); + + Vector2<double> vec3 = {3.0, 4.0}; + Vector2<double> vec4 = {5.0, 6.0}; + EXPECT_FLOAT_EQ(vec3.dot(vec4), 39.0); + + Vector2<long> vec5 = {3, 4}; + Vector2<long> vec6 = {5, 6}; + EXPECT_EQ(vec5.dot(vec6), 39); + + Vector2<float> vec7 = {3.0f, 4.0f}; + Vector2<float> vec8 = {5.0f, 6.0f}; + EXPECT_FLOAT_EQ(vec7.dot(vec8), 39.0f); +} + +TEST_F(Vector2Test, Distance) { + Vector2<int> vec1 = {1, 1}; + Vector2<int> vec2 = {4, 5}; + EXPECT_EQ(vec1.distance(vec2), 5); + + Vector2<double> vec3 = {1.0, 1.0}; + Vector2<double> vec4 = {4.0, 5.0}; + EXPECT_FLOAT_EQ(vec3.distance(vec4), 5.0); + + Vector2<long> vec5 = {1, 1}; + Vector2<long> vec6 = {4, 5}; + EXPECT_EQ(vec5.distance(vec6), 5); + + Vector2<float> vec7 = {1.0f, 1.0f}; + Vector2<float> vec8 = {4.0f, 5.0f}; + EXPECT_FLOAT_EQ(vec7.distance(vec8), 5.0f); +} + +TEST_F(Vector2Test, DistanceSquared) { + Vector2<int> vec1 = {3, 4}; + Vector2<int> vec2 = {5, 6}; + EXPECT_EQ(vec1.distance_squared(vec2), 8); + + Vector2<double> vec3 = {3.0, 4.0}; + Vector2<double> vec4 = {5.0, 6.0}; + EXPECT_FLOAT_EQ(vec3.distance_squared(vec4), 8.0); + + Vector2<long> vec5 = {3, 4}; + Vector2<long> vec6 = {5, 6}; + EXPECT_EQ(vec5.distance_squared(vec6), 8); + + Vector2<float> vec7 = {3.0f, 4.0f}; + Vector2<float> vec8 = {5.0f, 6.0f}; + EXPECT_FLOAT_EQ(vec7.distance_squared(vec8), 8.0f); +} + +TEST_F(Vector2Test, Perpendicular) { + Vector2<int> vec = {3, 4}; + Vector2<int> result = vec.perpendicular(); + EXPECT_EQ(result.x, -4); + EXPECT_EQ(result.y, 3); + + Vector2<double> vec2 = {3.0, 4.0}; + Vector2<double> result2 = vec2.perpendicular(); + EXPECT_FLOAT_EQ(result2.x, -4.0); + EXPECT_FLOAT_EQ(result2.y, 3.0); + + Vector2<long> vec3 = {3, 4}; + Vector2<long> result3 = vec3.perpendicular(); + EXPECT_EQ(result3.x, -4); + EXPECT_EQ(result3.y, 3); + + Vector2<float> vec4 = {3.0f, 4.0f}; + Vector2<float> result4 = vec4.perpendicular(); + EXPECT_FLOAT_EQ(result4.x, -4.0f); + EXPECT_FLOAT_EQ(result4.y, 3.0f); +} + +TEST_F(Vector2Test, Rotate) { + vec2 foo {0, 1}; + + foo = foo.rotate(90); + const float GOOD_ENOUGH = 0.001; + EXPECT_NEAR(foo.x, 1, GOOD_ENOUGH); + EXPECT_NEAR(foo.y, 0, GOOD_ENOUGH); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 241015d..0e1bc75 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,15 +1,29 @@ -#include <crepe/api/Config.h> - #include <gtest/gtest.h> +#include <crepe/api/Config.h> + using namespace crepe; using namespace testing; +class GlobalConfigReset : public EmptyTestEventListener { +public: + Config & cfg = Config::get_instance(); + + // This function is called before each test + void OnTestStart(const TestInfo &) override { + cfg = { + .log = { + .level = Log::Level::WARNING, + }, + }; + } +}; + int main(int argc, char ** argv) { InitGoogleTest(&argc, argv); - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::ERROR; + UnitTest & ut = *UnitTest::GetInstance(); + ut.listeners().Append(new GlobalConfigReset); return RUN_ALL_TESTS(); } |