From ce14236bd08469737185962d0be11d72c442b60e Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 20 Nov 2024 15:39:27 +0100 Subject: most tests done --- src/test/CMakeLists.txt | 1 + src/test/EventTest.cpp | 470 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 471 insertions(+) create mode 100644 src/test/EventTest.cpp (limited to 'src/test') diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 49c8151..6f6ad79 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -3,5 +3,6 @@ target_sources(test_main PUBLIC PhysicsTest.cpp ScriptTest.cpp ParticleTest.cpp + EventTest.cpp ) diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp new file mode 100644 index 0000000..19f6d2e --- /dev/null +++ b/src/test/EventTest.cpp @@ -0,0 +1,470 @@ + +#include "api/EventManager.h" +#include "api/Event.h" +#include "api/IKeyListener.h" +#include "api/IMouseListener.h" +#include +#include +#include +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; + + +class EventManagerTest : public ::testing::Test { +protected: + void SetUp() override { + // Clear any existing subscriptions or events before each test + EventManager::get_instance().clear(); + } + + void TearDown() override { + // Ensure cleanup after each test + EventManager::get_instance().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 key_handler = [](const KeyPressEvent& e) { + std::cout << "Key Event Triggered" << std::endl; + return true; + }; + + // Subscribe to KeyPressEvent + EventManager::get_instance().subscribe(key_handler, 1); + + // Verify subscription (not directly verifiable; test by triggering event) + + EventManager::get_instance().trigger_event(KeyPressEvent{ + .repeat = true, + .key = Keycode::A, + }, 1); + EventManager::get_instance().trigger_event(KeyPressEvent{ + .repeat = true, + .key = Keycode::A, + + }, CHANNEL_ALL); + +} +TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { + bool triggered = false; + + EventHandler mouse_handler = [&](const MouseClickEvent& e) { + triggered = true; + std::cout << "mouse handled" <(mouse_handler, CHANNEL_ALL); + + MouseClickEvent click_event{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }; + EventManager::get_instance().trigger_event(click_event, CHANNEL_ALL); + + EXPECT_TRUE(triggered); +} + +TEST_F(EventManagerTest, EventManagerTest_priority_order) { + EventManager& event_manager = EventManager::get_instance(); + + // Vector to track call order + std::vector call_order; + + // Handlers with different priorities + EventHandler handler_priority_3 = [&](const MouseClickEvent& e) { + call_order.push_back(3); + return false; // Allow propagation + }; + + EventHandler handler_priority_1 = [&](const MouseClickEvent& e) { + call_order.push_back(1); + return false; // Allow propagation + }; + + EventHandler handler_priority_2 = [&](const MouseClickEvent& e) { + call_order.push_back(2); + return false; // Allow propagation + }; + + // Subscribe handlers with different priorities + event_manager.subscribe(handler_priority_1, CHANNEL_ALL, 1); + event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); + event_manager.subscribe(handler_priority_3, CHANNEL_ALL, 3); + + // Trigger the event + event_manager.trigger_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }, CHANNEL_ALL); + + // Check the call order matches the expected priority order + std::vector expected_order = {3, 2, 1}; + EXPECT_EQ(call_order, expected_order); + +} + +TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { + EventManager& event_manager = EventManager::get_instance(); + + // Flags to track handler calls + bool triggered_true = false; + bool triggered_false = false; + + // Handlers + EventHandler mouse_handler_true = [&](const MouseClickEvent& e) { + triggered_true = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return true; // Stops propagation + }; + + EventHandler mouse_handler_false = [&](const MouseClickEvent& e) { + triggered_false = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_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 + }; + + // First Scenario: True handler has higher priority + event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 1); + event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 0); + + // Trigger event + event_manager.trigger_event(click_event, CHANNEL_ALL); + + // Check that only the true handler was triggered + EXPECT_TRUE(triggered_true); + EXPECT_FALSE(triggered_false); + + // Reset and clear + triggered_true = false; + triggered_false = false; + event_manager.clear(); + + // Second Scenario: False handler has higher priority + event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 0); + event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 1); + + // Trigger event again + event_manager.trigger_event(click_event, CHANNEL_ALL); + + // Check that both handlers were triggered + EXPECT_TRUE(triggered_true); + EXPECT_TRUE(triggered_false); +} + +TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { + EventManager& event_manager = EventManager::get_instance(); + bool triggered1 = false; + bool triggered2 = false; + int test_channel = 1; + EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { + triggered1 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { + triggered2 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + event_manager.subscribe(mouse_handler1); + event_manager.subscribe(mouse_handler2,test_channel); + + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }); + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + },test_channel); + event_manager.dispatch_events(); + EXPECT_TRUE(triggered1); + EXPECT_TRUE(triggered2); +} + +TEST_F(EventManagerTest, EventManagerTest_dispatch_priority) { + EventManager& event_manager = EventManager::get_instance(); + std::vector call_order; + int test_channel = 1; + EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { + call_order.push_back(1); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { + call_order.push_back(2); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler3 = [&](const MouseClickEvent& e) { + call_order.push_back(3); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + event_manager.subscribe(mouse_handler1,CHANNEL_ALL,1); + event_manager.subscribe(mouse_handler2,CHANNEL_ALL,2); + event_manager.subscribe(mouse_handler2,CHANNEL_ALL,3); + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }); + event_manager.dispatch_events(); + std::vector expected_order = {3, 2, 1}; + EXPECT_EQ(call_order, expected_order); +} + +TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { + EventManager& event_manager = EventManager::get_instance(); + + // Flags to track if handlers are triggered + bool triggered1 = false; + bool triggered2 = false; + + // Define EventHandlers + EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { + triggered1 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + + EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { + triggered2 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + + // Subscribe handlers + event_manager.subscribe(mouse_handler1); + event_manager.subscribe(mouse_handler2); + + // Queue events + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }); + + // Dispatch events - both handlers should be triggered + event_manager.dispatch_events(); + EXPECT_TRUE(triggered1); // Handler 1 should be triggered + EXPECT_TRUE(triggered2); // Handler 2 should be triggered + + // Reset flags + triggered1 = false; + triggered2 = false; + + // Unsubscribe handler1 + event_manager.unsubscribe(mouse_handler1); + + // Queue the same event again + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }); + + // Dispatch events - only handler 2 should be triggered, handler 1 should NOT + event_manager.dispatch_events(); + EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered + EXPECT_TRUE(triggered2); // Handler 2 should be triggered + + // Reset flags + triggered2 = false; + + // Unsubscribe handler2 + event_manager.unsubscribe(mouse_handler2); + + // Queue the event again + event_manager.queue_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 200, + .button = MouseButton::LEFT_MOUSE + }); + + // Dispatch events - no handler should be triggered + event_manager.dispatch_events(); + EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered + EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered +} + +// TEST_F(EventManagerTest, OnKeyPressedTest) { +// MockKeyListener mock_key_listener; +// EventManager& event_manager = EventManager::get_instance(); + +// // Create the KeyPressEvent object +// KeyPressEvent key_event = KeyPressEvent{ +// .key = Keycode::A, +// .repeat = false +// }; + +// // Set up the mock expectation with direct object passing +// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) +// .Times(1) // Expect it to be called exactly once +// .WillOnce(::testing::Return(true)); // Return value (can be true/false) + + +// // Queue the event +// event_manager.queue_event(key_event); + +// // Dispatch the event to trigger the mock +// event_manager.dispatch_events(); // Trigger event dispatch + +// // The mock will ensure the on_key_pressed method is called +// } + + + +// TEST_F(EventManagerTest, OnKeyReleaseTest) { +// MockKeyListener mock_key_listener; +// EventManager& event_manager = EventManager::get_instance(); + +// // Create the KeyPressEvent object +// KeyReleaseEvent key_event = KeyReleaseEvent{ +// .key = Keycode::A, +// }; + +// // Set up the mock expectation with direct object passing +// EXPECT_CALL(mock_key_listener, on_key_released(key_event)) +// .Times(1) // Expect it to be called exactly once +// .WillOnce(::testing::Return(true)); // Return value (can be true/false) + + +// // Queue the event +// event_manager.queue_event(key_event); + +// // Dispatch the event to trigger the mock +// event_manager.dispatch_events(); // Trigger event dispatch + +// // The mock will ensure the on_key_pressed method is called +// } + +// TEST_F(EventManagerTest, UnsubscribeEventsTest) { +// EventManager& event_manager = EventManager::get_instance(); +// KeyPressEvent key_event{ +// .key = Keycode::A, +// .repeat = false +// }; + +// // Create the mock listener +// MockKeyListener mock_key_listener; + +// // Set up the mock expectation that on_key_pressed will be called once initially +// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) +// .Times(1) // Expect it to be called exactly once +// .WillOnce(::testing::Return(true)); // Return value (can be true/false) + +// event_manager.queue_event(key_event); +// event_manager.dispatch_events(); // Should trigger on_key_pressed once + +// // Now unsubscribe the listener +// event_manager.unsubscribe(std::bind(&MockKeyListener::on_key_pressed, &mock_key_listener, std::placeholders::_1)); + +// // Set up the expectation that on_key_pressed will NOT be called after unsubscribing +// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) +// .Times(0); // Should not be called after unsubscribe + +// // Queue and dispatch the event again (after unsubscribe) +// event_manager.queue_event(key_event); +// event_manager.dispatch_events(); // Should NOT trigger on_key_pressed after unsubscribe +// } + +// TEST_F(EventManagerTest, OnMouseButtonPressedTest) { +// MockMouseListener mock_mouse_listener; +// EventManager& event_manager = EventManager::get_instance(); + +// // Create the MouseButtonPressEvent object +// MousePressEvent mouse_event{ +// .mouse_x = 100, +// .mouse_y = 150, +// .button = MouseButton::LEFT_MOUSE +// }; + +// // Set up the mock expectation with direct object passing +// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) +// .Times(1) // Expect it to be called exactly once +// .WillOnce(::testing::Return(true)); // Return value (can be true/false) + +// // Queue the event +// event_manager.queue_event(mouse_event); + +// // Dispatch the event to trigger the mock +// event_manager.dispatch_events(); // Should trigger on_mouse_button_pressed once +// } + +// TEST_F(EventManagerTest, UnsubscribeMouseEventsTest) { +// EventManager& event_manager = EventManager::get_instance(); +// MousePressEvent mouse_event{ +// .mouse_x = 100, +// .mouse_y = 150, +// .button = MouseButton::LEFT_MOUSE +// }; + +// // Create the mock listener +// MockMouseListener mock_mouse_listener; + +// // Set up the mock expectation that on_mouse_button_pressed will be called once initially +// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) +// .Times(1) // Expect it to be called exactly once +// .WillOnce(::testing::Return(true)); // Return value (can be true/false) + +// // Queue and dispatch the event (should trigger on_mouse_button_pressed) +// event_manager.queue_event(mouse_event); +// event_manager.dispatch_events(); // Should trigger on_mouse_button_pressed once + +// // Now unsubscribe the listener +// event_manager.unsubscribe(std::bind(&MockMouseListener::on_mouse_pressed, &mock_mouse_listener, std::placeholders::_1)); + +// // Set up the expectation that on_mouse_button_pressed will NOT be called after unsubscribing +// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) +// .Times(0); // Should not be called after unsubscribe + +// // Queue and dispatch the event again (after unsubscribe) +// event_manager.queue_event(mouse_event); +// event_manager.dispatch_events(); // Should NOT trigger on_mouse_button_pressed after unsubscribe +// } -- cgit v1.2.3 From d9a51069366650051e644453f5d3ac90f2ab29b5 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 20 Nov 2024 16:29:48 +0100 Subject: turn examples into unit tests --- src/crepe/util/Proxy.h | 2 ++ src/crepe/util/Proxy.hpp | 6 +++++ src/example/CMakeLists.txt | 3 --- src/example/log.cpp | 28 ------------------- src/example/proxy.cpp | 45 ------------------------------- src/example/script.cpp | 49 --------------------------------- src/test/CMakeLists.txt | 1 + src/test/ValueBrokerTest.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 73 insertions(+), 125 deletions(-) delete mode 100644 src/example/log.cpp delete mode 100644 src/example/proxy.cpp delete mode 100644 src/example/script.cpp create mode 100644 src/test/ValueBrokerTest.cpp (limited to 'src/test') diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h index b34f7c6..789144a 100644 --- a/src/crepe/util/Proxy.h +++ b/src/crepe/util/Proxy.h @@ -15,6 +15,8 @@ namespace crepe { template class Proxy { public: + //! Set operator + Proxy & operator=(Proxy &); //! Set operator Proxy & operator=(const T &); //! Get operator diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp index b9923db..ef2b69f 100644 --- a/src/crepe/util/Proxy.hpp +++ b/src/crepe/util/Proxy.hpp @@ -13,6 +13,12 @@ Proxy & Proxy::operator=(const T & val) { return *this; } +template +Proxy & Proxy::operator=(Proxy & proxy) { + this->broker.set(T(proxy)); + return *this; +} + template Proxy::operator const T &() { return this->broker.get(); diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 30432e5..f21bd24 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -17,13 +17,10 @@ function(add_example target_name) endfunction() add_example(audio_internal) -add_example(script) -add_example(log) add_example(rendering) add_example(asset_manager) add_example(physics) add_example(savemgr) -add_example(proxy) add_example(db) add_example(particles) add_example(gameloop) diff --git a/src/example/log.cpp b/src/example/log.cpp deleted file mode 100644 index 5baa021..0000000 --- a/src/example/log.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** \file - * - * Standalone example for usage of the logging functions - */ - -#include -#include - -using namespace crepe; - -// unrelated setup code -int _ = []() { - // make sure all log messages get printed - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - - return 0; // satisfy compiler -}(); - -int main() { - dbg_trace(); - dbg_log("debug message"); - Log::logf("info message with variable: {}", 3); - Log::logf(Log::Level::WARNING, "warning"); - Log::logf(Log::Level::ERROR, "error"); - - return 0; -} diff --git a/src/example/proxy.cpp b/src/example/proxy.cpp deleted file mode 100644 index 69451f8..0000000 --- a/src/example/proxy.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/** \file - * - * Standalone example for usage of the proxy type - */ - -#include -#include -#include -#include - -using namespace std; -using namespace crepe; - -void test_ro_ref(const int & val) {} -void test_rw_ref(int & val) {} -void test_ro_val(int val) {} - -int main() { - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::DEBUG; - - int real_value = 0; - - ValueBroker broker{ - [&real_value](const int & target) { - dbg_logf("set {} to {}", real_value, target); - real_value = target; - }, - [&real_value]() -> const int & { - dbg_logf("get {}", real_value); - return real_value; - }, - }; - - Proxy proxy{broker}; - - broker.set(54); - proxy = 84; - - test_ro_ref(proxy); // this is allowed - // test_rw_ref(proxy); // this should throw a compile error - test_ro_val(proxy); - - return 0; -} diff --git a/src/example/script.cpp b/src/example/script.cpp deleted file mode 100644 index a23295b..0000000 --- a/src/example/script.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** \file - * - * Standalone example for usage of the script component and system - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace crepe; -using namespace std; - -// Unrelated stuff that is not part of this POC -int _ = []() { - // Show dbg_trace() output - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - - return 0; // satisfy compiler -}(); - -// User-defined script: -class MyScript : public Script { - void update() { - // Retrieve component from the same GameObject this script is on - Transform & test = get_component(); - dbg_logf("Transform({:.2f}, {:.2f})", test.position.x, test.position.y); - } -}; - -int main() { - ComponentManager component_manager{}; - ScriptSystem system{component_manager}; - - // Create game object with Transform and BehaviorScript components - GameObject obj = component_manager.new_object("name"); - obj.add_component().set_script(); - - // Update all scripts. This should result in MyScript::update being called - system.update(); - - return EXIT_SUCCESS; -} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d56d80f..c1f935d 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -5,5 +5,6 @@ target_sources(test_main PUBLIC ParticleTest.cpp ECSTest.cpp SceneManagerTest.cpp + ValueBrokerTest.cpp ) diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp new file mode 100644 index 0000000..10a4654 --- /dev/null +++ b/src/test/ValueBrokerTest.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class ValueBrokerTest : public Test { +public: + int read_count = 0; + int write_count = 0; + int value = 0; + + ValueBroker broker { + [this](const int & target) -> void { + this->write_count++; + this->value = target; + }, + [this]() -> const int & { + this->read_count++; + return this->value; + }, + }; + Proxy proxy{broker}; + + void SetUp() override { + ASSERT_EQ(read_count, 0); + ASSERT_EQ(write_count, 0); + } +}; + +TEST_F(ValueBrokerTest, BrokerWrite) { + broker.set(0); + EXPECT_EQ(read_count, 0); + EXPECT_EQ(write_count, 1); +} + +TEST_F(ValueBrokerTest, BrokerRead) { + broker.get(); + EXPECT_EQ(read_count, 1); + EXPECT_EQ(write_count, 0); +} + +TEST_F(ValueBrokerTest, ProxyWrite) { + proxy = 0; + EXPECT_EQ(read_count, 0); + EXPECT_EQ(write_count, 1); +} + +void dummy(int) { } +TEST_F(ValueBrokerTest, ProxyRead) { + dummy(proxy); + EXPECT_EQ(read_count, 1); + EXPECT_EQ(write_count, 0); +} + +TEST_F(ValueBrokerTest, ProxyReadWrite) { + proxy = proxy; + ASSERT_EQ(read_count, 1); + ASSERT_EQ(write_count, 1); +} + -- cgit v1.2.3 From fdf91122ddc1fa9942c8790d2c8c845f877df11d Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 20 Nov 2024 16:46:00 +0100 Subject: convert more examples to unit tests --- src/crepe/facade/DB.cpp | 4 +-- src/crepe/facade/DB.h | 4 ++- src/example/CMakeLists.txt | 5 --- src/example/audio_internal.cpp | 56 -------------------------------- src/example/db.cpp | 30 ------------------ src/example/particles.cpp | 43 ------------------------- src/example/physics.cpp | 24 -------------- src/example/rendering.cpp | 72 ------------------------------------------ src/test/CMakeLists.txt | 1 + src/test/DBTest.cpp | 29 +++++++++++++++++ 10 files changed, 35 insertions(+), 233 deletions(-) delete mode 100644 src/example/audio_internal.cpp delete mode 100644 src/example/db.cpp delete mode 100644 src/example/particles.cpp delete mode 100644 src/example/physics.cpp delete mode 100644 src/example/rendering.cpp create mode 100644 src/test/DBTest.cpp (limited to 'src/test') diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index d5d19dc..95cf606 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -18,8 +18,8 @@ DB::DB(const string & path) { this->db = {db, [](libdb::DB * db) { db->close(db, 0); }}; // load or create database file - ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE, - 0); + const char * file = path.empty() ? NULL : path.c_str(); + ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0); if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret))); // create cursor diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h index 629b0eb..115c0f1 100644 --- a/src/crepe/facade/DB.h +++ b/src/crepe/facade/DB.h @@ -22,8 +22,10 @@ class DB { public: /** * \param path The path of the database (created if nonexistant) + * + * \note If \p path is empty, the database is entirely in-memory */ - DB(const std::string & path); + DB(const std::string & path = ""); virtual ~DB() = default; public: diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index f21bd24..c75a4b5 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -16,12 +16,7 @@ function(add_example target_name) add_dependencies(examples ${target_name}) endfunction() -add_example(audio_internal) -add_example(rendering) add_example(asset_manager) -add_example(physics) add_example(savemgr) -add_example(db) -add_example(particles) add_example(gameloop) diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp deleted file mode 100644 index 661161a..0000000 --- a/src/example/audio_internal.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/** \file - * - * Standalone example for usage of the internal \c Sound class. - */ - -#include -#include -#include - -#include - -using namespace crepe; -using namespace std; -using namespace std::chrono_literals; -using std::make_unique; - -// Unrelated stuff that is not part of this POC -int _ = []() { - // Show dbg_trace() output - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - - return 0; // satisfy compiler -}(); - -int main() { - // Load a background track (Ogg Vorbis) - auto bgm = Sound("../mwe/audio/bgm.ogg"); - // Load three short samples (WAV) - auto sfx1 = Sound("../mwe/audio/sfx1.wav"); - auto sfx2 = Sound("../mwe/audio/sfx2.wav"); - auto sfx3 = Sound("../mwe/audio/sfx3.wav"); - - // Start the background track - bgm.play(); - - // Play each sample sequentially while pausing and resuming the background track - this_thread::sleep_for(500ms); - sfx1.play(); - this_thread::sleep_for(500ms); - sfx2.play(); - bgm.pause(); - this_thread::sleep_for(500ms); - sfx3.play(); - bgm.play(); - this_thread::sleep_for(500ms); - - // Play all samples simultaniously - sfx1.play(); - sfx2.play(); - sfx3.play(); - this_thread::sleep_for(1000ms); - - // Stop all audio and exit - return EXIT_SUCCESS; -} diff --git a/src/example/db.cpp b/src/example/db.cpp deleted file mode 100644 index ee4e8fc..0000000 --- a/src/example/db.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -using namespace crepe; -using namespace std; - -// run before main -static auto _ = []() { - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - return 0; -}(); - -int main() { - dbg_trace(); - - DB db("file.db"); - - const char * test_key = "test-key"; - string test_data = "Hello world!"; - - dbg_logf("DB has key = {}", db.has(test_key)); - - db.set(test_key, test_data); - - dbg_logf("key = \"{}\"", db.get(test_key)); - - return 0; -} diff --git a/src/example/particles.cpp b/src/example/particles.cpp deleted file mode 100644 index 3d5f676..0000000 --- a/src/example/particles.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace crepe; -using namespace std; - -int main(int argc, char * argv[]) { - ComponentManager mgr{}; - GameObject game_object = mgr.new_object("", "", Vector2{0, 0}, 0, 0); - Color color(0, 0, 0, 0); - Sprite test_sprite = game_object.add_component( - make_shared("../asset/texture/img.png"), color, FlipSettings{true, true}); - game_object.add_component(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, - }); - - return 0; -} diff --git a/src/example/physics.cpp b/src/example/physics.cpp deleted file mode 100644 index ad663a0..0000000 --- a/src/example/physics.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace crepe; -using namespace std; - -int main(int argc, char * argv[]) { - ComponentManager mgr{}; - - GameObject game_object = mgr.new_object("Name", "Tag", Vector2{0, 0}, 0, 0); - game_object.add_component(Rigidbody::Data{ - .mass = 1, - .gravity_scale = 1, - .body_type = Rigidbody::BodyType::DYNAMIC, - .constraints = {0, 0, 0}, - .use_gravity = true, - .bounce = false, - }); - return 0; -} diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp deleted file mode 100644 index ecd3f6a..0000000 --- a/src/example/rendering.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "api/Camera.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; -using namespace crepe; - -int main() { - dbg_trace(); - - ComponentManager mgr{}; - RenderSystem sys{mgr}; - - GameObject obj = mgr.new_object("name", "tag", Vector2{0, 0}, 1, 1); - GameObject obj1 = mgr.new_object("name", "tag", Vector2{500, 0}, 1, 0.1); - GameObject obj2 = mgr.new_object("name", "tag", Vector2{800, 0}, 1, 0.1); - - // Normal adding components - { - Color color(0, 0, 0, 0); - Sprite & sprite - = obj.add_component(make_shared("../asset/texture/img.png"), - color, FlipSettings{false, false}); - sprite.sorting_in_layer = 2; - sprite.order_in_layer = 1; - obj.add_component(Color::get_red()); - } - { - Color color(0, 0, 0, 0); - Sprite & sprite = obj1.add_component( - make_shared("../asset/texture/img.png"), color, FlipSettings{true, true}); - sprite.sorting_in_layer = 2; - sprite.order_in_layer = 2; - } - - { - Color color(0, 0, 0, 0); - Sprite & sprite = obj2.add_component( - make_shared("../asset/texture/img.png"), color, FlipSettings{true, true}); - sprite.sorting_in_layer = 1; - sprite.order_in_layer = 2; - } - - /* - { - Color color(0, 0, 0, 0); - auto img = mgr.cache("../asset/texture/second.png"); - obj2.add_component(img, color, FlipSettings{true, true}); - } - */ - - sys.update(); - /* - - auto start = std::chrono::steady_clock::now(); - while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) { - sys.update(); - } - */ -} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index c1f935d..61dd2d9 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -6,5 +6,6 @@ target_sources(test_main PUBLIC ECSTest.cpp SceneManagerTest.cpp ValueBrokerTest.cpp + DBTest.cpp ) diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp new file mode 100644 index 0000000..b57eba9 --- /dev/null +++ b/src/test/DBTest.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class DBTest : public Test { +public: + DB db; +}; + +TEST_F(DBTest, ReadWrite) { + db.set("foo", "bar"); + EXPECT_EQ(db.get("foo"), "bar"); +} + +TEST_F(DBTest, Nonexistant) { + EXPECT_THROW(db.get("foo"), std::out_of_range); + db.set("foo", "bar"); + EXPECT_NO_THROW(db.get("foo")); +} + +TEST_F(DBTest, Has) { + EXPECT_EQ(db.has("foo"), false); + db.set("foo", "bar"); + EXPECT_EQ(db.has("foo"), true); +} + -- cgit v1.2.3 From 9a46acde813e00e574e70439795dedcdc9a8192a Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 20 Nov 2024 18:41:50 +0100 Subject: dispatch fixed and priority added --- src/crepe/api/EventManager.cpp | 73 ++++++++------------- src/crepe/api/EventManager.hpp | 95 ++++++++++----------------- src/crepe/api/KeyCodes.h | 29 ++++++--- src/test/EventTest.cpp | 142 +---------------------------------------- 4 files changed, 84 insertions(+), 255 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index 27a304f..4f88e97 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -8,58 +8,37 @@ EventManager & EventManager::get_instance() { } void EventManager::dispatch_events() { - using HandlersMap - = std::unordered_map>>; - using HandlersVec = std::vector>; + for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) { + std::unique_ptr& event = (*event_it).event; + int channel = (*event_it).channel; + std::type_index event_type = (*event_it).type; - for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) { - std::unique_ptr & event = (*event_it).event; - int channel = (*event_it).channel; - std::type_index event_type = (*event_it).type; + bool event_handled = false; + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + std::vector& handlers = handlers_it->second; + + std::sort(handlers.begin(), handlers.end(), [](const CallbackEntry& a, const CallbackEntry& b) { + return a.priority > b.priority; + }); - bool event_handled = false; + for (auto handler_it = handlers.begin(); handler_it != handlers.end(); ++handler_it) { + // If callback is executed and returns true, remove the event from the queue + if ((*handler_it).callback->exec(*event)) { + event_it = this->events_queue.erase(event_it); + event_handled = true; + break; + } + } + } - if (channel) { - auto handlers_it = subscribers_by_event_id.find(event_type); - if (handlers_it != subscribers_by_event_id.end()) { - HandlersMap & handlers_map = handlers_it->second; - auto handlers = handlers_map.find(channel); - if (handlers != handlers_map.end()) { - HandlersVec & callbacks = handlers->second; - for (auto handler_it = callbacks.begin(); handler_it != callbacks.end(); - ++handler_it) { - if ((*handler_it)->exec(*event)) { - event_it = events_queue.erase(event_it); - event_handled = true; - break; - } - } - } - } - } else { - // Handle event for all channels - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - HandlersVec & handlers = handlers_it->second; - for (auto handler_it = handlers.begin(); handler_it != handlers.end(); - ++handler_it) { - // remove event from queue since and continue when callback returns true - if ((*handler_it)->exec(*event)) { - event_it = this->events_queue.erase(event_it); - event_handled = true; - break; - } - } - } - } - - if (!event_handled) { - ++event_it; - } - } + if (!event_handled) { + ++event_it; + } + } } + void EventManager::clear(){ this->subscribers.clear(); this->events_queue.clear(); - this->subscribers_by_event_id.clear(); } diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index 3a40336..481017d 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -4,17 +4,20 @@ namespace crepe { template void EventManager::subscribe(const EventHandler & callback, int channel, int priority) { - using HandlersVec = std::vector; std::type_index event_type = typeid(EventType); std::unique_ptr> handler = std::make_unique>(callback); - HandlersVec & handlers = this->subscribers[event_type]; + std::vector & handlers = this->subscribers[event_type]; handlers.emplace_back(CallbackEntry{ .callback = std::move(handler), .channel = channel, .priority = priority, }); + // Sort handlers by priority (highest first) + std::sort(handlers.begin(), handlers.end(), [](const CallbackEntry &a, const CallbackEntry &b) { + return a.priority > b.priority; + }); } template @@ -36,72 +39,44 @@ void EventManager::queue_event(const EventType & event, int channel,int priority template void EventManager::trigger_event(const EventType & event, int channel) { - using HandlersMap - = std::unordered_map>>; - using HandlersVec = std::vector>; + std::type_index event_type = typeid(EventType); - std::type_index event_type = typeid(EventType); - - if (channel == CHANNEL_ALL) { - HandlersMap & handlers_map = this->subscribers_by_event_id[event_type]; - auto handlers_it = handlers_map.find(channel); + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + const std::vector &handlers = handlers_it->second; - if (handlers_it != handlers_map.end()) { - HandlersVec & handlers = handlers_it->second; - for (auto it = handlers.begin(); it != handlers.end(); ++it) { - // stops when callback returns true - if ((*it)->exec(event)) { - break; - } - } - } - } else { - HandlersVec & handlers = this->subscribers[event_type]; - for (auto it = handlers.begin(); it != handlers.end(); ++it) { - // stops when callback returns true - if ((*it)->exec(event)) { - break; - } - } - } + for (const CallbackEntry &handler : handlers) { + if (handler.channel != channel && handler.channel != CHANNEL_ALL) { + continue; + } + if (handler.callback->exec(event)) { + break; + } + } + } } + template void EventManager::unsubscribe(const EventHandler & callback, int channel) { - using HandlersMap - = std::unordered_map>>; - using HandlersVec = std::vector>; + std::type_index event_type = typeid(EventType); + std::string handler_name = callback.target_type().name(); - std::type_index event_type = typeid(EventType); - std::string handler_name = callback.target_type().name(); + // Find the list of handlers for this event type + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + std::vector & handlers = handlers_it->second; - if (channel) { - auto subscriber_list = this->subscribers_by_event_id.find(event_type); - if (subscriber_list != this->subscribers_by_event_id.end()) { - HandlersMap & handlers_map = subscriber_list->second; - auto handlers = handlers_map.find(channel); - if (handlers != handlers_map.end()) { - HandlersVec & callbacks = handlers->second; - for (auto it = callbacks.begin(); it != callbacks.end(); ++it) { - if ((*it)->get_type() == handler_name) { - it = callbacks.erase(it); - return; - } - } - } - } - } else { - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - HandlersVec & handlers = handlers_it->second; - for (auto it = handlers.begin(); it != handlers.end(); ++it) { - if ((*it)->get_type() == handler_name) { - it = handlers.erase(it); - return; - } - } - } - } + for (auto it = handlers.begin(); it != handlers.end(); ) { + // Match based on handler type and channel + if (it->callback->get_type() == handler_name && it->channel == channel) { + it = handlers.erase(it); + return; + } + ++it; + } + } } + } // namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index feed0b2..16c2108 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -1,7 +1,6 @@ #pragma once -//! \enum MouseButton -//! \brief Enumeration for mouse button inputs, including standard and extended buttons. +//! Enumeration for mouse button inputs, including standard and extended buttons. enum class MouseButton { //! No mouse button input. NONE = 0, @@ -18,12 +17,11 @@ enum class MouseButton { //! Scroll wheel upward movement. SCROLL_UP = 6, //! Scroll wheel downward movement. - SCROLL_DOWN = 7 + SCROLL_DOWN = 7, }; -//! \enum Keycode -//! \brief Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. -enum class Keycode : int { +//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. +enum class Keycode { //! No key input. NONE = 0, //! Spacebar. @@ -164,7 +162,10 @@ enum class Keycode : int { PRINT_SCREEN = 283, //! Pause key. PAUSE = 284, - //! Function keys (F1-F25). + /** + * \name Function keys (F1-F25). + * \{ + */ F1 = 290, F2 = 291, F3 = 292, @@ -190,7 +191,11 @@ enum class Keycode : int { F23 = 312, F24 = 313, F25 = 314, - //! Keypad digits and operators. + /// \} + /** + * \name Keypad digits and operators. + * \{ + */ KP0 = 320, KP1 = 321, KP2 = 322, @@ -208,6 +213,11 @@ enum class Keycode : int { KP_ADD = 334, KP_ENTER = 335, KP_EQUAL = 336, + /// \} + /** + * \name Modifier keys. + * \{ + */ //! Modifier keys. LEFT_SHIFT = 340, LEFT_CONTROL = 341, @@ -217,6 +227,7 @@ enum class Keycode : int { RIGHT_CONTROL = 345, RIGHT_ALT = 346, RIGHT_SUPER = 347, + /// \} //! Menu key. - MENU = 348 + MENU = 348, }; diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 19f6d2e..3a78590 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -105,8 +105,8 @@ TEST_F(EventManagerTest, EventManagerTest_priority_order) { // Subscribe handlers with different priorities event_manager.subscribe(handler_priority_1, CHANNEL_ALL, 1); - event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); event_manager.subscribe(handler_priority_3, CHANNEL_ALL, 3); + event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); // Trigger the event event_manager.trigger_event(MouseClickEvent{ @@ -242,9 +242,9 @@ TEST_F(EventManagerTest, EventManagerTest_dispatch_priority) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - event_manager.subscribe(mouse_handler1,CHANNEL_ALL,1); event_manager.subscribe(mouse_handler2,CHANNEL_ALL,2); - event_manager.subscribe(mouse_handler2,CHANNEL_ALL,3); + event_manager.subscribe(mouse_handler1,CHANNEL_ALL,1); + event_manager.subscribe(mouse_handler3,CHANNEL_ALL,3); event_manager.queue_event(MouseClickEvent{ .mouse_x = 100, .mouse_y = 200, @@ -278,7 +278,6 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; // Allows propagation }; - // Subscribe handlers event_manager.subscribe(mouse_handler1); event_manager.subscribe(mouse_handler2); @@ -333,138 +332,3 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered } -// TEST_F(EventManagerTest, OnKeyPressedTest) { -// MockKeyListener mock_key_listener; -// EventManager& event_manager = EventManager::get_instance(); - -// // Create the KeyPressEvent object -// KeyPressEvent key_event = KeyPressEvent{ -// .key = Keycode::A, -// .repeat = false -// }; - -// // Set up the mock expectation with direct object passing -// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) -// .Times(1) // Expect it to be called exactly once -// .WillOnce(::testing::Return(true)); // Return value (can be true/false) - - -// // Queue the event -// event_manager.queue_event(key_event); - -// // Dispatch the event to trigger the mock -// event_manager.dispatch_events(); // Trigger event dispatch - -// // The mock will ensure the on_key_pressed method is called -// } - - - -// TEST_F(EventManagerTest, OnKeyReleaseTest) { -// MockKeyListener mock_key_listener; -// EventManager& event_manager = EventManager::get_instance(); - -// // Create the KeyPressEvent object -// KeyReleaseEvent key_event = KeyReleaseEvent{ -// .key = Keycode::A, -// }; - -// // Set up the mock expectation with direct object passing -// EXPECT_CALL(mock_key_listener, on_key_released(key_event)) -// .Times(1) // Expect it to be called exactly once -// .WillOnce(::testing::Return(true)); // Return value (can be true/false) - - -// // Queue the event -// event_manager.queue_event(key_event); - -// // Dispatch the event to trigger the mock -// event_manager.dispatch_events(); // Trigger event dispatch - -// // The mock will ensure the on_key_pressed method is called -// } - -// TEST_F(EventManagerTest, UnsubscribeEventsTest) { -// EventManager& event_manager = EventManager::get_instance(); -// KeyPressEvent key_event{ -// .key = Keycode::A, -// .repeat = false -// }; - -// // Create the mock listener -// MockKeyListener mock_key_listener; - -// // Set up the mock expectation that on_key_pressed will be called once initially -// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) -// .Times(1) // Expect it to be called exactly once -// .WillOnce(::testing::Return(true)); // Return value (can be true/false) - -// event_manager.queue_event(key_event); -// event_manager.dispatch_events(); // Should trigger on_key_pressed once - -// // Now unsubscribe the listener -// event_manager.unsubscribe(std::bind(&MockKeyListener::on_key_pressed, &mock_key_listener, std::placeholders::_1)); - -// // Set up the expectation that on_key_pressed will NOT be called after unsubscribing -// EXPECT_CALL(mock_key_listener, on_key_pressed(key_event)) -// .Times(0); // Should not be called after unsubscribe - -// // Queue and dispatch the event again (after unsubscribe) -// event_manager.queue_event(key_event); -// event_manager.dispatch_events(); // Should NOT trigger on_key_pressed after unsubscribe -// } - -// TEST_F(EventManagerTest, OnMouseButtonPressedTest) { -// MockMouseListener mock_mouse_listener; -// EventManager& event_manager = EventManager::get_instance(); - -// // Create the MouseButtonPressEvent object -// MousePressEvent mouse_event{ -// .mouse_x = 100, -// .mouse_y = 150, -// .button = MouseButton::LEFT_MOUSE -// }; - -// // Set up the mock expectation with direct object passing -// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) -// .Times(1) // Expect it to be called exactly once -// .WillOnce(::testing::Return(true)); // Return value (can be true/false) - -// // Queue the event -// event_manager.queue_event(mouse_event); - -// // Dispatch the event to trigger the mock -// event_manager.dispatch_events(); // Should trigger on_mouse_button_pressed once -// } - -// TEST_F(EventManagerTest, UnsubscribeMouseEventsTest) { -// EventManager& event_manager = EventManager::get_instance(); -// MousePressEvent mouse_event{ -// .mouse_x = 100, -// .mouse_y = 150, -// .button = MouseButton::LEFT_MOUSE -// }; - -// // Create the mock listener -// MockMouseListener mock_mouse_listener; - -// // Set up the mock expectation that on_mouse_button_pressed will be called once initially -// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) -// .Times(1) // Expect it to be called exactly once -// .WillOnce(::testing::Return(true)); // Return value (can be true/false) - -// // Queue and dispatch the event (should trigger on_mouse_button_pressed) -// event_manager.queue_event(mouse_event); -// event_manager.dispatch_events(); // Should trigger on_mouse_button_pressed once - -// // Now unsubscribe the listener -// event_manager.unsubscribe(std::bind(&MockMouseListener::on_mouse_pressed, &mock_mouse_listener, std::placeholders::_1)); - -// // Set up the expectation that on_mouse_button_pressed will NOT be called after unsubscribing -// EXPECT_CALL(mock_mouse_listener, on_mouse_pressed(mouse_event)) -// .Times(0); // Should not be called after unsubscribe - -// // Queue and dispatch the event again (after unsubscribe) -// event_manager.queue_event(mouse_event); -// event_manager.dispatch_events(); // Should NOT trigger on_mouse_button_pressed after unsubscribe -// } -- cgit v1.2.3 From 9f882131f09410113d757d96e5aa0322aa5584bd Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 20 Nov 2024 18:48:18 +0100 Subject: git remake --- mwe/events/include/event.h | 2 +- src/crepe/api/EventManager.cpp | 60 ++--- src/crepe/api/EventManager.h | 19 +- src/crepe/api/EventManager.hpp | 87 ++++--- src/crepe/api/IKeyListener.cpp | 8 +- src/crepe/api/IKeyListener.h | 1 + src/crepe/api/IMouseListener.cpp | 3 +- src/crepe/api/IMouseListener.h | 1 + src/example/events.cpp | 10 +- src/test/EventTest.cpp | 530 +++++++++++++++++++-------------------- 10 files changed, 344 insertions(+), 377 deletions(-) (limited to 'src/test') diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h index ee1bf52..e1b220b 100644 --- a/mwe/events/include/event.h +++ b/mwe/events/include/event.h @@ -148,7 +148,7 @@ private: }; class ShutDownEvent : public Event { public: - ShutDownEvent() : Event("ShutDownEvent") {}; + ShutDownEvent() : Event("ShutDownEvent"){}; REGISTER_EVENT_TYPE(ShutDownEvent) diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index 4f88e97..dbdb0c3 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -8,37 +8,39 @@ EventManager & EventManager::get_instance() { } void EventManager::dispatch_events() { - for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) { - std::unique_ptr& event = (*event_it).event; - int channel = (*event_it).channel; - std::type_index event_type = (*event_it).type; - - bool event_handled = false; - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - std::vector& handlers = handlers_it->second; - - std::sort(handlers.begin(), handlers.end(), [](const CallbackEntry& a, const CallbackEntry& b) { - return a.priority > b.priority; - }); - - for (auto handler_it = handlers.begin(); handler_it != handlers.end(); ++handler_it) { - // If callback is executed and returns true, remove the event from the queue - if ((*handler_it).callback->exec(*event)) { - event_it = this->events_queue.erase(event_it); - event_handled = true; - break; - } - } - } - - if (!event_handled) { - ++event_it; - } - } + for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) { + std::unique_ptr & event = (*event_it).event; + int channel = (*event_it).channel; + std::type_index event_type = (*event_it).type; + + bool event_handled = false; + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + std::vector & handlers = handlers_it->second; + + std::sort(handlers.begin(), handlers.end(), + [](const CallbackEntry & a, const CallbackEntry & b) { + return a.priority > b.priority; + }); + + for (auto handler_it = handlers.begin(); handler_it != handlers.end(); + ++handler_it) { + // If callback is executed and returns true, remove the event from the queue + if ((*handler_it).callback->exec(*event)) { + event_it = this->events_queue.erase(event_it); + event_handled = true; + break; + } + } + } + + if (!event_handled) { + ++event_it; + } + } } -void EventManager::clear(){ +void EventManager::clear() { this->subscribers.clear(); this->events_queue.clear(); } diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h index eccb0bf..133d72d 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/api/EventManager.h @@ -19,7 +19,6 @@ static constexpr int CHANNEL_ALL = -1; */ class EventManager { public: - /** * \brief Get the singleton instance of the EventManager. * @@ -39,7 +38,8 @@ public: * \param channel The channel number to subscribe to (default is 0). */ template - void subscribe(const EventHandler & callback, int channel = CHANNEL_ALL, int priority = 0); + void subscribe(const EventHandler & callback, int channel = CHANNEL_ALL, + int priority = 0); /** * \brief Unsubscribe from an event. @@ -76,7 +76,7 @@ public: * \param channel The channel number for the event (default is 0). */ template - void queue_event(const EventType & event, int channel = CHANNEL_ALL,int priority = 0); + void queue_event(const EventType & event, int channel = CHANNEL_ALL, int priority = 0); /** * \brief Dispatch all queued events. @@ -90,8 +90,8 @@ public: * */ void clear(); + private: - /** * \brief Default constructor for the EventManager. * @@ -99,21 +99,20 @@ private: */ EventManager() = default; struct QueueEntry { - std::unique_ptr event; + std::unique_ptr event; int channel = CHANNEL_ALL; std::type_index type; int priority = 0; - }; + }; struct CallbackEntry { - std::unique_ptr callback; + std::unique_ptr callback; int channel = CHANNEL_ALL; int priority = 0; - }; + }; //! The queue of events to be processed. std::vector events_queue; //! Registered event handlers. - std::unordered_map> subscribers; - + std::unordered_map> subscribers; }; } // namespace crepe diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index 481017d..a04a43a 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -3,7 +3,8 @@ namespace crepe { template -void EventManager::subscribe(const EventHandler & callback, int channel, int priority) { +void EventManager::subscribe(const EventHandler & callback, int channel, + int priority) { std::type_index event_type = typeid(EventType); std::unique_ptr> handler @@ -14,69 +15,65 @@ void EventManager::subscribe(const EventHandler & callback, int chann .channel = channel, .priority = priority, }); - // Sort handlers by priority (highest first) - std::sort(handlers.begin(), handlers.end(), [](const CallbackEntry &a, const CallbackEntry &b) { - return a.priority > b.priority; - }); + // Sort handlers by priority (highest first) + std::sort(handlers.begin(), handlers.end(), + [](const CallbackEntry & a, const CallbackEntry & b) { + return a.priority > b.priority; + }); } template -void EventManager::queue_event(const EventType & event, int channel,int priority) { - static_assert(std::is_base_of::value, "EventType must derive from Event"); +void EventManager::queue_event(const EventType & event, int channel, int priority) { + static_assert(std::is_base_of::value, + "EventType must derive from Event"); std::type_index event_type = typeid(EventType); auto event_ptr = std::make_unique(event); - this->events_queue.push_back( - QueueEntry{ - .event = std::move(event_ptr), - .channel = channel, - .type = event_type, - .priority = priority - } - ); + this->events_queue.push_back(QueueEntry{.event = std::move(event_ptr), + .channel = channel, + .type = event_type, + .priority = priority}); } template void EventManager::trigger_event(const EventType & event, int channel) { - std::type_index event_type = typeid(EventType); + std::type_index event_type = typeid(EventType); - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - const std::vector &handlers = handlers_it->second; + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + const std::vector & handlers = handlers_it->second; - for (const CallbackEntry &handler : handlers) { - if (handler.channel != channel && handler.channel != CHANNEL_ALL) { - continue; - } - if (handler.callback->exec(event)) { - break; - } - } - } + for (const CallbackEntry & handler : handlers) { + if (handler.channel != channel && handler.channel != CHANNEL_ALL) { + continue; + } + if (handler.callback->exec(event)) { + break; + } + } + } } - template void EventManager::unsubscribe(const EventHandler & callback, int channel) { - std::type_index event_type = typeid(EventType); - std::string handler_name = callback.target_type().name(); + std::type_index event_type = typeid(EventType); + std::string handler_name = callback.target_type().name(); - // Find the list of handlers for this event type - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - std::vector & handlers = handlers_it->second; + // Find the list of handlers for this event type + auto handlers_it = this->subscribers.find(event_type); + if (handlers_it != this->subscribers.end()) { + std::vector & handlers = handlers_it->second; - for (auto it = handlers.begin(); it != handlers.end(); ) { - // Match based on handler type and channel - if (it->callback->get_type() == handler_name && it->channel == channel) { - it = handlers.erase(it); - return; - } - ++it; - } - } + for (auto it = handlers.begin(); it != handlers.end();) { + // Match based on handler type and channel + if (it->callback->get_type() == handler_name && it->channel == channel) { + it = handlers.erase(it); + return; + } + ++it; + } + } } - } // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index 5e7d9bb..6a522c1 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -2,7 +2,6 @@ using namespace crepe; - // Constructor with specified channel IKeyListener::IKeyListener(int channel) : channel(channel), @@ -21,10 +20,8 @@ void IKeyListener::subscribe_events() { key_released_handler = [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }; - event_manager.subscribe(this->key_pressed_handler, - this->channel); - event_manager.subscribe(this->key_released_handler, - this->channel); + event_manager.subscribe(this->key_pressed_handler, this->channel); + event_manager.subscribe(this->key_released_handler, this->channel); } // Unsubscribe from key events @@ -32,4 +29,3 @@ void IKeyListener::unsubscribe_events() { event_manager.unsubscribe(this->key_pressed_handler, this->channel); event_manager.unsubscribe(this->key_released_handler, this->channel); } - diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 70243b4..9402cce 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -36,6 +36,7 @@ public: * \return True if the event was handled, false otherwise. */ virtual bool on_key_released(const KeyReleaseEvent & event) = 0; + protected: /** * \brief Subscribes to key events. diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index 6fd6c4b..f3ceb84 100644 --- a/src/crepe/api/IMouseListener.cpp +++ b/src/crepe/api/IMouseListener.cpp @@ -24,8 +24,7 @@ void IMouseListener::subscribe_events() { // Subscribe event handlers (no need for std::move) event_manager.subscribe(mouse_click_handler, this->channel); event_manager.subscribe(mouse_press_handler, this->channel); - event_manager.subscribe(mouse_release_handler, - this->channel); + event_manager.subscribe(mouse_release_handler, this->channel); event_manager.subscribe(mouse_move_handler, this->channel); } diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h index 1195a4e..6bc5716 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -55,6 +55,7 @@ public: * \return True if the event was handled, false otherwise. */ virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; + protected: /** * \brief Subscribes to mouse events on the specified channel. diff --git a/src/example/events.cpp b/src/example/events.cpp index 402a857..3dee9fa 100644 --- a/src/example/events.cpp +++ b/src/example/events.cpp @@ -63,9 +63,9 @@ public: } }; int main() { - + { - // two events to trigger + // two events to trigger KeyPressEvent key_press; key_press.key = Keycode::A; key_press.repeat = 0; @@ -106,10 +106,8 @@ int main() { EventManager::get_instance().subscribe(event_handler, CHANNEL_ALL); EventManager::get_instance().subscribe(event_handler, CHANNEL_ALL); EventManager::get_instance().subscribe(event_handler2, CHANNEL_ALL); - EventManager::get_instance().trigger_event(KeyPressEvent{ - .repeat = false, - .key = Keycode::A - }); + EventManager::get_instance().trigger_event( + KeyPressEvent{.repeat = false, .key = Keycode::A}); //EventManager::get_instance().unsubscribe(event_handler, 0); // testing trigger with testListener not in scope (unsubscribed) // EventManager::get_instance().trigger_event(key_press, 0); diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 3a78590..9dc2360 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -1,334 +1,308 @@ -#include "api/EventManager.h" #include "api/Event.h" +#include "api/EventManager.h" #include "api/IKeyListener.h" #include "api/IMouseListener.h" -#include -#include #include +#include using namespace std; using namespace std::chrono_literals; using namespace crepe; - class EventManagerTest : public ::testing::Test { protected: - void SetUp() override { - // Clear any existing subscriptions or events before each test - EventManager::get_instance().clear(); - } - - void TearDown() override { - // Ensure cleanup after each test - EventManager::get_instance().clear(); - } + void SetUp() override { + // Clear any existing subscriptions or events before each test + EventManager::get_instance().clear(); + } + + void TearDown() override { + // Ensure cleanup after each test + EventManager::get_instance().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)); + 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)); + 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 key_handler = [](const KeyPressEvent& e) { - std::cout << "Key Event Triggered" << std::endl; - return true; - }; - - // Subscribe to KeyPressEvent - EventManager::get_instance().subscribe(key_handler, 1); - - // Verify subscription (not directly verifiable; test by triggering event) - - EventManager::get_instance().trigger_event(KeyPressEvent{ - .repeat = true, - .key = Keycode::A, - }, 1); - EventManager::get_instance().trigger_event(KeyPressEvent{ - .repeat = true, - .key = Keycode::A, - - }, CHANNEL_ALL); - + EventHandler key_handler = [](const KeyPressEvent & e) { + std::cout << "Key Event Triggered" << std::endl; + return true; + }; + + // Subscribe to KeyPressEvent + EventManager::get_instance().subscribe(key_handler, 1); + + // Verify subscription (not directly verifiable; test by triggering event) + + EventManager::get_instance().trigger_event( + KeyPressEvent{ + .repeat = true, + .key = Keycode::A, + }, + 1); + EventManager::get_instance().trigger_event( + KeyPressEvent{ + .repeat = true, + .key = Keycode::A, + + }, + CHANNEL_ALL); } TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { - bool triggered = false; - - EventHandler mouse_handler = [&](const MouseClickEvent& e) { - triggered = true; - std::cout << "mouse handled" <(mouse_handler, CHANNEL_ALL); - - MouseClickEvent click_event{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE + bool triggered = false; + + EventHandler mouse_handler = [&](const MouseClickEvent & e) { + triggered = true; + std::cout << "mouse handled" << std::endl; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; }; + EventManager::get_instance().subscribe(mouse_handler, CHANNEL_ALL); + + MouseClickEvent click_event{ + .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; EventManager::get_instance().trigger_event(click_event, CHANNEL_ALL); - EXPECT_TRUE(triggered); + EXPECT_TRUE(triggered); } TEST_F(EventManagerTest, EventManagerTest_priority_order) { - EventManager& event_manager = EventManager::get_instance(); - - // Vector to track call order - std::vector call_order; - - // Handlers with different priorities - EventHandler handler_priority_3 = [&](const MouseClickEvent& e) { - call_order.push_back(3); - return false; // Allow propagation - }; - - EventHandler handler_priority_1 = [&](const MouseClickEvent& e) { - call_order.push_back(1); - return false; // Allow propagation - }; - - EventHandler handler_priority_2 = [&](const MouseClickEvent& e) { - call_order.push_back(2); - return false; // Allow propagation - }; - - // Subscribe handlers with different priorities - event_manager.subscribe(handler_priority_1, CHANNEL_ALL, 1); - event_manager.subscribe(handler_priority_3, CHANNEL_ALL, 3); - event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); + EventManager & event_manager = EventManager::get_instance(); + + // Vector to track call order + std::vector call_order; + + // Handlers with different priorities + EventHandler handler_priority_3 = [&](const MouseClickEvent & e) { + call_order.push_back(3); + return false; // Allow propagation + }; + + EventHandler handler_priority_1 = [&](const MouseClickEvent & e) { + call_order.push_back(1); + return false; // Allow propagation + }; - // Trigger the event - event_manager.trigger_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }, CHANNEL_ALL); + EventHandler handler_priority_2 = [&](const MouseClickEvent & e) { + call_order.push_back(2); + return false; // Allow propagation + }; + + // Subscribe handlers with different priorities + event_manager.subscribe(handler_priority_1, CHANNEL_ALL, 1); + event_manager.subscribe(handler_priority_3, CHANNEL_ALL, 3); + event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); - // Check the call order matches the expected priority order - std::vector expected_order = {3, 2, 1}; - EXPECT_EQ(call_order, expected_order); + // Trigger the event + event_manager.trigger_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, + CHANNEL_ALL); + // Check the call order matches the expected priority order + std::vector expected_order = {3, 2, 1}; + EXPECT_EQ(call_order, expected_order); } TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { - EventManager& event_manager = EventManager::get_instance(); - - // Flags to track handler calls - bool triggered_true = false; - bool triggered_false = false; - - // Handlers - EventHandler mouse_handler_true = [&](const MouseClickEvent& e) { - triggered_true = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return true; // Stops propagation - }; - - EventHandler mouse_handler_false = [&](const MouseClickEvent& e) { - triggered_false = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_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 - }; - - // First Scenario: True handler has higher priority - event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 1); - event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 0); - - // Trigger event - event_manager.trigger_event(click_event, CHANNEL_ALL); - - // Check that only the true handler was triggered - EXPECT_TRUE(triggered_true); - EXPECT_FALSE(triggered_false); - - // Reset and clear - triggered_true = false; - triggered_false = false; - event_manager.clear(); - - // Second Scenario: False handler has higher priority - event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 0); - event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 1); - - // Trigger event again - event_manager.trigger_event(click_event, CHANNEL_ALL); - - // Check that both handlers were triggered - EXPECT_TRUE(triggered_true); - EXPECT_TRUE(triggered_false); + EventManager & event_manager = EventManager::get_instance(); + + // Flags to track handler calls + bool triggered_true = false; + bool triggered_false = false; + + // Handlers + EventHandler mouse_handler_true = [&](const MouseClickEvent & e) { + triggered_true = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return true; // Stops propagation + }; + + EventHandler mouse_handler_false = [&](const MouseClickEvent & e) { + triggered_false = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_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}; + + // First Scenario: True handler has higher priority + event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 1); + event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 0); + + // Trigger event + event_manager.trigger_event(click_event, CHANNEL_ALL); + + // Check that only the true handler was triggered + EXPECT_TRUE(triggered_true); + EXPECT_FALSE(triggered_false); + + // Reset and clear + triggered_true = false; + triggered_false = false; + event_manager.clear(); + + // Second Scenario: False handler has higher priority + event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 0); + event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 1); + + // Trigger event again + event_manager.trigger_event(click_event, CHANNEL_ALL); + + // Check that both handlers were triggered + EXPECT_TRUE(triggered_true); + EXPECT_TRUE(triggered_false); } TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { - EventManager& event_manager = EventManager::get_instance(); + EventManager & event_manager = EventManager::get_instance(); bool triggered1 = false; bool triggered2 = false; int test_channel = 1; - EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { - triggered1 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { - triggered2 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; + EventHandler mouse_handler1 = [&](const MouseClickEvent & e) { + triggered1 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler2 = [&](const MouseClickEvent & e) { + triggered2 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; event_manager.subscribe(mouse_handler1); - event_manager.subscribe(mouse_handler2,test_channel); - - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }); - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - },test_channel); + event_manager.subscribe(mouse_handler2, test_channel); + + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, + test_channel); event_manager.dispatch_events(); EXPECT_TRUE(triggered1); EXPECT_TRUE(triggered2); } TEST_F(EventManagerTest, EventManagerTest_dispatch_priority) { - EventManager& event_manager = EventManager::get_instance(); + EventManager & event_manager = EventManager::get_instance(); std::vector call_order; int test_channel = 1; - EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { + EventHandler mouse_handler1 = [&](const MouseClickEvent & e) { call_order.push_back(1); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { - call_order.push_back(2); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - EventHandler mouse_handler3 = [&](const MouseClickEvent& e) { - call_order.push_back(3); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - event_manager.subscribe(mouse_handler2,CHANNEL_ALL,2); - event_manager.subscribe(mouse_handler1,CHANNEL_ALL,1); - event_manager.subscribe(mouse_handler3,CHANNEL_ALL,3); - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler2 = [&](const MouseClickEvent & e) { + call_order.push_back(2); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler3 = [&](const MouseClickEvent & e) { + call_order.push_back(3); + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + event_manager.subscribe(mouse_handler2, CHANNEL_ALL, 2); + event_manager.subscribe(mouse_handler1, CHANNEL_ALL, 1); + event_manager.subscribe(mouse_handler3, CHANNEL_ALL, 3); + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); event_manager.dispatch_events(); std::vector expected_order = {3, 2, 1}; - EXPECT_EQ(call_order, expected_order); + EXPECT_EQ(call_order, expected_order); } TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { - EventManager& event_manager = EventManager::get_instance(); - - // Flags to track if handlers are triggered - bool triggered1 = false; - bool triggered2 = false; - - // Define EventHandlers - EventHandler mouse_handler1 = [&](const MouseClickEvent& e) { - triggered1 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - - EventHandler mouse_handler2 = [&](const MouseClickEvent& e) { - triggered2 = true; - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - // Subscribe handlers - event_manager.subscribe(mouse_handler1); - event_manager.subscribe(mouse_handler2); - - // Queue events - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }); - - // Dispatch events - both handlers should be triggered - event_manager.dispatch_events(); - EXPECT_TRUE(triggered1); // Handler 1 should be triggered - EXPECT_TRUE(triggered2); // Handler 2 should be triggered - - // Reset flags - triggered1 = false; - triggered2 = false; - - // Unsubscribe handler1 - event_manager.unsubscribe(mouse_handler1); - - // Queue the same event again - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }); - - // Dispatch events - only handler 2 should be triggered, handler 1 should NOT - event_manager.dispatch_events(); - EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered - EXPECT_TRUE(triggered2); // Handler 2 should be triggered - - // Reset flags - triggered2 = false; - - // Unsubscribe handler2 - event_manager.unsubscribe(mouse_handler2); - - // Queue the event again - event_manager.queue_event(MouseClickEvent{ - .mouse_x = 100, - .mouse_y = 200, - .button = MouseButton::LEFT_MOUSE - }); - - // Dispatch events - no handler should be triggered - event_manager.dispatch_events(); - EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered - EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered -} + EventManager & event_manager = EventManager::get_instance(); + + // Flags to track if handlers are triggered + bool triggered1 = false; + bool triggered2 = false; + + // Define EventHandlers + EventHandler mouse_handler1 = [&](const MouseClickEvent & e) { + triggered1 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + EventHandler mouse_handler2 = [&](const MouseClickEvent & e) { + triggered2 = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; // Allows propagation + }; + // Subscribe handlers + event_manager.subscribe(mouse_handler1); + event_manager.subscribe(mouse_handler2); + + // Queue events + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + + // Dispatch events - both handlers should be triggered + event_manager.dispatch_events(); + EXPECT_TRUE(triggered1); // Handler 1 should be triggered + EXPECT_TRUE(triggered2); // Handler 2 should be triggered + + // Reset flags + triggered1 = false; + triggered2 = false; + + // Unsubscribe handler1 + event_manager.unsubscribe(mouse_handler1); + + // Queue the same event again + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + + // Dispatch events - only handler 2 should be triggered, handler 1 should NOT + event_manager.dispatch_events(); + EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered + EXPECT_TRUE(triggered2); // Handler 2 should be triggered + + // Reset flags + triggered2 = false; + + // Unsubscribe handler2 + event_manager.unsubscribe(mouse_handler2); + + // Queue the event again + event_manager.queue_event( + MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); + + // Dispatch events - no handler should be triggered + event_manager.dispatch_events(); + EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered + EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered +} -- cgit v1.2.3 From 397da65e03ec681922aeea3881918026d36068a7 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 20 Nov 2024 20:24:17 +0100 Subject: result of loeks temper tantrum --- src/crepe/api/EventHandler.h | 23 +----- src/crepe/api/EventHandler.hpp | 9 +-- src/crepe/api/EventManager.cpp | 42 +++++++---- src/crepe/api/EventManager.h | 158 ++++++++++++++++++++++----------------- src/crepe/api/EventManager.hpp | 38 ++-------- src/crepe/api/IKeyListener.cpp | 26 ++----- src/crepe/api/IKeyListener.h | 26 ++----- src/crepe/api/IMouseListener.cpp | 47 +++++------- src/crepe/api/IMouseListener.h | 34 +++------ src/example/events.cpp | 120 ----------------------------- src/test/EventTest.cpp | 118 ++++++++--------------------- 11 files changed, 198 insertions(+), 443 deletions(-) delete mode 100644 src/example/events.cpp (limited to 'src/test') diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h index 90886aa..ef659fd 100644 --- a/src/crepe/api/EventHandler.h +++ b/src/crepe/api/EventHandler.h @@ -24,7 +24,7 @@ using EventHandler = std::function; * \brief An abstract base class for event handler wrappers. * * This class provides the interface for handling events. Derived classes must implement the - * `call()` method to process events and the `get_type()` method to return the handler's type. + * `call()` method to process events */ class IEventHandlerWrapper { public: @@ -43,15 +43,6 @@ public: */ bool exec(const Event & e); - /** - * \brief Get the type of the event handler. - * - * This method returns the type of the event handler as a string. - * - * \return A string representing the handler's type. - */ - virtual std::string get_type() const = 0; - private: /** * \brief The method responsible for handling the event. @@ -96,20 +87,8 @@ private: * \return A boolean value indicating whether the event is handled. */ bool call(const Event & e) override; - - /** - * \brief Returns the type of the handler. - * - * This method returns a string representing the type of the event handler. - * - * \return The handler type as a string. - */ - std::string get_type() const override; - //! The event handler function. EventHandler handler; - //! The type name of the handler function. - const std::string handler_type; }; } // namespace crepe diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp index a1e774d..8d8136b 100644 --- a/src/crepe/api/EventHandler.hpp +++ b/src/crepe/api/EventHandler.hpp @@ -1,4 +1,3 @@ - #include #include "EventHandler.h" @@ -8,8 +7,7 @@ namespace crepe { // Implementation of EventHandlerWrapper constructor template EventHandlerWrapper::EventHandlerWrapper(const EventHandler & handler) - : handler(handler), - handler_type(handler.target_type().name()) {} + : handler(handler){} // Implementation of EventHandlerWrapper::call template @@ -17,10 +15,5 @@ bool EventHandlerWrapper::call(const Event & e) { return this->handler(static_cast(e)); } -// Implementation of EventHandlerWrapper::get_type -template -std::string EventHandlerWrapper::get_type() const { - return this->handler_type; -} } //namespace crepe diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index dbdb0c3..64d7c26 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -15,22 +15,18 @@ void EventManager::dispatch_events() { bool event_handled = false; auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - std::vector & handlers = handlers_it->second; - - std::sort(handlers.begin(), handlers.end(), - [](const CallbackEntry & a, const CallbackEntry & b) { - return a.priority > b.priority; - }); - - for (auto handler_it = handlers.begin(); handler_it != handlers.end(); - ++handler_it) { - // If callback is executed and returns true, remove the event from the queue - if ((*handler_it).callback->exec(*event)) { - event_it = this->events_queue.erase(event_it); - event_handled = true; - break; - } + if (handlers_it == this->subscribers.end()) { + continue; + } + std::vector & handlers = handlers_it->second; + + for (auto handler_it = handlers.begin(); handler_it != handlers.end(); + ++handler_it) { + // If callback is executed and returns true, remove the event from the queue + if ((*handler_it).callback->exec(*event)) { + event_it = this->events_queue.erase(event_it); + event_handled = true; + break; } } @@ -44,3 +40,17 @@ void EventManager::clear() { this->subscribers.clear(); this->events_queue.clear(); } + +void EventManager::unsubscribe(subscription_t event_id) { + for (auto& [event_type, handlers] : this->subscribers) { + for (auto it = handlers.begin(); it != handlers.end();) { + if (it->id == event_id) { + it = handlers.erase(it); + return; + } else { + ++it; + } + } + } +} + diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h index 133d72d..93e9ca2 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/api/EventManager.h @@ -11,109 +11,131 @@ #include "EventHandler.h" namespace crepe { -static constexpr int CHANNEL_ALL = -1; +//! typedef for subscription value +typedef int subscription_t; + /** * \class EventManager - * \brief The EventManager class is responsible for managing the subscription, triggering, - * and queueing of events. It handles events and dispatches them to appropriate subscribers. + * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling. + * + * The `EventManager` acts as a centralized event system. It allows for registering callbacks + * for specific event types, triggering events synchronously, queueing events for later + * processing, and managing subscriptions via unique identifiers. */ class EventManager { public: + static constexpr int CHANNEL_ALL = -1; + /** - * \brief Get the singleton instance of the EventManager. - * - * This method returns the unique instance of the EventManager, creating it on the first call. - * - * \return Reference to the EventManager instance. - */ + * \brief Get the singleton instance of the EventManager. + * + * This method returns the unique instance of the EventManager, creating it if it + * doesn't already exist. Ensures only one instance is active in the program. + * + * \return Reference to the singleton instance of the EventManager. + */ static EventManager & get_instance(); /** - * \brief Subscribe to an event. - * - * This method allows the registration of a callback for a specific event type and channel. - * - * \tparam EventType The type of the event to subscribe to. - * \param callback The callback function to invoke when the event is triggered. - * \param channel The channel number to subscribe to (default is 0). - */ + * \brief Subscribe to a specific event type. + * + * Registers a callback for a given event type and optional channel. Each callback + * is assigned a unique subscription ID that can be used for later unsubscription. + * + * \tparam EventType The type of the event to subscribe to. + * \param callback The callback function to be invoked when the event is triggered. + * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels). + * \return A unique subscription ID associated with the registered callback. + */ template - void subscribe(const EventHandler & callback, int channel = CHANNEL_ALL, - int priority = 0); + subscription_t subscribe(const EventHandler & callback, int channel = CHANNEL_ALL); /** - * \brief Unsubscribe from an event. - * - * This method removes a previously registered callback from an event. - * - * \tparam EventType The type of the event to unsubscribe from. - * \param callback The callback function to remove from the subscription list. - * \param channel The event ID to unsubscribe from. - */ - template - void unsubscribe(const EventHandler &, int channel = CHANNEL_ALL); + * \brief Unsubscribe a previously registered callback. + * + * Removes a callback from the subscription list based on its unique subscription ID. + * + * \param event_id The unique subscription ID of the callback to remove. + */ + void unsubscribe(subscription_t event_id); /** - * \brief Trigger an event. - * - * This method invokes the appropriate callback(s) for the specified event. - * - * \tparam EventType The type of the event to trigger. - * \param event The event data to pass to the callback. - * \param channel The channel from which to trigger the event (default is 0). - */ + * \brief Trigger an event immediately. + * + * Synchronously invokes all registered callbacks for the given event type on the specified channel. + * + * \tparam EventType The type of the event to trigger. + * \param event The event instance to pass to the callbacks. + * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels). + */ template void trigger_event(const EventType & event, int channel = CHANNEL_ALL); /** - * \brief Queue an event for later processing. - * - * This method adds an event to the event queue, which will be processed in the - * dispatch_events function. - * - * \tparam EventType The type of the event to queue. - * \param event The event to queue. - * \param channel The channel number for the event (default is 0). - */ + * \brief Queue an event for later processing. + * + * Adds an event to the event queue to be processed during the next call to `dispatch_events`. + * + * \tparam EventType The type of the event to queue. + * \param event The event instance to queue. + * \param channel The channel to associate with the event (default is CHANNEL_ALL). + */ template - void queue_event(const EventType & event, int channel = CHANNEL_ALL, int priority = 0); + void queue_event(const EventType & event, int channel = CHANNEL_ALL); /** - * \brief Dispatch all queued events. - * - * This method processes all events in the event queue and triggers the corresponding - * callbacks for each event. - */ + * \brief Process all queued events. + * + * Iterates through the event queue and triggers callbacks for each queued event. + * Events are removed from the queue once processed. + */ void dispatch_events(); + /** - * \brief clears all subscribers - * - */ + * \brief Clear all subscriptions. + * + * Removes all registered event handlers and clears the subscription list. + */ void clear(); private: /** - * \brief Default constructor for the EventManager. - * - * This constructor is private to enforce the singleton pattern. - */ + * \brief Default constructor for the EventManager. + * + * Constructor is private to enforce the singleton pattern. + */ EventManager() = default; + + /** + * \struct QueueEntry + * \brief Represents an entry in the event queue. + */ struct QueueEntry { - std::unique_ptr event; - int channel = CHANNEL_ALL; - std::type_index type; - int priority = 0; + std::unique_ptr event; ///< The event instance. + int channel = CHANNEL_ALL; ///< The channel associated with the event. + std::type_index type; ///< The type of the event. }; + + /** + * \struct CallbackEntry + * \brief Represents a registered event handler callback. + */ struct CallbackEntry { - std::unique_ptr callback; - int channel = CHANNEL_ALL; - int priority = 0; + std::unique_ptr callback; ///< The callback function wrapper. + int channel = CHANNEL_ALL; ///< The channel this callback listens to. + subscription_t id = -1; ///< Unique subscription ID. }; - //! The queue of events to be processed. + + //! The queue of events to be processed during dispatch. std::vector events_queue; - //! Registered event handlers. + + //! A map of event type to registered callbacks. std::unordered_map> subscribers; + + //! Counter to generate unique subscription IDs. + subscription_t subscription_counter = 0; }; } // namespace crepe + #include "EventManager.hpp" diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index a04a43a..d7afa9f 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -3,9 +3,8 @@ namespace crepe { template -void EventManager::subscribe(const EventHandler & callback, int channel, - int priority) { - +subscription_t EventManager::subscribe(const EventHandler & callback, int channel) { + subscription_counter++; std::type_index event_type = typeid(EventType); std::unique_ptr> handler = std::make_unique>(callback); @@ -13,17 +12,13 @@ void EventManager::subscribe(const EventHandler & callback, int chann handlers.emplace_back(CallbackEntry{ .callback = std::move(handler), .channel = channel, - .priority = priority, + .id = subscription_counter }); - // Sort handlers by priority (highest first) - std::sort(handlers.begin(), handlers.end(), - [](const CallbackEntry & a, const CallbackEntry & b) { - return a.priority > b.priority; - }); + return subscription_counter; } template -void EventManager::queue_event(const EventType & event, int channel, int priority) { +void EventManager::queue_event(const EventType & event, int channel) { static_assert(std::is_base_of::value, "EventType must derive from Event"); std::type_index event_type = typeid(EventType); @@ -32,8 +27,7 @@ void EventManager::queue_event(const EventType & event, int channel, int priorit this->events_queue.push_back(QueueEntry{.event = std::move(event_ptr), .channel = channel, - .type = event_type, - .priority = priority}); + .type = event_type}); } template @@ -55,25 +49,5 @@ void EventManager::trigger_event(const EventType & event, int channel) { } } -template -void EventManager::unsubscribe(const EventHandler & callback, int channel) { - std::type_index event_type = typeid(EventType); - std::string handler_name = callback.target_type().name(); - - // Find the list of handlers for this event type - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - std::vector & handlers = handlers_it->second; - - for (auto it = handlers.begin(); it != handlers.end();) { - // Match based on handler type and channel - if (it->callback->get_type() == handler_name && it->channel == channel) { - it = handlers.erase(it); - return; - } - ++it; - } - } -} } // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index 6a522c1..ebbf486 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -3,29 +3,15 @@ using namespace crepe; // Constructor with specified channel -IKeyListener::IKeyListener(int channel) - : channel(channel), - active(true), +IKeyListener::IKeyListener(int channel) : event_manager(EventManager::get_instance()) { - this->subscribe_events(); + press_id = event_manager.subscribe([this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); + release_id = event_manager.subscribe([this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, channel); } // Destructor, unsubscribe events -IKeyListener::~IKeyListener() { this->unsubscribe_events(); } - -// Subscribe to key events -void IKeyListener::subscribe_events() { - key_pressed_handler - = [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }; - key_released_handler - = [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }; - - event_manager.subscribe(this->key_pressed_handler, this->channel); - event_manager.subscribe(this->key_released_handler, this->channel); +IKeyListener::~IKeyListener() { + event_manager.unsubscribe(press_id); + event_manager.unsubscribe(release_id); } -// Unsubscribe from key events -void IKeyListener::unsubscribe_events() { - event_manager.unsubscribe(this->key_pressed_handler, this->channel); - event_manager.unsubscribe(this->key_released_handler, this->channel); -} diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 9402cce..4726aa7 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -16,7 +16,7 @@ public: * \brief Constructs an IKeyListener with a specified channel. * \param channel The channel ID for event handling. */ - IKeyListener(int channel = CHANNEL_ALL); + IKeyListener(int channel = EventManager::CHANNEL_ALL); virtual ~IKeyListener(); IKeyListener(const IKeyListener &) = delete; IKeyListener & operator=(const IKeyListener &) = delete; @@ -36,27 +36,11 @@ public: * \return True if the event was handled, false otherwise. */ virtual bool on_key_released(const KeyReleaseEvent & event) = 0; - -protected: - /** - * \brief Subscribes to key events. - */ - void subscribe_events(); - - /** - * \brief Unsubscribes from key events. - */ - void unsubscribe_events(); - private: - //! Indicates whether key listening is active. - bool active = true; - //! Channel ID for event handling. - int channel = 0; - //! Key press event handler. - EventHandler key_pressed_handler; - //!< Key release event handler. - EventHandler key_released_handler; + //! Key press event id + subscription_t press_id = -1; + //!< Key release event id + subscription_t release_id = -1; EventManager & event_manager; }; diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index f3ceb84..a6cb163 100644 --- a/src/crepe/api/IMouseListener.cpp +++ b/src/crepe/api/IMouseListener.cpp @@ -3,35 +3,28 @@ using namespace crepe; IMouseListener::IMouseListener(int channel) - : event_manager(EventManager::get_instance()), - channel(channel) { - this->subscribe_events(); -} + : event_manager(EventManager::get_instance()) { + click_id = event_manager.subscribe( + [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, + channel); -IMouseListener::~IMouseListener() { this->unsubscribe_events(); } + press_id = event_manager.subscribe( + [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, + channel); -void IMouseListener::subscribe_events() { - // Define handler lambdas and subscribe them - mouse_click_handler - = [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }; - mouse_press_handler - = [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }; - mouse_release_handler - = [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }; - mouse_move_handler - = [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }; + release_id = event_manager.subscribe( + [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, + channel); - // Subscribe event handlers (no need for std::move) - event_manager.subscribe(mouse_click_handler, this->channel); - event_manager.subscribe(mouse_press_handler, this->channel); - event_manager.subscribe(mouse_release_handler, this->channel); - event_manager.subscribe(mouse_move_handler, this->channel); + move_id = event_manager.subscribe( + [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, + channel); } -void IMouseListener::unsubscribe_events() { - // Unsubscribe event handlers - event_manager.unsubscribe(mouse_click_handler, this->channel); - event_manager.unsubscribe(mouse_press_handler, this->channel); - event_manager.unsubscribe(mouse_release_handler, this->channel); - event_manager.unsubscribe(mouse_move_handler, this->channel); -} +IMouseListener::~IMouseListener() { + // Unsubscribe event handlers + event_manager.unsubscribe(click_id); + event_manager.unsubscribe(press_id); + event_manager.unsubscribe(release_id); + event_manager.unsubscribe(move_id); + } diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h index 6bc5716..91b33e1 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -16,7 +16,7 @@ public: * \brief Constructs an IMouseListener with a specified channel. * \param channel The channel ID for event handling. */ - IMouseListener(int channel = CHANNEL_ALL); + IMouseListener(int channel = EventManager::CHANNEL_ALL); virtual ~IMouseListener(); IMouseListener & operator=(const IMouseListener &) = delete; IMouseListener(const IMouseListener &) = delete; @@ -56,30 +56,16 @@ public: */ virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; -protected: - /** - * \brief Subscribes to mouse events on the specified channel. - */ - void subscribe_events(); - - /** - * \brief Unsubscribes from mouse events on the specified channel. - */ - void unsubscribe_events(); - private: - //! Indicates whether mouse listening is active. - bool active = true; - //! Channel ID for event handling. - int channel = 0; - //! Mouse click event handler. - EventHandler mouse_click_handler; - //! Mouse press event handler. - EventHandler mouse_press_handler; - //! Mouse release event handler. - EventHandler mouse_release_handler; - //! Mouse move event handler. - EventHandler mouse_move_handler; + //! Mouse click event id + subscription_t click_id = -1; + //! Mouse press event id + subscription_t press_id = -1; + //! Mouse release event id + subscription_t release_id = -1; + //! Mouse move event id + subscription_t move_id = -1; + //! EventManager reference EventManager & event_manager; }; diff --git a/src/example/events.cpp b/src/example/events.cpp deleted file mode 100644 index 3dee9fa..0000000 --- a/src/example/events.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace crepe; -using namespace std; - -class MyScript : public Script, public IKeyListener, public IMouseListener { - void update() { - // Retrieve component from the same GameObject this script is on - Transform & test = get_component(); - dbg_logf("Transform(%.2f, %.2f)", test.position.x, test.position.y); - } - - bool on_key_pressed(const KeyPressEvent & event) override { - std::cout << "KeyPressed function" << std::endl; - return false; - } - bool on_key_released(const KeyReleaseEvent & event) override { - std::cout << "KeyRelease function" << std::endl; - return false; - } - bool on_mouse_clicked(const MouseClickEvent & event) override { - std::cout << "MouseClick function" << std::endl; - return false; - } - bool on_mouse_pressed(const MousePressEvent & event) override { - std::cout << "MousePress function" << std::endl; - return false; - } - bool on_mouse_released(const MouseReleaseEvent & event) override { - std::cout << "MouseRelease function" << std::endl; - return false; - } - bool on_mouse_moved(const MouseMoveEvent & event) override { - std::cout << "MouseMove function" << std::endl; - return false; - } -}; -class TestKeyListener : public IKeyListener { -public: - bool on_key_pressed(const KeyPressEvent & event) override { - std::cout << "TestKeyListener: Key Pressed - Code: " << static_cast(event.key) - << std::endl; - return true; // Return true if the listener should remain active - } - bool on_key_released(const KeyReleaseEvent & event) override { - std::cout << "TestKeyListener: Key Released - Code: " << static_cast(event.key) - << std::endl; - return true; - } -}; -int main() { - - { - // two events to trigger - KeyPressEvent key_press; - key_press.key = Keycode::A; - key_press.repeat = 0; - MouseClickEvent click_event; - click_event.button = MouseButton::LEFT_MOUSE; - click_event.mouse_x = 100; - click_event.mouse_y = 200; - // queue events to test queue - EventManager::get_instance().queue_event(key_press); - EventManager::get_instance().queue_event(click_event); - TestKeyListener test_listener; - //auto obj = GameObject(0, "name", "tag", Vector2{1.2, 3.4}, 0, 1); - //obj.add_component().set_script(); - - //ScriptSystem sys; - //sys.update(); - - // Trigger the events while `testListener` is in scope - //EventManager::get_instance().trigger_event(key_press, 1); - // EventManager::get_instance().trigger_event( - // MouseClickEvent{ - // .mouse_x = 100, - // .mouse_y = 100, - // .button = MouseButton::LEFT_MOUSE, - // }, - // 1); - //EventManager::get_instance().trigger_event(click_event, 0); - } - // custom lambda event handler - EventHandler event_handler = [](const KeyPressEvent & e) { - std::cout << "key lambda test" << std::endl; - return true; - }; - EventHandler event_handler2 = [](const MouseClickEvent & e) { - std::cout << "mouse lambda test" << std::endl; - return false; - }; - EventManager::get_instance().subscribe(event_handler, CHANNEL_ALL); - EventManager::get_instance().subscribe(event_handler, CHANNEL_ALL); - EventManager::get_instance().subscribe(event_handler2, CHANNEL_ALL); - EventManager::get_instance().trigger_event( - KeyPressEvent{.repeat = false, .key = Keycode::A}); - //EventManager::get_instance().unsubscribe(event_handler, 0); - // testing trigger with testListener not in scope (unsubscribed) - // EventManager::get_instance().trigger_event(key_press, 0); - // EventManager::get_instance().trigger_event(click_event, 0); - // dispatching queued events - //EventManager::get_instance().dispatch_events(); - - EventManager::get_instance().unsubscribe(event_handler); - return EXIT_SUCCESS; -} diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 9dc2360..d42e742 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -34,6 +34,7 @@ public: 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 key_handler = [](const KeyPressEvent & e) { std::cout << "Key Event Triggered" << std::endl; @@ -57,65 +58,47 @@ TEST_F(EventManagerTest, EventSubscription) { .key = Keycode::A, }, - CHANNEL_ALL); + EventManager::CHANNEL_ALL); } TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { bool triggered = false; EventHandler mouse_handler = [&](const MouseClickEvent & e) { triggered = true; - std::cout << "mouse handled" << std::endl; EXPECT_EQ(e.mouse_x, 100); EXPECT_EQ(e.mouse_y, 200); EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, CHANNEL_ALL); + EventManager::get_instance().subscribe(mouse_handler, EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, CHANNEL_ALL); + EventManager::get_instance().trigger_event(click_event, EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } - -TEST_F(EventManagerTest, EventManagerTest_priority_order) { - EventManager & event_manager = EventManager::get_instance(); - - // Vector to track call order - std::vector call_order; - - // Handlers with different priorities - EventHandler handler_priority_3 = [&](const MouseClickEvent & e) { - call_order.push_back(3); - return false; // Allow propagation - }; - - EventHandler handler_priority_1 = [&](const MouseClickEvent & e) { - call_order.push_back(1); - return false; // Allow propagation - }; - - EventHandler handler_priority_2 = [&](const MouseClickEvent & e) { - call_order.push_back(2); - return false; // Allow propagation +TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { + bool triggered = false; + int test_channel = 1; + EventHandler mouse_handler = [&](const MouseClickEvent & e) { + triggered = true; + EXPECT_EQ(e.mouse_x, 100); + EXPECT_EQ(e.mouse_y, 200); + EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); + return false; }; + EventManager::get_instance().subscribe(mouse_handler, test_channel); - // Subscribe handlers with different priorities - event_manager.subscribe(handler_priority_1, CHANNEL_ALL, 1); - event_manager.subscribe(handler_priority_3, CHANNEL_ALL, 3); - event_manager.subscribe(handler_priority_2, CHANNEL_ALL, 2); - - // Trigger the event - event_manager.trigger_event( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}, - CHANNEL_ALL); + MouseClickEvent click_event{ + .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; + EventManager::get_instance().trigger_event(click_event, EventManager::CHANNEL_ALL); - // Check the call order matches the expected priority order - std::vector expected_order = {3, 2, 1}; - EXPECT_EQ(call_order, expected_order); + EXPECT_FALSE(triggered); + EventManager::get_instance().trigger_event(click_event, test_channel); } + TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { EventManager & event_manager = EventManager::get_instance(); @@ -143,13 +126,13 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { // Test event MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - - // First Scenario: True handler has higher priority - event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 1); - event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 0); + event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + + // Trigger event - event_manager.trigger_event(click_event, CHANNEL_ALL); + event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that only the true handler was triggered EXPECT_TRUE(triggered_true); @@ -159,13 +142,12 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { triggered_true = false; triggered_false = false; event_manager.clear(); - - // Second Scenario: False handler has higher priority - event_manager.subscribe(mouse_handler_true, CHANNEL_ALL, 0); - event_manager.subscribe(mouse_handler_false, CHANNEL_ALL, 1); + event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); + event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); + // Trigger event again - event_manager.trigger_event(click_event, CHANNEL_ALL); + event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); // Check that both handlers were triggered EXPECT_TRUE(triggered_true); @@ -204,40 +186,6 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { EXPECT_TRUE(triggered2); } -TEST_F(EventManagerTest, EventManagerTest_dispatch_priority) { - EventManager & event_manager = EventManager::get_instance(); - std::vector call_order; - int test_channel = 1; - EventHandler mouse_handler1 = [&](const MouseClickEvent & e) { - call_order.push_back(1); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - EventHandler mouse_handler2 = [&](const MouseClickEvent & e) { - call_order.push_back(2); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - EventHandler mouse_handler3 = [&](const MouseClickEvent & e) { - call_order.push_back(3); - EXPECT_EQ(e.mouse_x, 100); - EXPECT_EQ(e.mouse_y, 200); - EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); - return false; // Allows propagation - }; - event_manager.subscribe(mouse_handler2, CHANNEL_ALL, 2); - event_manager.subscribe(mouse_handler1, CHANNEL_ALL, 1); - event_manager.subscribe(mouse_handler3, CHANNEL_ALL, 3); - event_manager.queue_event( - MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}); - event_manager.dispatch_events(); - std::vector expected_order = {3, 2, 1}; - EXPECT_EQ(call_order, expected_order); -} TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { EventManager & event_manager = EventManager::get_instance(); @@ -263,8 +211,8 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { return false; // Allows propagation }; // Subscribe handlers - event_manager.subscribe(mouse_handler1); - event_manager.subscribe(mouse_handler2); + subscription_t handler1_id = event_manager.subscribe(mouse_handler1); + subscription_t handler2_id = event_manager.subscribe(mouse_handler2); // Queue events event_manager.queue_event( @@ -280,7 +228,7 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler1 - event_manager.unsubscribe(mouse_handler1); + event_manager.unsubscribe(handler1_id); // Queue the same event again event_manager.queue_event( @@ -295,7 +243,7 @@ TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { triggered2 = false; // Unsubscribe handler2 - event_manager.unsubscribe(mouse_handler2); + event_manager.unsubscribe(handler2_id); // Queue the event again event_manager.queue_event( -- cgit v1.2.3 From 5ec30d8915debef409f6db6f8ee9b822fb24d46b Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 20 Nov 2024 20:30:33 +0100 Subject: make format --- src/crepe/api/EventHandler.hpp | 3 +-- src/crepe/api/EventManager.cpp | 24 +++++++++++------------- src/crepe/api/EventManager.h | 11 ++++++----- src/crepe/api/EventManager.hpp | 11 +++-------- src/crepe/api/IKeyListener.cpp | 17 +++++++++-------- src/crepe/api/IKeyListener.h | 6 ++++-- src/crepe/api/IMouseListener.cpp | 26 ++++++++++++-------------- src/test/EventTest.cpp | 14 ++++++-------- 8 files changed, 52 insertions(+), 60 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp index 8d8136b..391dcca 100644 --- a/src/crepe/api/EventHandler.hpp +++ b/src/crepe/api/EventHandler.hpp @@ -7,7 +7,7 @@ namespace crepe { // Implementation of EventHandlerWrapper constructor template EventHandlerWrapper::EventHandlerWrapper(const EventHandler & handler) - : handler(handler){} + : handler(handler) {} // Implementation of EventHandlerWrapper::call template @@ -15,5 +15,4 @@ bool EventHandlerWrapper::call(const Event & e) { return this->handler(static_cast(e)); } - } //namespace crepe diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index 64d7c26..993db86 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -20,8 +20,7 @@ void EventManager::dispatch_events() { } std::vector & handlers = handlers_it->second; - for (auto handler_it = handlers.begin(); handler_it != handlers.end(); - ++handler_it) { + for (auto handler_it = handlers.begin(); handler_it != handlers.end(); ++handler_it) { // If callback is executed and returns true, remove the event from the queue if ((*handler_it).callback->exec(*event)) { event_it = this->events_queue.erase(event_it); @@ -42,15 +41,14 @@ void EventManager::clear() { } void EventManager::unsubscribe(subscription_t event_id) { - for (auto& [event_type, handlers] : this->subscribers) { - for (auto it = handlers.begin(); it != handlers.end();) { - if (it->id == event_id) { - it = handlers.erase(it); - return; - } else { - ++it; - } - } - } + for (auto & [event_type, handlers] : this->subscribers) { + for (auto it = handlers.begin(); it != handlers.end();) { + if (it->id == event_id) { + it = handlers.erase(it); + return; + } else { + ++it; + } + } + } } - diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h index 93e9ca2..bd9772a 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/api/EventManager.h @@ -48,7 +48,8 @@ public: * \return A unique subscription ID associated with the registered callback. */ template - subscription_t subscribe(const EventHandler & callback, int channel = CHANNEL_ALL); + subscription_t subscribe(const EventHandler & callback, + int channel = CHANNEL_ALL); /** * \brief Unsubscribe a previously registered callback. @@ -112,8 +113,8 @@ private: */ struct QueueEntry { std::unique_ptr event; ///< The event instance. - int channel = CHANNEL_ALL; ///< The channel associated with the event. - std::type_index type; ///< The type of the event. + int channel = CHANNEL_ALL; ///< The channel associated with the event. + std::type_index type; ///< The type of the event. }; /** @@ -122,8 +123,8 @@ private: */ struct CallbackEntry { std::unique_ptr callback; ///< The callback function wrapper. - int channel = CHANNEL_ALL; ///< The channel this callback listens to. - subscription_t id = -1; ///< Unique subscription ID. + int channel = CHANNEL_ALL; ///< The channel this callback listens to. + subscription_t id = -1; ///< Unique subscription ID. }; //! The queue of events to be processed during dispatch. diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index d7afa9f..b2a94bd 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -10,10 +10,7 @@ subscription_t EventManager::subscribe(const EventHandler & callback, = std::make_unique>(callback); std::vector & handlers = this->subscribers[event_type]; handlers.emplace_back(CallbackEntry{ - .callback = std::move(handler), - .channel = channel, - .id = subscription_counter - }); + .callback = std::move(handler), .channel = channel, .id = subscription_counter}); return subscription_counter; } @@ -25,9 +22,8 @@ void EventManager::queue_event(const EventType & event, int channel) { auto event_ptr = std::make_unique(event); - this->events_queue.push_back(QueueEntry{.event = std::move(event_ptr), - .channel = channel, - .type = event_type}); + this->events_queue.push_back( + QueueEntry{.event = std::move(event_ptr), .channel = channel, .type = event_type}); } template @@ -49,5 +45,4 @@ void EventManager::trigger_event(const EventType & event, int channel) { } } - } // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index ebbf486..7aefaf7 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -3,15 +3,16 @@ using namespace crepe; // Constructor with specified channel -IKeyListener::IKeyListener(int channel) : - event_manager(EventManager::get_instance()) { - press_id = event_manager.subscribe([this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); - release_id = event_manager.subscribe([this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, channel); +IKeyListener::IKeyListener(int channel) : event_manager(EventManager::get_instance()) { + this->press_id = event_manager.subscribe( + [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); + this->release_id = event_manager.subscribe( + [this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, + channel); } // Destructor, unsubscribe events -IKeyListener::~IKeyListener() { - event_manager.unsubscribe(press_id); - event_manager.unsubscribe(release_id); +IKeyListener::~IKeyListener() { + event_manager.unsubscribe(this->press_id); + event_manager.unsubscribe(this->release_id); } - diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 4726aa7..2a89cbc 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -36,12 +36,14 @@ public: * \return True if the event was handled, false otherwise. */ virtual bool on_key_released(const KeyReleaseEvent & event) = 0; + private: //! Key press event id subscription_t press_id = -1; - //!< Key release event id + //! Key release event id subscription_t release_id = -1; - EventManager & event_manager; + //! EventManager reference + EventManager & event_manager;; }; } // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index a6cb163..7d38280 100644 --- a/src/crepe/api/IMouseListener.cpp +++ b/src/crepe/api/IMouseListener.cpp @@ -2,29 +2,27 @@ using namespace crepe; -IMouseListener::IMouseListener(int channel) - : event_manager(EventManager::get_instance()) { - click_id = event_manager.subscribe( +IMouseListener::IMouseListener(int channel) : event_manager(EventManager::get_instance()) { + this->click_id = event_manager.subscribe( [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, channel); - press_id = event_manager.subscribe( + this->press_id = event_manager.subscribe( [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, channel); - release_id = event_manager.subscribe( + this->release_id = event_manager.subscribe( [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, channel); - move_id = event_manager.subscribe( - [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, - channel); + this->move_id = event_manager.subscribe( + [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel); } IMouseListener::~IMouseListener() { - // Unsubscribe event handlers - event_manager.unsubscribe(click_id); - event_manager.unsubscribe(press_id); - event_manager.unsubscribe(release_id); - event_manager.unsubscribe(move_id); - } + // Unsubscribe event handlers + event_manager.unsubscribe(this->click_id); + event_manager.unsubscribe(this->press_id); + event_manager.unsubscribe(this->release_id); + event_manager.unsubscribe(this->move_id); +} diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index d42e742..b0e6c9c 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -70,11 +70,13 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) { EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE); return false; }; - EventManager::get_instance().subscribe(mouse_handler, EventManager::CHANNEL_ALL); + EventManager::get_instance().subscribe(mouse_handler, + EventManager::CHANNEL_ALL); MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, EventManager::CHANNEL_ALL); + EventManager::get_instance().trigger_event(click_event, + EventManager::CHANNEL_ALL); EXPECT_TRUE(triggered); } @@ -92,13 +94,13 @@ TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) { MouseClickEvent click_event{ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; - EventManager::get_instance().trigger_event(click_event, EventManager::CHANNEL_ALL); + EventManager::get_instance().trigger_event(click_event, + EventManager::CHANNEL_ALL); EXPECT_FALSE(triggered); EventManager::get_instance().trigger_event(click_event, test_channel); } - TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { EventManager & event_manager = EventManager::get_instance(); @@ -128,8 +130,6 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE}; event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); - - // Trigger event event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); @@ -144,7 +144,6 @@ TEST_F(EventManagerTest, EventManagerTest_callback_propagation) { event_manager.clear(); event_manager.subscribe(mouse_handler_false, EventManager::CHANNEL_ALL); event_manager.subscribe(mouse_handler_true, EventManager::CHANNEL_ALL); - // Trigger event again event_manager.trigger_event(click_event, EventManager::CHANNEL_ALL); @@ -186,7 +185,6 @@ TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) { EXPECT_TRUE(triggered2); } - TEST_F(EventManagerTest, EventManagerTest_unsubscribe) { EventManager & event_manager = EventManager::get_instance(); -- cgit v1.2.3 From 8e72968e294cbc4ac6e9ff09bd94cde1775d735b Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 20 Nov 2024 21:51:30 +0100 Subject: nitpick #34 --- mwe/events/include/event.h | 2 +- src/crepe/api/EventManager.cpp | 54 +++++++++++++++++----------------------- src/crepe/api/EventManager.h | 39 +++++++++++++++++++++-------- src/crepe/api/EventManager.hpp | 36 +++++++++------------------ src/crepe/api/IKeyListener.cpp | 3 ++- src/crepe/api/IKeyListener.h | 4 +-- src/crepe/api/IMouseListener.cpp | 3 ++- src/crepe/api/IMouseListener.h | 2 +- src/crepe/facade/SDLContext.cpp | 3 ++- src/example/CMakeLists.txt | 6 ----- src/test/DBTest.cpp | 3 +-- src/test/ValueBrokerTest.cpp | 5 ++-- 12 files changed, 77 insertions(+), 83 deletions(-) (limited to 'src/test') diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h index e1b220b..ee1bf52 100644 --- a/mwe/events/include/event.h +++ b/mwe/events/include/event.h @@ -148,7 +148,7 @@ private: }; class ShutDownEvent : public Event { public: - ShutDownEvent() : Event("ShutDownEvent"){}; + ShutDownEvent() : Event("ShutDownEvent") {}; REGISTER_EVENT_TYPE(ShutDownEvent) diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index 993db86..20f0dd3 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -1,6 +1,7 @@ #include "EventManager.h" using namespace crepe; +using namespace std; EventManager & EventManager::get_instance() { static EventManager instance; @@ -8,30 +9,23 @@ EventManager & EventManager::get_instance() { } void EventManager::dispatch_events() { - for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) { - std::unique_ptr & event = (*event_it).event; - int channel = (*event_it).channel; - std::type_index event_type = (*event_it).type; - - bool event_handled = false; - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it == this->subscribers.end()) { - continue; - } - std::vector & handlers = handlers_it->second; - - for (auto handler_it = handlers.begin(); handler_it != handlers.end(); ++handler_it) { - // If callback is executed and returns true, remove the event from the queue - if ((*handler_it).callback->exec(*event)) { - event_it = this->events_queue.erase(event_it); - event_handled = true; - break; - } - } + for (auto & event : this->events_queue) { + this->handle_event(event.type, event.channel, *event.event.get()); + } + this->events_queue.clear(); +} - if (!event_handled) { - ++event_it; - } +void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) { + auto handlers_it = this->subscribers.find(type); + if (handlers_it == this->subscribers.end()) return; + + vector & handlers = handlers_it->second; + for (auto & handler : handlers) { + bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL; + if (check_channel && handler.channel != channel) continue; + + bool handled = handler.callback->exec(data); + if (handled) return; } } @@ -40,15 +34,13 @@ void EventManager::clear() { this->events_queue.clear(); } -void EventManager::unsubscribe(subscription_t event_id) { +void EventManager::unsubscribe(subscription_t id) { for (auto & [event_type, handlers] : this->subscribers) { - for (auto it = handlers.begin(); it != handlers.end();) { - if (it->id == event_id) { - it = handlers.erase(it); - return; - } else { - ++it; - } + for (auto it = handlers.begin(); it != handlers.end(); it++) { + // find listener with subscription id + if ((*it).id != id) continue; + it = handlers.erase(it); + return; } } } diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h index bd9772a..348a04d 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/api/EventManager.h @@ -1,8 +1,6 @@ #pragma once -#include #include -#include #include #include #include @@ -11,8 +9,17 @@ #include "EventHandler.h" namespace crepe { -//! typedef for subscription value -typedef int subscription_t; + +//! Event listener unique ID +typedef size_t subscription_t; + +/** + * \brief Event channel + * + * Events can be sent to a specific channel, which prevents listeners on other channels from + * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners. + */ +typedef size_t event_channel_t; /** * \class EventManager @@ -24,7 +31,7 @@ typedef int subscription_t; */ class EventManager { public: - static constexpr int CHANNEL_ALL = -1; + static constexpr const event_channel_t CHANNEL_ALL = -1; /** * \brief Get the singleton instance of the EventManager. @@ -49,7 +56,7 @@ public: */ template subscription_t subscribe(const EventHandler & callback, - int channel = CHANNEL_ALL); + event_channel_t channel = CHANNEL_ALL); /** * \brief Unsubscribe a previously registered callback. @@ -70,7 +77,7 @@ public: * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels). */ template - void trigger_event(const EventType & event, int channel = CHANNEL_ALL); + void trigger_event(const EventType & event, event_channel_t channel = CHANNEL_ALL); /** * \brief Queue an event for later processing. @@ -82,7 +89,7 @@ public: * \param channel The channel to associate with the event (default is CHANNEL_ALL). */ template - void queue_event(const EventType & event, int channel = CHANNEL_ALL); + void queue_event(const EventType & event, event_channel_t channel = CHANNEL_ALL); /** * \brief Process all queued events. @@ -113,17 +120,29 @@ private: */ struct QueueEntry { std::unique_ptr event; ///< The event instance. - int channel = CHANNEL_ALL; ///< The channel associated with the event. + event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event. std::type_index type; ///< The type of the event. }; + /** + * \brief Internal event handler + * + * This function processes a single event, and is used to process events both during + * EventManager::dispatch_events and inside EventManager::trigger_event + * + * \param type \c typeid of concrete Event class + * \param channel Event channel + * \param data Event data + */ + void handle_event(std::type_index type, event_channel_t channel, const Event & data); + /** * \struct CallbackEntry * \brief Represents a registered event handler callback. */ struct CallbackEntry { std::unique_ptr callback; ///< The callback function wrapper. - int channel = CHANNEL_ALL; ///< The channel this callback listens to. + event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to. subscription_t id = -1; ///< Unique subscription ID. }; diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index b2a94bd..a5f4556 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -1,9 +1,12 @@ +#pragma once + #include "EventManager.h" namespace crepe { template -subscription_t EventManager::subscribe(const EventHandler & callback, int channel) { +subscription_t EventManager::subscribe(const EventHandler & callback, + event_channel_t channel) { subscription_counter++; std::type_index event_type = typeid(EventType); std::unique_ptr> handler @@ -15,34 +18,19 @@ subscription_t EventManager::subscribe(const EventHandler & callback, } template -void EventManager::queue_event(const EventType & event, int channel) { +void EventManager::queue_event(const EventType & event, event_channel_t channel) { static_assert(std::is_base_of::value, "EventType must derive from Event"); - std::type_index event_type = typeid(EventType); - - auto event_ptr = std::make_unique(event); - - this->events_queue.push_back( - QueueEntry{.event = std::move(event_ptr), .channel = channel, .type = event_type}); + this->events_queue.push_back(QueueEntry{ + .event = std::make_unique(event), + .channel = channel, + .type = typeid(EventType), + }); } template -void EventManager::trigger_event(const EventType & event, int channel) { - std::type_index event_type = typeid(EventType); - - auto handlers_it = this->subscribers.find(event_type); - if (handlers_it != this->subscribers.end()) { - const std::vector & handlers = handlers_it->second; - - for (const CallbackEntry & handler : handlers) { - if (handler.channel != channel && handler.channel != CHANNEL_ALL) { - continue; - } - if (handler.callback->exec(event)) { - break; - } - } - } +void EventManager::trigger_event(const EventType & event, event_channel_t channel) { + this->handle_event(typeid(EventType), channel, event); } } // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index 7aefaf7..8642655 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -3,7 +3,8 @@ using namespace crepe; // Constructor with specified channel -IKeyListener::IKeyListener(int channel) : event_manager(EventManager::get_instance()) { +IKeyListener::IKeyListener(event_channel_t channel) + : event_manager(EventManager::get_instance()) { this->press_id = event_manager.subscribe( [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); this->release_id = event_manager.subscribe( diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 2a89cbc..328a4c2 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -16,7 +16,7 @@ public: * \brief Constructs an IKeyListener with a specified channel. * \param channel The channel ID for event handling. */ - IKeyListener(int channel = EventManager::CHANNEL_ALL); + IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL); virtual ~IKeyListener(); IKeyListener(const IKeyListener &) = delete; IKeyListener & operator=(const IKeyListener &) = delete; @@ -43,7 +43,7 @@ private: //! Key release event id subscription_t release_id = -1; //! EventManager reference - EventManager & event_manager;; + EventManager & event_manager; }; } // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index 7d38280..989aeb3 100644 --- a/src/crepe/api/IMouseListener.cpp +++ b/src/crepe/api/IMouseListener.cpp @@ -2,7 +2,8 @@ using namespace crepe; -IMouseListener::IMouseListener(int channel) : event_manager(EventManager::get_instance()) { +IMouseListener::IMouseListener(event_channel_t channel) + : event_manager(EventManager::get_instance()) { this->click_id = event_manager.subscribe( [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, channel); diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h index 91b33e1..15e1619 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -16,7 +16,7 @@ public: * \brief Constructs an IMouseListener with a specified channel. * \param channel The channel ID for event handling. */ - IMouseListener(int channel = EventManager::CHANNEL_ALL); + IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); virtual ~IMouseListener(); IMouseListener & operator=(const IMouseListener &) = delete; IMouseListener(const IMouseListener &) = delete; diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index b8b2bda..00523a6 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -47,7 +47,8 @@ SDLContext::SDLContext() { SDL_Renderer * tmp_renderer = SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED); if (!tmp_renderer) { - throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())); + throw runtime_error( + format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())); } this->game_renderer diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 7b4cc43..560e2bc 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -18,12 +18,6 @@ endfunction() add_example(asset_manager) add_example(savemgr) -add_example(proxy) -add_example(db) -add_example(ecs) -add_example(scene_manager) -add_example(events) -add_example(particles) add_example(rendering_particle) add_example(gameloop) diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp index b57eba9..e80814c 100644 --- a/src/test/DBTest.cpp +++ b/src/test/DBTest.cpp @@ -1,5 +1,5 @@ -#include #include +#include using namespace std; using namespace crepe; @@ -26,4 +26,3 @@ TEST_F(DBTest, Has) { db.set("foo", "bar"); EXPECT_EQ(db.has("foo"), true); } - diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp index 10a4654..e6bb058 100644 --- a/src/test/ValueBrokerTest.cpp +++ b/src/test/ValueBrokerTest.cpp @@ -13,7 +13,7 @@ public: int write_count = 0; int value = 0; - ValueBroker broker { + ValueBroker broker{ [this](const int & target) -> void { this->write_count++; this->value = target; @@ -49,7 +49,7 @@ TEST_F(ValueBrokerTest, ProxyWrite) { EXPECT_EQ(write_count, 1); } -void dummy(int) { } +void dummy(int) {} TEST_F(ValueBrokerTest, ProxyRead) { dummy(proxy); EXPECT_EQ(read_count, 1); @@ -61,4 +61,3 @@ TEST_F(ValueBrokerTest, ProxyReadWrite) { ASSERT_EQ(read_count, 1); ASSERT_EQ(write_count, 1); } - -- cgit v1.2.3