Skip to content

Commit 34400c8

Browse files
committed
✨ (BehaviorKit): Add structure to play behaviors
interface::Behavior and interface::BehaviorKit BehaviorID Register behaviors Use eventloop to start and stop behavior Unit tests + mock
1 parent b872fd7 commit 34400c8

File tree

9 files changed

+283
-4
lines changed

9 files changed

+283
-4
lines changed

include/interface/libs/BehaviorKit.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Leka - LekaOS
2+
// Copyright 2023 APF France handicap
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include <span>
8+
9+
#include "interface/Behavior.h"
10+
#include "internal/BehaviorID.h"
11+
12+
namespace leka::interface {
13+
14+
class BehaviorKit
15+
{
16+
public:
17+
virtual ~BehaviorKit() = default;
18+
19+
virtual void registerBehaviors(std::span<interface::Behavior *> behaviors) = 0;
20+
virtual void start(interface::Behavior *behavior) = 0;
21+
virtual void start(BehaviorID id) = 0;
22+
virtual void stop() = 0;
23+
};
24+
25+
} // namespace leka::interface

libs/BehaviorKit/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ target_sources(BehaviorKit
1515
)
1616

1717
target_link_libraries(BehaviorKit
18+
EventLoopKit
1819
)
1920

2021
if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests")
2122
leka_unit_tests_sources(
23+
tests/BehaviorID_test.cpp
2224
tests/BehaviorKit_test.cpp
2325
)
2426
endif()

libs/BehaviorKit/include/BehaviorKit.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,33 @@
44

55
#pragma once
66

7+
#include <span>
8+
9+
#include "interface/Behavior.h"
10+
#include "interface/libs/BehaviorKit.h"
11+
#include "interface/libs/EventLoop.h"
12+
#include "internal/BehaviorID.h"
13+
714
namespace leka {
815

9-
class BehaviorKit
16+
class BehaviorKit : public interface::BehaviorKit
1017
{
1118
public:
12-
explicit BehaviorKit() = default;
19+
explicit BehaviorKit(interface::EventLoop &event_loop);
20+
21+
void registerBehaviors(std::span<interface::Behavior *> behaviors) final;
22+
23+
void start(interface::Behavior *behavior) final;
24+
void start(BehaviorID id) final;
25+
void stop() final;
1326

1427
private:
28+
void run();
29+
30+
interface::EventLoop &_event_loop;
31+
32+
std::span<interface::Behavior *> _behaviors {};
33+
interface::Behavior *_behavior = nullptr;
1534
};
1635

1736
} // namespace leka
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Leka - LekaOS
2+
// Copyright 2023 APF France handicap
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include <cstddef>
8+
#include <cstdint>
9+
10+
#include "../internal/BehaviorID.h"
11+
12+
namespace leka::interface {
13+
14+
struct Behavior {
15+
virtual ~Behavior() = default;
16+
17+
virtual auto id() -> BehaviorID = 0;
18+
19+
virtual void run() = 0;
20+
virtual void stop() = 0;
21+
};
22+
23+
} // namespace leka::interface
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Leka - LekaOS
2+
// Copyright 2023 APF France handicap
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include <cstdint>
8+
9+
using BehaviorID = uint16_t;
10+
11+
namespace leka::behavior_id {
12+
13+
inline constexpr BehaviorID none = 0x0000;
14+
15+
} // namespace leka::behavior_id

libs/BehaviorKit/source/BehaviorKit.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,60 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
#include "BehaviorKit.h"
6+
#include <algorithm>
67

78
using namespace leka;
9+
10+
BehaviorKit::BehaviorKit(interface::EventLoop &event_loop) : _event_loop(event_loop)
11+
{
12+
_event_loop.registerCallback([this] { run(); });
13+
}
14+
15+
void BehaviorKit::registerBehaviors(std::span<interface::Behavior *> behaviors)
16+
{
17+
_behaviors = behaviors;
18+
}
19+
20+
void BehaviorKit::start(interface::Behavior *behavior)
21+
{
22+
stop();
23+
24+
_behavior = nullptr;
25+
for (auto *b: _behaviors) {
26+
if (b == behavior) {
27+
_behavior = b;
28+
}
29+
}
30+
31+
if (_behavior == nullptr) {
32+
return;
33+
}
34+
35+
_event_loop.start();
36+
}
37+
38+
void BehaviorKit::start(BehaviorID id)
39+
{
40+
interface::Behavior *found_behavior = nullptr;
41+
42+
for (auto *behavior: _behaviors) {
43+
if (id == behavior->id()) {
44+
found_behavior = behavior;
45+
break;
46+
}
47+
}
48+
49+
start(found_behavior);
50+
}
51+
52+
void BehaviorKit::run()
53+
{
54+
_behavior->run();
55+
}
56+
57+
void BehaviorKit::stop()
58+
{
59+
if (_behavior != nullptr) {
60+
_behavior->stop();
61+
}
62+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Leka - LekaOS
2+
// Copyright 2023 APF France handicap
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <array>
6+
7+
#include "gtest/gtest.h"
8+
#include "interface/Behavior.h"
9+
10+
using namespace leka;
11+
12+
TEST(BehaviorIDTest, behaviorsHaveUniqueID)
13+
{
14+
// auto all_behaviors = std::to_array<interface::Behavior *>({});
15+
std::array<interface::Behavior *, 0> all_behaviors =
16+
{}; // TODO: to remove, only for empty list, use above instead
17+
18+
auto comparator = [](interface::Behavior *a, interface::Behavior *b) { return a->id() < b->id(); };
19+
std::sort(all_behaviors.begin(), all_behaviors.end(), comparator);
20+
21+
auto array_size = std::size(all_behaviors);
22+
if (array_size <= 1) {
23+
return;
24+
}
25+
26+
for (auto i = 1; i < array_size; i++) {
27+
ASSERT_NE(all_behaviors.at(i - 1)->id(), all_behaviors.at(i)->id());
28+
}
29+
}

libs/BehaviorKit/tests/BehaviorKit_test.cpp

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,111 @@
66

77
#include "gmock/gmock.h"
88
#include "gtest/gtest.h"
9+
#include "mocks/leka/Behavior.h"
10+
#include "stubs/leka/EventLoopKit.h"
911

1012
using namespace leka;
1113

14+
using ::testing::Return;
15+
1216
class BehaviorKitTest : public ::testing::Test
1317
{
1418
protected:
15-
BehaviorKitTest() : behaviorkit() {};
19+
BehaviorKitTest() = default;
1620

1721
// void SetUp() override {}
1822
// void TearDown() override {}
1923

20-
BehaviorKit behaviorkit;
24+
stub::EventLoopKit stub_event_loop {};
25+
BehaviorKit behaviorkit {stub_event_loop};
26+
27+
mock::Behavior mock_behavior_a {};
28+
mock::Behavior mock_behavior_b {};
2129
};
2230

2331
TEST_F(BehaviorKitTest, initialization)
2432
{
2533
ASSERT_NE(&behaviorkit, nullptr);
2634
}
35+
36+
TEST_F(BehaviorKitTest, startFirstBehavior)
37+
{
38+
auto behaviors = std::to_array<interface::Behavior *>({&mock_behavior_a});
39+
behaviorkit.registerBehaviors(behaviors);
40+
41+
EXPECT_CALL(mock_behavior_a, run);
42+
43+
behaviorkit.start(&mock_behavior_a);
44+
}
45+
46+
TEST_F(BehaviorKitTest, startBehaviorNullPtr)
47+
{
48+
// nothing expected
49+
50+
behaviorkit.start(nullptr);
51+
}
52+
53+
TEST_F(BehaviorKitTest, startFirstBehaviorID)
54+
{
55+
auto behaviors = std::to_array<interface::Behavior *>({
56+
&mock_behavior_a,
57+
&mock_behavior_b,
58+
});
59+
behaviorkit.registerBehaviors(behaviors);
60+
61+
auto behavior_a_id = BehaviorID {1};
62+
auto behavior_b_id = BehaviorID {2};
63+
64+
EXPECT_CALL(mock_behavior_a, id).WillRepeatedly(Return(behavior_a_id));
65+
EXPECT_CALL(mock_behavior_b, id).WillRepeatedly(Return(behavior_b_id));
66+
EXPECT_CALL(mock_behavior_b, run);
67+
68+
behaviorkit.start(behavior_b_id);
69+
}
70+
71+
TEST_F(BehaviorKitTest, startBehaviorIDNotRegistered)
72+
{
73+
auto behaviors = std::to_array<interface::Behavior *>({
74+
&mock_behavior_a,
75+
&mock_behavior_b,
76+
});
77+
behaviorkit.registerBehaviors(behaviors);
78+
79+
auto behavior_a_id = BehaviorID {1};
80+
auto behavior_b_id = BehaviorID {2};
81+
82+
EXPECT_CALL(mock_behavior_a, id).WillRepeatedly(Return(behavior_a_id));
83+
EXPECT_CALL(mock_behavior_b, id).WillRepeatedly(Return(behavior_b_id));
84+
85+
behaviorkit.start(BehaviorID {3});
86+
}
87+
88+
TEST_F(BehaviorKitTest, startBehaviorNotRegistered)
89+
{
90+
auto behaviors = std::to_array<interface::Behavior *>({
91+
&mock_behavior_a,
92+
});
93+
behaviorkit.registerBehaviors(behaviors);
94+
95+
EXPECT_CALL(mock_behavior_b, run).Times(0);
96+
97+
behaviorkit.start(&mock_behavior_b);
98+
}
99+
100+
TEST_F(BehaviorKitTest, startAnyBehavior)
101+
{
102+
auto behaviors = std::to_array<interface::Behavior *>({
103+
&mock_behavior_a,
104+
&mock_behavior_b,
105+
});
106+
behaviorkit.registerBehaviors(behaviors);
107+
108+
EXPECT_CALL(mock_behavior_a, run);
109+
110+
behaviorkit.start(&mock_behavior_a);
111+
112+
EXPECT_CALL(mock_behavior_a, stop);
113+
EXPECT_CALL(mock_behavior_b, run);
114+
115+
behaviorkit.start(&mock_behavior_b);
116+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Leka - LekaOS
2+
// Copyright 2023 APF France handicap
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include "gmock/gmock.h"
8+
#include "interface/Behavior.h"
9+
10+
namespace leka::mock {
11+
12+
class Behavior : public interface::Behavior
13+
{
14+
public:
15+
MOCK_METHOD(BehaviorID, id, (), (override));
16+
17+
MOCK_METHOD(void, run, (), (override));
18+
MOCK_METHOD(void, stop, (), (override));
19+
};
20+
21+
} // namespace leka::mock

0 commit comments

Comments
 (0)