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