Skip to content

Commit 56a6df4

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 56a6df4

File tree

9 files changed

+265
-4
lines changed

9 files changed

+265
-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: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,54 @@
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+
if (behavior == nullptr) {
25+
return;
26+
}
27+
_behavior = *std::find(std::begin(_behaviors), std::end(_behaviors), behavior); // TODO: can't be null
28+
29+
_event_loop.start();
30+
}
31+
32+
void BehaviorKit::start(BehaviorID id)
33+
{
34+
interface::Behavior *found_behavior = nullptr;
35+
36+
for (auto *behavior: _behaviors) {
37+
if (id == behavior->id()) {
38+
found_behavior = behavior;
39+
break;
40+
}
41+
}
42+
43+
start(found_behavior);
44+
}
45+
46+
void BehaviorKit::run()
47+
{
48+
_behavior->run();
49+
}
50+
51+
void BehaviorKit::stop()
52+
{
53+
if (_behavior != nullptr) {
54+
_behavior->stop();
55+
}
56+
}
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: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,99 @@
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, startAnyBehavior)
89+
{
90+
auto behaviors = std::to_array<interface::Behavior *>({
91+
&mock_behavior_a,
92+
&mock_behavior_b,
93+
});
94+
behaviorkit.registerBehaviors(behaviors);
95+
96+
EXPECT_CALL(mock_behavior_a, run);
97+
98+
behaviorkit.start(&mock_behavior_a);
99+
100+
EXPECT_CALL(mock_behavior_a, stop);
101+
EXPECT_CALL(mock_behavior_b, run);
102+
103+
behaviorkit.start(&mock_behavior_b);
104+
}
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)