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