1 #include "libpldm/base.h"
2 
3 #include "common/types.hpp"
4 #include "common/utils.hpp"
5 #include "mock_request.hpp"
6 #include "pldmd/dbus_impl_requester.hpp"
7 #include "requester/handler.hpp"
8 
9 #include <gmock/gmock.h>
10 #include <gtest/gtest.h>
11 
12 using namespace pldm::requester;
13 using namespace std::chrono;
14 
15 using ::testing::AtLeast;
16 using ::testing::Between;
17 using ::testing::Exactly;
18 using ::testing::NiceMock;
19 using ::testing::Return;
20 
21 class HandlerTest : public testing::Test
22 {
23   protected:
24     HandlerTest() :
25         event(sdeventplus::Event::get_default()),
26         dbusImplReq(pldm::utils::DBusHandler::getBus(),
27                     "/xyz/openbmc_project/pldm")
28     {}
29 
30     int fd = 0;
31     mctp_eid_t eid = 0;
32     sdeventplus::Event event;
33     pldm::dbus_api::Requester dbusImplReq;
34 
35     /** @brief This function runs the sd_event_run in a loop till all the events
36      *         in the testcase are dispatched and exits when there are no events
37      *         for the timeout time.
38      *
39      *  @param[in] timeout - maximum time to wait for an event
40      */
41     void waitEventExpiry(milliseconds timeout)
42     {
43         while (1)
44         {
45             auto sleepTime = duration_cast<microseconds>(timeout);
46             // Returns 0 on timeout
47             if (!sd_event_run(event.get(), sleepTime.count()))
48             {
49                 break;
50             }
51         }
52     }
53 
54   public:
55     bool nullResponse = false;
56     bool validResponse = false;
57     int callbackCount = 0;
58     bool response2 = false;
59 
60     void pldmResponseCallBack(mctp_eid_t /*eid*/, const pldm_msg* response,
61                               size_t respMsgLen)
62     {
63         if (response == nullptr && respMsgLen == 0)
64         {
65             nullResponse = true;
66         }
67         else
68         {
69             validResponse = true;
70         }
71         callbackCount++;
72     }
73 };
74 
75 TEST_F(HandlerTest, singleRequestResponseScenario)
76 {
77     Handler<NiceMock<MockRequest>> reqHandler(
78         fd, event, dbusImplReq, false, 90000, seconds(1), 2, milliseconds(100));
79     pldm::Request request{};
80     auto instanceId = dbusImplReq.getInstanceId(eid);
81     auto rc = reqHandler.registerRequest(
82         eid, instanceId, 0, 0, std::move(request),
83         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
84     EXPECT_EQ(rc, PLDM_SUCCESS);
85 
86     pldm::Response response(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
87     auto responsePtr = reinterpret_cast<const pldm_msg*>(response.data());
88     reqHandler.handleResponse(eid, instanceId, 0, 0, responsePtr,
89                               sizeof(response));
90 
91     // handleResponse() will free the instance ID after calling the response
92     // handler, so the same instance ID is granted next as well
93     EXPECT_EQ(validResponse, true);
94     EXPECT_EQ(instanceId, dbusImplReq.getInstanceId(eid));
95 }
96 
97 TEST_F(HandlerTest, singleRequestInstanceIdTimerExpired)
98 {
99     Handler<NiceMock<MockRequest>> reqHandler(
100         fd, event, dbusImplReq, false, 90000, seconds(1), 2, milliseconds(100));
101     pldm::Request request{};
102     auto instanceId = dbusImplReq.getInstanceId(eid);
103     auto rc = reqHandler.registerRequest(
104         eid, instanceId, 0, 0, std::move(request),
105         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
106     EXPECT_EQ(rc, PLDM_SUCCESS);
107 
108     // Waiting for 500ms so that the instance ID expiry callback is invoked
109     waitEventExpiry(milliseconds(500));
110 
111     // cleanup() will free the instance ID after calling the response
112     // handler will no response, so the same instance ID is granted next
113     EXPECT_EQ(instanceId, dbusImplReq.getInstanceId(eid));
114     EXPECT_EQ(nullResponse, true);
115 }
116 
117 TEST_F(HandlerTest, multipleRequestResponseScenario)
118 {
119     Handler<NiceMock<MockRequest>> reqHandler(
120         fd, event, dbusImplReq, false, 90000, seconds(2), 2, milliseconds(100));
121     pldm::Request request{};
122     auto instanceId = dbusImplReq.getInstanceId(eid);
123     auto rc = reqHandler.registerRequest(
124         eid, instanceId, 0, 0, std::move(request),
125         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
126     EXPECT_EQ(rc, PLDM_SUCCESS);
127 
128     pldm::Request requestNxt{};
129     auto instanceIdNxt = dbusImplReq.getInstanceId(eid);
130     rc = reqHandler.registerRequest(
131         eid, instanceIdNxt, 0, 0, std::move(requestNxt),
132         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
133     EXPECT_EQ(rc, PLDM_SUCCESS);
134 
135     pldm::Response response(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
136     auto responsePtr = reinterpret_cast<const pldm_msg*>(response.data());
137     reqHandler.handleResponse(eid, instanceIdNxt, 0, 0, responsePtr,
138                               sizeof(response));
139     EXPECT_EQ(validResponse, true);
140     EXPECT_EQ(callbackCount, 1);
141     validResponse = false;
142 
143     // Waiting for 500ms and handle the response for the first request, to
144     // simulate a delayed response for the first request
145     waitEventExpiry(milliseconds(500));
146 
147     reqHandler.handleResponse(eid, instanceId, 0, 0, responsePtr,
148                               sizeof(response));
149 
150     EXPECT_EQ(validResponse, true);
151     EXPECT_EQ(callbackCount, 2);
152     EXPECT_EQ(instanceId, dbusImplReq.getInstanceId(eid));
153 }
154