From bc8e069cd942bfb5d9ce34729f2d5d03e08ba02a Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 13 Dec 2023 17:10:12 +0100 Subject: [PATCH 1/3] :sparkles: (BehaviorKit): Create new BehaviorKit --- libs/BehaviorKit/CMakeLists.txt | 24 +++++++++++++++++++ libs/BehaviorKit/include/BehaviorKit.h | 17 ++++++++++++++ libs/BehaviorKit/source/BehaviorKit.cpp | 7 ++++++ libs/BehaviorKit/tests/BehaviorKit_test.cpp | 26 +++++++++++++++++++++ libs/CMakeLists.txt | 1 + spikes/lk_behavior_kit/CMakeLists.txt | 1 + tests/unit/CMakeLists.txt | 1 + 7 files changed, 77 insertions(+) create mode 100644 libs/BehaviorKit/CMakeLists.txt create mode 100644 libs/BehaviorKit/include/BehaviorKit.h create mode 100644 libs/BehaviorKit/source/BehaviorKit.cpp create mode 100644 libs/BehaviorKit/tests/BehaviorKit_test.cpp diff --git a/libs/BehaviorKit/CMakeLists.txt b/libs/BehaviorKit/CMakeLists.txt new file mode 100644 index 0000000000..73c3901a1b --- /dev/null +++ b/libs/BehaviorKit/CMakeLists.txt @@ -0,0 +1,24 @@ +# Leka - LekaOS +# Copyright 2023 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(BehaviorKit STATIC) + +target_include_directories(BehaviorKit + PUBLIC + include +) + +target_sources(BehaviorKit + PRIVATE + source/BehaviorKit.cpp +) + +target_link_libraries(BehaviorKit +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/BehaviorKit_test.cpp + ) +endif() diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h new file mode 100644 index 0000000000..76f563e4f1 --- /dev/null +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -0,0 +1,17 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace leka { + +class BehaviorKit +{ + public: + explicit BehaviorKit() = default; + + private: +}; + +} // namespace leka diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp new file mode 100644 index 0000000000..80ba6d3d1f --- /dev/null +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -0,0 +1,7 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "BehaviorKit.h" + +using namespace leka; diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp new file mode 100644 index 0000000000..84e497d153 --- /dev/null +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -0,0 +1,26 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "BehaviorKit.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace leka; + +class BehaviorKitTest : public ::testing::Test +{ + protected: + BehaviorKitTest() : behaviorkit() {}; + + // void SetUp() override {} + // void TearDown() override {} + + BehaviorKit behaviorkit; +}; + +TEST_F(BehaviorKitTest, initialization) +{ + ASSERT_NE(&behaviorkit, nullptr); +} diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index c70dc14560..734c63fa87 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(${LIBS_DIR}/ActivityKit) add_subdirectory(${LIBS_DIR}/BatteryKit) +add_subdirectory(${LIBS_DIR}/BehaviorKit) add_subdirectory(${LIBS_DIR}/BehaviorKitDeprecated) add_subdirectory(${LIBS_DIR}/BLEKit) add_subdirectory(${LIBS_DIR}/ColorKit) diff --git a/spikes/lk_behavior_kit/CMakeLists.txt b/spikes/lk_behavior_kit/CMakeLists.txt index b5e0ae0976..ab3c1e8b55 100644 --- a/spikes/lk_behavior_kit/CMakeLists.txt +++ b/spikes/lk_behavior_kit/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources(spike_lk_behavior_kit ) target_link_libraries(spike_lk_behavior_kit + BehaviorKit BehaviorKitDeprecated CorePwm EventLoopKit diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index d5af3a081d..674dce1eeb 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -281,6 +281,7 @@ leka_register_unit_tests_for_driver(CoreVideo) # Register libraries leka_register_unit_tests_for_library(ActivityKit) leka_register_unit_tests_for_library(BatteryKit) +leka_register_unit_tests_for_library(BehaviorKit) leka_register_unit_tests_for_library(BehaviorKitDeprecated) leka_register_unit_tests_for_library(BLEKit) leka_register_unit_tests_for_library(ColorKit) From 4d443b28b28473a01aed8c822733c268cfbde12b Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 13 Dec 2023 17:28:59 +0100 Subject: [PATCH 2/3] :sparkles: (BehaviorKit): Add structure to play behaviors interface::Behavior and interface::BehaviorKit BehaviorID Register behaviors Use eventloop to start and stop behavior Unit tests + mock --- include/interface/libs/BehaviorKit.h | 25 +++++ libs/BehaviorKit/CMakeLists.txt | 2 + libs/BehaviorKit/include/BehaviorKit.h | 23 ++++- libs/BehaviorKit/include/interface/Behavior.h | 23 +++++ .../BehaviorKit/include/internal/BehaviorID.h | 15 +++ libs/BehaviorKit/source/BehaviorKit.cpp | 55 +++++++++++ libs/BehaviorKit/tests/BehaviorID_test.cpp | 29 ++++++ libs/BehaviorKit/tests/BehaviorKit_test.cpp | 94 ++++++++++++++++++- tests/unit/mocks/mocks/leka/Behavior.h | 21 +++++ 9 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 include/interface/libs/BehaviorKit.h create mode 100644 libs/BehaviorKit/include/interface/Behavior.h create mode 100644 libs/BehaviorKit/include/internal/BehaviorID.h create mode 100644 libs/BehaviorKit/tests/BehaviorID_test.cpp create mode 100644 tests/unit/mocks/mocks/leka/Behavior.h diff --git a/include/interface/libs/BehaviorKit.h b/include/interface/libs/BehaviorKit.h new file mode 100644 index 0000000000..50755131b4 --- /dev/null +++ b/include/interface/libs/BehaviorKit.h @@ -0,0 +1,25 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "interface/Behavior.h" +#include "internal/BehaviorID.h" + +namespace leka::interface { + +class BehaviorKit +{ + public: + virtual ~BehaviorKit() = default; + + virtual void registerBehaviors(std::span behaviors) = 0; + virtual void start(interface::Behavior *behavior) = 0; + virtual void start(BehaviorID id) = 0; + virtual void stop() = 0; +}; + +} // namespace leka::interface diff --git a/libs/BehaviorKit/CMakeLists.txt b/libs/BehaviorKit/CMakeLists.txt index 73c3901a1b..95d922c947 100644 --- a/libs/BehaviorKit/CMakeLists.txt +++ b/libs/BehaviorKit/CMakeLists.txt @@ -15,10 +15,12 @@ target_sources(BehaviorKit ) target_link_libraries(BehaviorKit + EventLoopKit ) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( + tests/BehaviorID_test.cpp tests/BehaviorKit_test.cpp ) endif() diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h index 76f563e4f1..30be72fe96 100644 --- a/libs/BehaviorKit/include/BehaviorKit.h +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -4,14 +4,33 @@ #pragma once +#include + +#include "interface/Behavior.h" +#include "interface/libs/BehaviorKit.h" +#include "interface/libs/EventLoop.h" +#include "internal/BehaviorID.h" + namespace leka { -class BehaviorKit +class BehaviorKit : public interface::BehaviorKit { public: - explicit BehaviorKit() = default; + explicit BehaviorKit(interface::EventLoop &event_loop); + + void registerBehaviors(std::span behaviors) final; + + void start(interface::Behavior *behavior) final; + void start(BehaviorID id) final; + void stop() final; private: + void run(); + + interface::EventLoop &_event_loop; + + std::span _behaviors {}; + interface::Behavior *_behavior = nullptr; }; } // namespace leka diff --git a/libs/BehaviorKit/include/interface/Behavior.h b/libs/BehaviorKit/include/interface/Behavior.h new file mode 100644 index 0000000000..5d4e668a36 --- /dev/null +++ b/libs/BehaviorKit/include/interface/Behavior.h @@ -0,0 +1,23 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "../internal/BehaviorID.h" + +namespace leka::interface { + +struct Behavior { + virtual ~Behavior() = default; + + virtual auto id() -> BehaviorID = 0; + + virtual void run() = 0; + virtual void stop() = 0; +}; + +} // namespace leka::interface diff --git a/libs/BehaviorKit/include/internal/BehaviorID.h b/libs/BehaviorKit/include/internal/BehaviorID.h new file mode 100644 index 0000000000..b54b9a4d01 --- /dev/null +++ b/libs/BehaviorKit/include/internal/BehaviorID.h @@ -0,0 +1,15 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +using BehaviorID = uint16_t; + +namespace leka::behavior_id { + +inline constexpr BehaviorID none = 0x0000; + +} // namespace leka::behavior_id diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp index 80ba6d3d1f..926159142f 100644 --- a/libs/BehaviorKit/source/BehaviorKit.cpp +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -3,5 +3,60 @@ // SPDX-License-Identifier: Apache-2.0 #include "BehaviorKit.h" +#include using namespace leka; + +BehaviorKit::BehaviorKit(interface::EventLoop &event_loop) : _event_loop(event_loop) +{ + _event_loop.registerCallback([this] { run(); }); +} + +void BehaviorKit::registerBehaviors(std::span behaviors) +{ + _behaviors = behaviors; +} + +void BehaviorKit::start(interface::Behavior *behavior) +{ + stop(); + + _behavior = nullptr; + for (auto *b: _behaviors) { + if (b == behavior) { + _behavior = b; + } + } + + if (_behavior == nullptr) { + return; + } + + _event_loop.start(); +} + +void BehaviorKit::start(BehaviorID id) +{ + interface::Behavior *found_behavior = nullptr; + + for (auto *behavior: _behaviors) { + if (id == behavior->id()) { + found_behavior = behavior; + break; + } + } + + start(found_behavior); +} + +void BehaviorKit::run() +{ + _behavior->run(); +} + +void BehaviorKit::stop() +{ + if (_behavior != nullptr) { + _behavior->stop(); + } +} diff --git a/libs/BehaviorKit/tests/BehaviorID_test.cpp b/libs/BehaviorKit/tests/BehaviorID_test.cpp new file mode 100644 index 0000000000..e3d8392bc5 --- /dev/null +++ b/libs/BehaviorKit/tests/BehaviorID_test.cpp @@ -0,0 +1,29 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "gtest/gtest.h" +#include "interface/Behavior.h" + +using namespace leka; + +TEST(BehaviorIDTest, behaviorsHaveUniqueID) +{ + // auto all_behaviors = std::to_array({}); + std::array all_behaviors = + {}; // TODO: to remove, only for empty list, use above instead + + auto comparator = [](interface::Behavior *a, interface::Behavior *b) { return a->id() < b->id(); }; + std::sort(all_behaviors.begin(), all_behaviors.end(), comparator); + + auto array_size = std::size(all_behaviors); + if (array_size <= 1) { + return; + } + + for (auto i = 1; i < array_size; i++) { + ASSERT_NE(all_behaviors.at(i - 1)->id(), all_behaviors.at(i)->id()); + } +} diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp index 84e497d153..70b0d402be 100644 --- a/libs/BehaviorKit/tests/BehaviorKit_test.cpp +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -6,21 +6,111 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "mocks/leka/Behavior.h" +#include "stubs/leka/EventLoopKit.h" using namespace leka; +using ::testing::Return; + class BehaviorKitTest : public ::testing::Test { protected: - BehaviorKitTest() : behaviorkit() {}; + BehaviorKitTest() = default; // void SetUp() override {} // void TearDown() override {} - BehaviorKit behaviorkit; + stub::EventLoopKit stub_event_loop {}; + BehaviorKit behaviorkit {stub_event_loop}; + + mock::Behavior mock_behavior_a {}; + mock::Behavior mock_behavior_b {}; }; TEST_F(BehaviorKitTest, initialization) { ASSERT_NE(&behaviorkit, nullptr); } + +TEST_F(BehaviorKitTest, startFirstBehavior) +{ + auto behaviors = std::to_array({&mock_behavior_a}); + behaviorkit.registerBehaviors(behaviors); + + EXPECT_CALL(mock_behavior_a, run); + + behaviorkit.start(&mock_behavior_a); +} + +TEST_F(BehaviorKitTest, startBehaviorNullPtr) +{ + // nothing expected + + behaviorkit.start(nullptr); +} + +TEST_F(BehaviorKitTest, startFirstBehaviorID) +{ + auto behaviors = std::to_array({ + &mock_behavior_a, + &mock_behavior_b, + }); + behaviorkit.registerBehaviors(behaviors); + + auto behavior_a_id = BehaviorID {1}; + auto behavior_b_id = BehaviorID {2}; + + EXPECT_CALL(mock_behavior_a, id).WillRepeatedly(Return(behavior_a_id)); + EXPECT_CALL(mock_behavior_b, id).WillRepeatedly(Return(behavior_b_id)); + EXPECT_CALL(mock_behavior_b, run); + + behaviorkit.start(behavior_b_id); +} + +TEST_F(BehaviorKitTest, startBehaviorIDNotRegistered) +{ + auto behaviors = std::to_array({ + &mock_behavior_a, + &mock_behavior_b, + }); + behaviorkit.registerBehaviors(behaviors); + + auto behavior_a_id = BehaviorID {1}; + auto behavior_b_id = BehaviorID {2}; + + EXPECT_CALL(mock_behavior_a, id).WillRepeatedly(Return(behavior_a_id)); + EXPECT_CALL(mock_behavior_b, id).WillRepeatedly(Return(behavior_b_id)); + + behaviorkit.start(BehaviorID {3}); +} + +TEST_F(BehaviorKitTest, startBehaviorNotRegistered) +{ + auto behaviors = std::to_array({ + &mock_behavior_a, + }); + behaviorkit.registerBehaviors(behaviors); + + EXPECT_CALL(mock_behavior_b, run).Times(0); + + behaviorkit.start(&mock_behavior_b); +} + +TEST_F(BehaviorKitTest, startAnyBehavior) +{ + auto behaviors = std::to_array({ + &mock_behavior_a, + &mock_behavior_b, + }); + behaviorkit.registerBehaviors(behaviors); + + EXPECT_CALL(mock_behavior_a, run); + + behaviorkit.start(&mock_behavior_a); + + EXPECT_CALL(mock_behavior_a, stop); + EXPECT_CALL(mock_behavior_b, run); + + behaviorkit.start(&mock_behavior_b); +} diff --git a/tests/unit/mocks/mocks/leka/Behavior.h b/tests/unit/mocks/mocks/leka/Behavior.h new file mode 100644 index 0000000000..377b57c7d7 --- /dev/null +++ b/tests/unit/mocks/mocks/leka/Behavior.h @@ -0,0 +1,21 @@ +// Leka - LekaOS +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "gmock/gmock.h" +#include "interface/Behavior.h" + +namespace leka::mock { + +class Behavior : public interface::Behavior +{ + public: + MOCK_METHOD(BehaviorID, id, (), (override)); + + MOCK_METHOD(void, run, (), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + +} // namespace leka::mock From 283355b4653b2a146cddd00748472cb6d3a32a3b Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 20 Dec 2023 13:25:53 +0100 Subject: [PATCH 3/3] Apply suggestion from oral review Remove eventloop --- libs/BehaviorKit/include/BehaviorKit.h | 7 +------ libs/BehaviorKit/source/BehaviorKit.cpp | 12 +----------- libs/BehaviorKit/tests/BehaviorKit_test.cpp | 4 +--- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h index 30be72fe96..4de8db6677 100644 --- a/libs/BehaviorKit/include/BehaviorKit.h +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -8,7 +8,6 @@ #include "interface/Behavior.h" #include "interface/libs/BehaviorKit.h" -#include "interface/libs/EventLoop.h" #include "internal/BehaviorID.h" namespace leka { @@ -16,7 +15,7 @@ namespace leka { class BehaviorKit : public interface::BehaviorKit { public: - explicit BehaviorKit(interface::EventLoop &event_loop); + explicit BehaviorKit() = default; void registerBehaviors(std::span behaviors) final; @@ -25,10 +24,6 @@ class BehaviorKit : public interface::BehaviorKit void stop() final; private: - void run(); - - interface::EventLoop &_event_loop; - std::span _behaviors {}; interface::Behavior *_behavior = nullptr; }; diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp index 926159142f..7d3d263072 100644 --- a/libs/BehaviorKit/source/BehaviorKit.cpp +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -7,11 +7,6 @@ using namespace leka; -BehaviorKit::BehaviorKit(interface::EventLoop &event_loop) : _event_loop(event_loop) -{ - _event_loop.registerCallback([this] { run(); }); -} - void BehaviorKit::registerBehaviors(std::span behaviors) { _behaviors = behaviors; @@ -32,7 +27,7 @@ void BehaviorKit::start(interface::Behavior *behavior) return; } - _event_loop.start(); + _behavior->run(); } void BehaviorKit::start(BehaviorID id) @@ -49,11 +44,6 @@ void BehaviorKit::start(BehaviorID id) start(found_behavior); } -void BehaviorKit::run() -{ - _behavior->run(); -} - void BehaviorKit::stop() { if (_behavior != nullptr) { diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp index 70b0d402be..2c1d7bfe29 100644 --- a/libs/BehaviorKit/tests/BehaviorKit_test.cpp +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -7,7 +7,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "mocks/leka/Behavior.h" -#include "stubs/leka/EventLoopKit.h" using namespace leka; @@ -21,8 +20,7 @@ class BehaviorKitTest : public ::testing::Test // void SetUp() override {} // void TearDown() override {} - stub::EventLoopKit stub_event_loop {}; - BehaviorKit behaviorkit {stub_event_loop}; + BehaviorKit behaviorkit {}; mock::Behavior mock_behavior_a {}; mock::Behavior mock_behavior_b {};