1 #include "MCTPEndpoint.hpp"
2 #include "MCTPReactor.hpp"
3 #include "Utils.hpp"
4
5 #include <cstdint>
6 #include <functional>
7 #include <memory>
8 #include <string>
9 #include <system_error>
10 #include <vector>
11
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14
15 class MockMCTPDevice : public MCTPDevice
16 {
17 public:
18 ~MockMCTPDevice() override = default;
19
20 MOCK_METHOD(void, setup,
21 (std::function<void(const std::error_code& ec,
22 const std::shared_ptr<MCTPEndpoint>& ep)> &&
23 added),
24 (override));
25 MOCK_METHOD(void, remove, (), (override));
26 MOCK_METHOD(std::string, describe, (), (const, override));
27 };
28
29 class MockMCTPEndpoint : public MCTPEndpoint
30 {
31 public:
32 ~MockMCTPEndpoint() override = default;
33
34 MOCK_METHOD(int, network, (), (const, override));
35 MOCK_METHOD(uint8_t, eid, (), (const, override));
36 MOCK_METHOD(void, subscribe,
37 (Event && degraded, Event&& available, Event&& removed),
38 (override));
39 MOCK_METHOD(void, remove, (), (override));
40 MOCK_METHOD(std::string, describe, (), (const, override));
41 MOCK_METHOD(std::shared_ptr<MCTPDevice>, device, (), (const, override));
42 };
43
44 class MockAssociationServer : public AssociationServer
45 {
46 public:
47 ~MockAssociationServer() override = default;
48
49 MOCK_METHOD(void, associate,
50 (const std::string& path,
51 const std::vector<Association>& associations),
52 (override));
53 MOCK_METHOD(void, disassociate, (const std::string& path), (override));
54 };
55
56 class MCTPReactorFixture : public testing::Test
57 {
58 protected:
SetUp()59 void SetUp() override
60 {
61 reactor = std::make_shared<MCTPReactor>(assoc);
62 device = std::make_shared<MockMCTPDevice>();
63 EXPECT_CALL(*device, describe())
64 .WillRepeatedly(testing::Return("mock device"));
65
66 endpoint = std::make_shared<MockMCTPEndpoint>();
67 EXPECT_CALL(*endpoint, device())
68 .WillRepeatedly(testing::Return(device));
69 EXPECT_CALL(*endpoint, describe())
70 .WillRepeatedly(testing::Return("mock endpoint"));
71 EXPECT_CALL(*endpoint, eid()).WillRepeatedly(testing::Return(9));
72 EXPECT_CALL(*endpoint, network()).WillRepeatedly(testing::Return(1));
73 }
74
TearDown()75 void TearDown() override
76 {
77 // https://stackoverflow.com/a/10289205
78 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(endpoint.get()));
79 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(device.get()));
80 }
81
82 MockAssociationServer assoc;
83 std::shared_ptr<MCTPReactor> reactor;
84 std::shared_ptr<MockMCTPDevice> device;
85 std::shared_ptr<MockMCTPEndpoint> endpoint;
86 };
87
TEST_F(MCTPReactorFixture,manageNullDevice)88 TEST_F(MCTPReactorFixture, manageNullDevice)
89 {
90 reactor->manageMCTPDevice("/test", {});
91 reactor->unmanageMCTPDevice("/test");
92 }
93
TEST_F(MCTPReactorFixture,manageMockDeviceSetupFailure)94 TEST_F(MCTPReactorFixture, manageMockDeviceSetupFailure)
95 {
96 EXPECT_CALL(*device, remove());
97 EXPECT_CALL(*device, setup(testing::_))
98 .WillOnce(testing::InvokeArgument<0>(
99 std::make_error_code(std::errc::permission_denied), endpoint));
100
101 reactor->manageMCTPDevice("/test", device);
102 reactor->unmanageMCTPDevice("/test");
103 }
104
TEST_F(MCTPReactorFixture,manageMockDevice)105 TEST_F(MCTPReactorFixture, manageMockDevice)
106 {
107 std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler;
108
109 std::vector<Association> requiredAssociation{
110 {"configured_by", "configures", "/test"}};
111 EXPECT_CALL(assoc, associate("/xyz/openbmc_project/mctp/1/9",
112 requiredAssociation));
113 EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9"));
114
115 EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() {
116 removeHandler(endpoint);
117 }));
118 EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_))
119 .WillOnce(testing::SaveArg<2>(&removeHandler));
120
121 EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() {
122 endpoint->remove();
123 }));
124 EXPECT_CALL(*device, setup(testing::_))
125 .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint));
126
127 reactor->manageMCTPDevice("/test", device);
128 reactor->unmanageMCTPDevice("/test");
129 }
130
TEST_F(MCTPReactorFixture,manageMockDeviceDeferredSetup)131 TEST_F(MCTPReactorFixture, manageMockDeviceDeferredSetup)
132 {
133 std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler;
134
135 std::vector<Association> requiredAssociation{
136 {"configured_by", "configures", "/test"}};
137 EXPECT_CALL(assoc, associate("/xyz/openbmc_project/mctp/1/9",
138 requiredAssociation));
139 EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9"));
140
141 EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() {
142 removeHandler(endpoint);
143 }));
144 EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_))
145 .WillOnce(testing::SaveArg<2>(&removeHandler));
146
147 EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() {
148 endpoint->remove();
149 }));
150 EXPECT_CALL(*device, setup(testing::_))
151 .WillOnce(testing::InvokeArgument<0>(
152 std::make_error_code(std::errc::permission_denied), endpoint))
153 .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint));
154
155 reactor->manageMCTPDevice("/test", device);
156 reactor->tick();
157 reactor->unmanageMCTPDevice("/test");
158 }
159
TEST_F(MCTPReactorFixture,manageMockDeviceRemoved)160 TEST_F(MCTPReactorFixture, manageMockDeviceRemoved)
161 {
162 std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler;
163
164 std::vector<Association> requiredAssociation{
165 {"configured_by", "configures", "/test"}};
166 EXPECT_CALL(assoc,
167 associate("/xyz/openbmc_project/mctp/1/9", requiredAssociation))
168 .Times(2);
169 EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")).Times(2);
170
171 EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() {
172 removeHandler(endpoint);
173 }));
174 EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_))
175 .Times(2)
176 .WillRepeatedly(testing::SaveArg<2>(&removeHandler));
177
178 EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() {
179 endpoint->remove();
180 }));
181 EXPECT_CALL(*device, setup(testing::_))
182 .Times(2)
183 .WillRepeatedly(
184 testing::InvokeArgument<0>(std::error_code(), endpoint));
185
186 reactor->manageMCTPDevice("/test", device);
187 removeHandler(endpoint);
188 reactor->tick();
189 reactor->unmanageMCTPDevice("/test");
190 }
191
TEST(MCTPReactor,replaceConfiguration)192 TEST(MCTPReactor, replaceConfiguration)
193 {
194 MockAssociationServer assoc{};
195 auto reactor = std::make_shared<MCTPReactor>(assoc);
196 std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler;
197
198 std::vector<Association> requiredAssociation{
199 {"configured_by", "configures", "/test"}};
200
201 EXPECT_CALL(assoc,
202 associate("/xyz/openbmc_project/mctp/1/9", requiredAssociation))
203 .Times(2);
204 EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")).Times(2);
205
206 auto endpoint = std::make_shared<MockMCTPEndpoint>();
207 EXPECT_CALL(*endpoint, describe())
208 .WillRepeatedly(testing::Return("mock endpoint"));
209 EXPECT_CALL(*endpoint, eid()).WillRepeatedly(testing::Return(9));
210 EXPECT_CALL(*endpoint, network()).WillRepeatedly(testing::Return(1));
211 EXPECT_CALL(*endpoint, remove())
212 .Times(2)
213 .WillRepeatedly(testing::Invoke([&]() { removeHandler(endpoint); }));
214 EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_))
215 .Times(2)
216 .WillRepeatedly(testing::SaveArg<2>(&removeHandler));
217
218 auto initial = std::make_shared<MockMCTPDevice>();
219 EXPECT_CALL(*initial, describe())
220 .WillRepeatedly(testing::Return("mock device: initial"));
221 EXPECT_CALL(*initial, setup(testing::_))
222 .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint));
223 EXPECT_CALL(*initial, remove()).WillOnce(testing::Invoke([&]() {
224 endpoint->remove();
225 }));
226
227 auto replacement = std::make_shared<MockMCTPDevice>();
228 EXPECT_CALL(*replacement, describe())
229 .WillRepeatedly(testing::Return("mock device: replacement"));
230 EXPECT_CALL(*replacement, setup(testing::_))
231 .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint));
232 EXPECT_CALL(*replacement, remove()).WillOnce(testing::Invoke([&]() {
233 endpoint->remove();
234 }));
235
236 EXPECT_CALL(*endpoint, device())
237 .WillOnce(testing::Return(initial))
238 .WillOnce(testing::Return(initial))
239 .WillOnce(testing::Return(replacement))
240 .WillOnce(testing::Return(replacement));
241
242 reactor->manageMCTPDevice("/test", initial);
243 reactor->manageMCTPDevice("/test", replacement);
244 reactor->tick();
245 reactor->unmanageMCTPDevice("/test");
246
247 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(initial.get()));
248 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(replacement.get()));
249 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(endpoint.get()));
250 }
251