1 #include "common/instance_id.hpp"
2 #include "common/types.hpp"
3 #include "common/utils.hpp"
4 #include "mock_request.hpp"
5 #include "requester/handler.hpp"
6 #include "test/test_instance_id.hpp"
7 
8 #include <libpldm/base.h>
9 #include <libpldm/transport.h>
10 
11 #include <gmock/gmock.h>
12 #include <gtest/gtest.h>
13 
14 using namespace pldm::requester;
15 using namespace std::chrono;
16 
17 using ::testing::AtLeast;
18 using ::testing::Between;
19 using ::testing::Exactly;
20 using ::testing::NiceMock;
21 using ::testing::Return;
22 
23 class HandlerTest : public testing::Test
24 {
25   protected:
26     HandlerTest() : event(sdeventplus::Event::get_default()), instanceIdDb() {}
27 
28     int fd = 0;
29     mctp_eid_t eid = 0;
30     PldmTransport* pldmTransport = nullptr;
31     sdeventplus::Event event;
32     TestInstanceIdDb instanceIdDb;
33 
34     /** @brief This function runs the sd_event_run in a loop till all the events
35      *         in the testcase are dispatched and exits when there are no events
36      *         for the timeout time.
37      *
38      *  @param[in] timeout - maximum time to wait for an event
39      */
40     void waitEventExpiry(milliseconds timeout)
41     {
42         while (1)
43         {
44             auto sleepTime = duration_cast<microseconds>(timeout);
45             // Returns 0 on timeout
46             if (!sd_event_run(event.get(), sleepTime.count()))
47             {
48                 break;
49             }
50         }
51     }
52 
53   public:
54     bool nullResponse = false;
55     bool validResponse = false;
56     int callbackCount = 0;
57     bool response2 = false;
58 
59     void pldmResponseCallBack(mctp_eid_t /*eid*/, const pldm_msg* response,
60                               size_t respMsgLen)
61     {
62         if (response == nullptr && respMsgLen == 0)
63         {
64             nullResponse = true;
65         }
66         else
67         {
68             validResponse = true;
69         }
70         callbackCount++;
71     }
72 };
73 
74 TEST_F(HandlerTest, singleRequestResponseScenario)
75 {
76     Handler<NiceMock<MockRequest>> reqHandler(pldmTransport, event,
77                                               instanceIdDb, false, seconds(1),
78                                               2, milliseconds(100));
79     pldm::Request request{};
80     auto instanceId = instanceIdDb.next(eid);
81     EXPECT_EQ(instanceId, 0);
82     auto rc = reqHandler.registerRequest(
83         eid, instanceId, 0, 0, std::move(request),
84         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
85     EXPECT_EQ(rc, PLDM_SUCCESS);
86 
87     pldm::Response response(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
88     auto responsePtr = reinterpret_cast<const pldm_msg*>(response.data());
89     reqHandler.handleResponse(eid, instanceId, 0, 0, responsePtr,
90                               response.size());
91 
92     EXPECT_EQ(validResponse, true);
93 }
94 
95 TEST_F(HandlerTest, singleRequestInstanceIdTimerExpired)
96 {
97     Handler<NiceMock<MockRequest>> reqHandler(pldmTransport, event,
98                                               instanceIdDb, false, seconds(1),
99                                               2, milliseconds(100));
100     pldm::Request request{};
101     auto instanceId = instanceIdDb.next(eid);
102     EXPECT_EQ(instanceId, 0);
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     EXPECT_EQ(nullResponse, true);
112 }
113 
114 TEST_F(HandlerTest, multipleRequestResponseScenario)
115 {
116     Handler<NiceMock<MockRequest>> reqHandler(pldmTransport, event,
117                                               instanceIdDb, false, seconds(2),
118                                               2, milliseconds(100));
119     pldm::Request request{};
120     auto instanceId = instanceIdDb.next(eid);
121     EXPECT_EQ(instanceId, 0);
122     auto rc = reqHandler.registerRequest(
123         eid, instanceId, 0, 0, std::move(request),
124         std::move(std::bind_front(&HandlerTest::pldmResponseCallBack, this)));
125     EXPECT_EQ(rc, PLDM_SUCCESS);
126 
127     pldm::Request requestNxt{};
128     auto instanceIdNxt = instanceIdDb.next(eid);
129     EXPECT_EQ(instanceIdNxt, 1);
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, instanceId, 0, 0, responsePtr,
138                               response.size());
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, instanceIdNxt, 0, 0, responsePtr,
148                               response.size());
149 
150     EXPECT_EQ(validResponse, true);
151     EXPECT_EQ(callbackCount, 2);
152 }
153