Skip to content

Commit

Permalink
✨ (BehaviorKit): Add structure to play behaviors
Browse files Browse the repository at this point in the history
interface::Behavior and interface::BehaviorKit
BehaviorID
Register behaviors
Use eventloop to start and stop behavior
Unit tests + mock
  • Loading branch information
YannLocatelli committed Dec 19, 2023
1 parent b872fd7 commit 34400c8
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 4 deletions.
25 changes: 25 additions & 0 deletions include/interface/libs/BehaviorKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Leka - LekaOS
// Copyright 2023 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <span>

#include "interface/Behavior.h"
#include "internal/BehaviorID.h"

namespace leka::interface {

class BehaviorKit
{
public:
virtual ~BehaviorKit() = default;

virtual void registerBehaviors(std::span<interface::Behavior *> behaviors) = 0;
virtual void start(interface::Behavior *behavior) = 0;
virtual void start(BehaviorID id) = 0;
virtual void stop() = 0;
};

} // namespace leka::interface
2 changes: 2 additions & 0 deletions libs/BehaviorKit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
23 changes: 21 additions & 2 deletions libs/BehaviorKit/include/BehaviorKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,33 @@

#pragma once

#include <span>

#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<interface::Behavior *> 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<interface::Behavior *> _behaviors {};
interface::Behavior *_behavior = nullptr;
};

} // namespace leka
23 changes: 23 additions & 0 deletions libs/BehaviorKit/include/interface/Behavior.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Leka - LekaOS
// Copyright 2023 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstddef>
#include <cstdint>

#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
15 changes: 15 additions & 0 deletions libs/BehaviorKit/include/internal/BehaviorID.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Leka - LekaOS
// Copyright 2023 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstdint>

using BehaviorID = uint16_t;

namespace leka::behavior_id {

inline constexpr BehaviorID none = 0x0000;

} // namespace leka::behavior_id
55 changes: 55 additions & 0 deletions libs/BehaviorKit/source/BehaviorKit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,60 @@
// SPDX-License-Identifier: Apache-2.0

#include "BehaviorKit.h"
#include <algorithm>

using namespace leka;

BehaviorKit::BehaviorKit(interface::EventLoop &event_loop) : _event_loop(event_loop)
{
_event_loop.registerCallback([this] { run(); });
}

void BehaviorKit::registerBehaviors(std::span<interface::Behavior *> 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();
}
}
29 changes: 29 additions & 0 deletions libs/BehaviorKit/tests/BehaviorID_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Leka - LekaOS
// Copyright 2023 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include <array>

#include "gtest/gtest.h"
#include "interface/Behavior.h"

using namespace leka;

TEST(BehaviorIDTest, behaviorsHaveUniqueID)
{
// auto all_behaviors = std::to_array<interface::Behavior *>({});
std::array<interface::Behavior *, 0> 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());
}
}
94 changes: 92 additions & 2 deletions libs/BehaviorKit/tests/BehaviorKit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<interface::Behavior *>({&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<interface::Behavior *>({
&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<interface::Behavior *>({
&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<interface::Behavior *>({
&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<interface::Behavior *>({
&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);
}
21 changes: 21 additions & 0 deletions tests/unit/mocks/mocks/leka/Behavior.h
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 34400c8

Please sign in to comment.