xref: /openbmc/pldm/requester/test/handler_test.cpp (revision 5ea723773b102e5820fb638b27175b23705c1a05)
12abbce76SAndrew Jeffery #include "common/instance_id.hpp"
274f27c73STom Joseph #include "common/types.hpp"
374f27c73STom Joseph #include "common/utils.hpp"
474f27c73STom Joseph #include "mock_request.hpp"
574f27c73STom Joseph #include "requester/handler.hpp"
67c1dc7eaSAndrew Jeffery #include "test/test_instance_id.hpp"
774f27c73STom Joseph 
8c453e164SGeorge Liu #include <libpldm/base.h>
91ed5f7a6SRashmica Gupta #include <libpldm/transport.h>
10c453e164SGeorge Liu 
11a85c69d3SGilbert Chen #include <sdbusplus/async.hpp>
12a85c69d3SGilbert Chen 
1374f27c73STom Joseph #include <gmock/gmock.h>
1474f27c73STom Joseph #include <gtest/gtest.h>
1574f27c73STom Joseph 
1674f27c73STom Joseph using namespace pldm::requester;
1774f27c73STom Joseph using namespace std::chrono;
1874f27c73STom Joseph 
1974f27c73STom Joseph using ::testing::AtLeast;
2074f27c73STom Joseph using ::testing::Between;
2174f27c73STom Joseph using ::testing::Exactly;
2274f27c73STom Joseph using ::testing::NiceMock;
2374f27c73STom Joseph using ::testing::Return;
2474f27c73STom Joseph 
2574f27c73STom Joseph class HandlerTest : public testing::Test
2674f27c73STom Joseph {
2774f27c73STom Joseph   protected:
HandlerTest()28a330b2f0SAndrew Jeffery     HandlerTest() : event(sdeventplus::Event::get_default()), instanceIdDb() {}
2974f27c73STom Joseph 
3074f27c73STom Joseph     int fd = 0;
3174f27c73STom Joseph     mctp_eid_t eid = 0;
321ed5f7a6SRashmica Gupta     PldmTransport* pldmTransport = nullptr;
3374f27c73STom Joseph     sdeventplus::Event event;
347c1dc7eaSAndrew Jeffery     TestInstanceIdDb instanceIdDb;
3574f27c73STom Joseph 
3674f27c73STom Joseph     /** @brief This function runs the sd_event_run in a loop till all the events
3774f27c73STom Joseph      *         in the testcase are dispatched and exits when there are no events
3874f27c73STom Joseph      *         for the timeout time.
3974f27c73STom Joseph      *
4074f27c73STom Joseph      *  @param[in] timeout - maximum time to wait for an event
4174f27c73STom Joseph      */
waitEventExpiry(milliseconds timeout)4274f27c73STom Joseph     void waitEventExpiry(milliseconds timeout)
4374f27c73STom Joseph     {
4474f27c73STom Joseph         while (1)
4574f27c73STom Joseph         {
4674f27c73STom Joseph             auto sleepTime = duration_cast<microseconds>(timeout);
4774f27c73STom Joseph             // Returns 0 on timeout
4874f27c73STom Joseph             if (!sd_event_run(event.get(), sleepTime.count()))
4974f27c73STom Joseph             {
5074f27c73STom Joseph                 break;
5174f27c73STom Joseph             }
5274f27c73STom Joseph         }
5374f27c73STom Joseph     }
5474f27c73STom Joseph 
5574f27c73STom Joseph   public:
5674f27c73STom Joseph     bool nullResponse = false;
5774f27c73STom Joseph     bool validResponse = false;
5874f27c73STom Joseph     int callbackCount = 0;
5974f27c73STom Joseph     bool response2 = false;
6074f27c73STom Joseph 
pldmResponseCallBack(mctp_eid_t,const pldm_msg * response,size_t respMsgLen)6174f27c73STom Joseph     void pldmResponseCallBack(mctp_eid_t /*eid*/, const pldm_msg* response,
6274f27c73STom Joseph                               size_t respMsgLen)
6374f27c73STom Joseph     {
6474f27c73STom Joseph         if (response == nullptr && respMsgLen == 0)
6574f27c73STom Joseph         {
6674f27c73STom Joseph             nullResponse = true;
6774f27c73STom Joseph         }
6874f27c73STom Joseph         else
6974f27c73STom Joseph         {
7074f27c73STom Joseph             validResponse = true;
7174f27c73STom Joseph         }
7274f27c73STom Joseph         callbackCount++;
7374f27c73STom Joseph     }
7474f27c73STom Joseph };
7574f27c73STom Joseph 
TEST_F(HandlerTest,singleRequestResponseScenario)7674f27c73STom Joseph TEST_F(HandlerTest, singleRequestResponseScenario)
7774f27c73STom Joseph {
7816c2a0a0SPatrick Williams     Handler<NiceMock<MockRequest>> reqHandler(
7916c2a0a0SPatrick Williams         pldmTransport, event, instanceIdDb, false, seconds(1), 2,
8016c2a0a0SPatrick Williams         milliseconds(100));
8174f27c73STom Joseph     pldm::Request request{};
82a330b2f0SAndrew Jeffery     auto instanceId = instanceIdDb.next(eid);
837c1dc7eaSAndrew Jeffery     EXPECT_EQ(instanceId, 0);
84a5ed6585STom Joseph     auto rc = reqHandler.registerRequest(
8574f27c73STom Joseph         eid, instanceId, 0, 0, std::move(request),
86c14fb4bdSAndrew Jeffery         std::bind_front(&HandlerTest::pldmResponseCallBack, this));
87a5ed6585STom Joseph     EXPECT_EQ(rc, PLDM_SUCCESS);
8874f27c73STom Joseph 
8974f27c73STom Joseph     pldm::Response response(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
9074f27c73STom Joseph     auto responsePtr = reinterpret_cast<const pldm_msg*>(response.data());
9174f27c73STom Joseph     reqHandler.handleResponse(eid, instanceId, 0, 0, responsePtr,
9293f1e00fSPavithra Barithaya                               response.size());
9374f27c73STom Joseph 
94a5ed6585STom Joseph     EXPECT_EQ(validResponse, true);
9574f27c73STom Joseph }
9674f27c73STom Joseph 
TEST_F(HandlerTest,singleRequestInstanceIdTimerExpired)9774f27c73STom Joseph TEST_F(HandlerTest, singleRequestInstanceIdTimerExpired)
9874f27c73STom Joseph {
9916c2a0a0SPatrick Williams     Handler<NiceMock<MockRequest>> reqHandler(
10016c2a0a0SPatrick Williams         pldmTransport, event, instanceIdDb, false, seconds(1), 2,
10116c2a0a0SPatrick Williams         milliseconds(100));
10274f27c73STom Joseph     pldm::Request request{};
103a330b2f0SAndrew Jeffery     auto instanceId = instanceIdDb.next(eid);
1047c1dc7eaSAndrew Jeffery     EXPECT_EQ(instanceId, 0);
105a5ed6585STom Joseph     auto rc = reqHandler.registerRequest(
10674f27c73STom Joseph         eid, instanceId, 0, 0, std::move(request),
107c14fb4bdSAndrew Jeffery         std::bind_front(&HandlerTest::pldmResponseCallBack, this));
108a5ed6585STom Joseph     EXPECT_EQ(rc, PLDM_SUCCESS);
10974f27c73STom Joseph 
11074f27c73STom Joseph     // Waiting for 500ms so that the instance ID expiry callback is invoked
11174f27c73STom Joseph     waitEventExpiry(milliseconds(500));
11274f27c73STom Joseph 
113a5ed6585STom Joseph     EXPECT_EQ(nullResponse, true);
11474f27c73STom Joseph }
11574f27c73STom Joseph 
TEST_F(HandlerTest,multipleRequestResponseScenario)11674f27c73STom Joseph TEST_F(HandlerTest, multipleRequestResponseScenario)
11774f27c73STom Joseph {
11816c2a0a0SPatrick Williams     Handler<NiceMock<MockRequest>> reqHandler(
11916c2a0a0SPatrick Williams         pldmTransport, event, instanceIdDb, false, seconds(2), 2,
12016c2a0a0SPatrick Williams         milliseconds(100));
12174f27c73STom Joseph     pldm::Request request{};
122a330b2f0SAndrew Jeffery     auto instanceId = instanceIdDb.next(eid);
1237c1dc7eaSAndrew Jeffery     EXPECT_EQ(instanceId, 0);
124a5ed6585STom Joseph     auto rc = reqHandler.registerRequest(
12574f27c73STom Joseph         eid, instanceId, 0, 0, std::move(request),
126c14fb4bdSAndrew Jeffery         std::bind_front(&HandlerTest::pldmResponseCallBack, this));
127a5ed6585STom Joseph     EXPECT_EQ(rc, PLDM_SUCCESS);
12874f27c73STom Joseph 
12974f27c73STom Joseph     pldm::Request requestNxt{};
130a330b2f0SAndrew Jeffery     auto instanceIdNxt = instanceIdDb.next(eid);
1317c1dc7eaSAndrew Jeffery     EXPECT_EQ(instanceIdNxt, 1);
132a5ed6585STom Joseph     rc = reqHandler.registerRequest(
13374f27c73STom Joseph         eid, instanceIdNxt, 0, 0, std::move(requestNxt),
134c14fb4bdSAndrew Jeffery         std::bind_front(&HandlerTest::pldmResponseCallBack, this));
135a5ed6585STom Joseph     EXPECT_EQ(rc, PLDM_SUCCESS);
13674f27c73STom Joseph 
13774f27c73STom Joseph     pldm::Response response(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
13874f27c73STom Joseph     auto responsePtr = reinterpret_cast<const pldm_msg*>(response.data());
1394ddee3a0SThu Nguyen     reqHandler.handleResponse(eid, instanceId, 0, 0, responsePtr,
14093f1e00fSPavithra Barithaya                               response.size());
141a5ed6585STom Joseph     EXPECT_EQ(validResponse, true);
142a5ed6585STom Joseph     EXPECT_EQ(callbackCount, 1);
14374f27c73STom Joseph     validResponse = false;
14474f27c73STom Joseph 
14574f27c73STom Joseph     // Waiting for 500ms and handle the response for the first request, to
14674f27c73STom Joseph     // simulate a delayed response for the first request
14774f27c73STom Joseph     waitEventExpiry(milliseconds(500));
14874f27c73STom Joseph 
1494ddee3a0SThu Nguyen     reqHandler.handleResponse(eid, instanceIdNxt, 0, 0, responsePtr,
15093f1e00fSPavithra Barithaya                               response.size());
15174f27c73STom Joseph 
152a5ed6585STom Joseph     EXPECT_EQ(validResponse, true);
153a5ed6585STom Joseph     EXPECT_EQ(callbackCount, 2);
15474f27c73STom Joseph }
155a85c69d3SGilbert Chen 
TEST_F(HandlerTest,singleRequestResponseScenarioUsingCoroutine)156a85c69d3SGilbert Chen TEST_F(HandlerTest, singleRequestResponseScenarioUsingCoroutine)
157a85c69d3SGilbert Chen {
158a85c69d3SGilbert Chen     exec::async_scope scope;
15916c2a0a0SPatrick Williams     Handler<NiceMock<MockRequest>> reqHandler(
16016c2a0a0SPatrick Williams         pldmTransport, event, instanceIdDb, false, seconds(1), 2,
16116c2a0a0SPatrick Williams         milliseconds(100));
162a85c69d3SGilbert Chen 
163a85c69d3SGilbert Chen     auto instanceId = instanceIdDb.next(eid);
164a85c69d3SGilbert Chen     EXPECT_EQ(instanceId, 0);
165a85c69d3SGilbert Chen 
16616c2a0a0SPatrick Williams     scope.spawn(
16716c2a0a0SPatrick Williams         stdexec::just() | stdexec::let_value([&] -> exec::task<void> {
168a85c69d3SGilbert Chen             pldm::Request request(sizeof(pldm_msg_hdr) + sizeof(uint8_t), 0);
169a85c69d3SGilbert Chen             const pldm_msg* responseMsg;
170a85c69d3SGilbert Chen             size_t responseLen;
1716b901e4aSThu Nguyen             int rc = PLDM_SUCCESS;
172a85c69d3SGilbert Chen 
173*5ea72377SPavithra Barithaya             auto requestPtr = new (request.data()) pldm_msg;
174a85c69d3SGilbert Chen             requestPtr->hdr.instance_id = instanceId;
175a85c69d3SGilbert Chen 
176a85c69d3SGilbert Chen             try
177a85c69d3SGilbert Chen             {
1786b901e4aSThu Nguyen                 std::tie(rc, responseMsg, responseLen) =
179a85c69d3SGilbert Chen                     co_await reqHandler.sendRecvMsg(eid, std::move(request));
180a85c69d3SGilbert Chen             }
181a85c69d3SGilbert Chen             catch (...)
182a85c69d3SGilbert Chen             {
183a85c69d3SGilbert Chen                 std::rethrow_exception(std::current_exception());
184a85c69d3SGilbert Chen             }
185a85c69d3SGilbert Chen 
186a85c69d3SGilbert Chen             EXPECT_NE(responseLen, 0);
187a85c69d3SGilbert Chen 
188a85c69d3SGilbert Chen             this->pldmResponseCallBack(eid, responseMsg, responseLen);
189a85c69d3SGilbert Chen 
190a85c69d3SGilbert Chen             EXPECT_EQ(validResponse, true);
191a85c69d3SGilbert Chen         }),
19261be7347SPatrick Williams         exec::default_task_context<void>(exec::inline_scheduler{}));
193a85c69d3SGilbert Chen 
194a85c69d3SGilbert Chen     pldm::Response mockResponse(sizeof(pldm_msg_hdr) + sizeof(uint8_t), 0);
195a85c69d3SGilbert Chen     auto mockResponsePtr =
196a85c69d3SGilbert Chen         reinterpret_cast<const pldm_msg*>(mockResponse.data());
197a85c69d3SGilbert Chen     reqHandler.handleResponse(eid, instanceId, 0, 0, mockResponsePtr,
198a85c69d3SGilbert Chen                               mockResponse.size() - sizeof(pldm_msg_hdr));
199a85c69d3SGilbert Chen 
200a85c69d3SGilbert Chen     stdexec::sync_wait(scope.on_empty());
201a85c69d3SGilbert Chen }
202a85c69d3SGilbert Chen 
TEST_F(HandlerTest,singleRequestCancellationScenarioUsingCoroutine)203a85c69d3SGilbert Chen TEST_F(HandlerTest, singleRequestCancellationScenarioUsingCoroutine)
204a85c69d3SGilbert Chen {
205a85c69d3SGilbert Chen     exec::async_scope scope;
20616c2a0a0SPatrick Williams     Handler<NiceMock<MockRequest>> reqHandler(
20716c2a0a0SPatrick Williams         pldmTransport, event, instanceIdDb, false, seconds(1), 2,
20816c2a0a0SPatrick Williams         milliseconds(100));
209a85c69d3SGilbert Chen     auto instanceId = instanceIdDb.next(eid);
210a85c69d3SGilbert Chen     EXPECT_EQ(instanceId, 0);
211a85c69d3SGilbert Chen 
212a85c69d3SGilbert Chen     bool stopped = false;
213a85c69d3SGilbert Chen 
21416c2a0a0SPatrick Williams     scope.spawn(
21516c2a0a0SPatrick Williams         stdexec::just() | stdexec::let_value([&] -> exec::task<void> {
216a85c69d3SGilbert Chen             pldm::Request request(sizeof(pldm_msg_hdr) + sizeof(uint8_t), 0);
217a85c69d3SGilbert Chen             pldm::Response response;
218a85c69d3SGilbert Chen 
219*5ea72377SPavithra Barithaya             auto requestPtr = new (request.data()) pldm_msg;
220a85c69d3SGilbert Chen             requestPtr->hdr.instance_id = instanceId;
221a85c69d3SGilbert Chen 
222a85c69d3SGilbert Chen             co_await reqHandler.sendRecvMsg(eid, std::move(request));
223a85c69d3SGilbert Chen 
224a85c69d3SGilbert Chen             EXPECT_TRUE(false); // unreachable
225a85c69d3SGilbert Chen         }) | stdexec::upon_stopped([&] { stopped = true; }),
22661be7347SPatrick Williams         exec::default_task_context<void>(exec::inline_scheduler{}));
227a85c69d3SGilbert Chen 
228a85c69d3SGilbert Chen     scope.request_stop();
229a85c69d3SGilbert Chen 
230a85c69d3SGilbert Chen     EXPECT_TRUE(stopped);
231a85c69d3SGilbert Chen 
232a85c69d3SGilbert Chen     stdexec::sync_wait(scope.on_empty());
233a85c69d3SGilbert Chen }
234a85c69d3SGilbert Chen 
TEST_F(HandlerTest,asyncRequestResponseByCoroutine)235a85c69d3SGilbert Chen TEST_F(HandlerTest, asyncRequestResponseByCoroutine)
236a85c69d3SGilbert Chen {
237a85c69d3SGilbert Chen     struct _
238a85c69d3SGilbert Chen     {
239366507c8SPatrick Williams         static exec::task<uint8_t> getTIDTask(Handler<MockRequest>& handler,
240366507c8SPatrick Williams                                               mctp_eid_t eid,
241a85c69d3SGilbert Chen                                               uint8_t instanceId, uint8_t& tid)
242a85c69d3SGilbert Chen         {
243a85c69d3SGilbert Chen             pldm::Request request(sizeof(pldm_msg_hdr), 0);
244*5ea72377SPavithra Barithaya             auto requestMsg = new (request.data()) pldm_msg;
245a85c69d3SGilbert Chen             const pldm_msg* responseMsg;
246a85c69d3SGilbert Chen             size_t responseLen;
247a85c69d3SGilbert Chen 
248a85c69d3SGilbert Chen             auto rc = encode_get_tid_req(instanceId, requestMsg);
249a85c69d3SGilbert Chen             EXPECT_EQ(rc, PLDM_SUCCESS);
250a85c69d3SGilbert Chen 
2516b901e4aSThu Nguyen             std::tie(rc, responseMsg, responseLen) =
252a85c69d3SGilbert Chen                 co_await handler.sendRecvMsg(eid, std::move(request));
253a85c69d3SGilbert Chen             EXPECT_NE(responseLen, 0);
254a85c69d3SGilbert Chen 
255a85c69d3SGilbert Chen             uint8_t cc = 0;
256a85c69d3SGilbert Chen             rc = decode_get_tid_resp(responseMsg, responseLen, &cc, &tid);
257a85c69d3SGilbert Chen             EXPECT_EQ(rc, PLDM_SUCCESS);
258a85c69d3SGilbert Chen 
259a85c69d3SGilbert Chen             co_return cc;
260a85c69d3SGilbert Chen         }
261a85c69d3SGilbert Chen     };
262a85c69d3SGilbert Chen 
263a85c69d3SGilbert Chen     exec::async_scope scope;
264a85c69d3SGilbert Chen     Handler<MockRequest> reqHandler(pldmTransport, event, instanceIdDb, false,
265a85c69d3SGilbert Chen                                     seconds(1), 2, milliseconds(100));
266a85c69d3SGilbert Chen     auto instanceId = instanceIdDb.next(eid);
267a85c69d3SGilbert Chen 
268a85c69d3SGilbert Chen     uint8_t expectedTid = 1;
269a85c69d3SGilbert Chen 
270a85c69d3SGilbert Chen     // Execute a coroutine to send getTID command. The coroutine is suspended
271a85c69d3SGilbert Chen     // until reqHandler.handleResponse() is received.
272a85c69d3SGilbert Chen     scope.spawn(stdexec::just() | stdexec::let_value([&] -> exec::task<void> {
273a85c69d3SGilbert Chen                     uint8_t respTid = 0;
274a85c69d3SGilbert Chen 
27516c2a0a0SPatrick Williams                     co_await _::getTIDTask(reqHandler, eid, instanceId,
27616c2a0a0SPatrick Williams                                            respTid);
277a85c69d3SGilbert Chen 
278a85c69d3SGilbert Chen                     EXPECT_EQ(expectedTid, respTid);
279a85c69d3SGilbert Chen                 }),
28061be7347SPatrick Williams                 exec::default_task_context<void>(exec::inline_scheduler{}));
281a85c69d3SGilbert Chen 
282a85c69d3SGilbert Chen     pldm::Response mockResponse(sizeof(pldm_msg_hdr) + PLDM_GET_TID_RESP_BYTES,
283a85c69d3SGilbert Chen                                 0);
284*5ea72377SPavithra Barithaya     auto mockResponseMsg = new (mockResponse.data()) pldm_msg;
285a85c69d3SGilbert Chen 
286a85c69d3SGilbert Chen     // Compose response message of getTID command
287a85c69d3SGilbert Chen     encode_get_tid_resp(instanceId, PLDM_SUCCESS, expectedTid, mockResponseMsg);
288a85c69d3SGilbert Chen 
289a85c69d3SGilbert Chen     // Send response back to resume getTID coroutine to update respTid by
290a85c69d3SGilbert Chen     // calling  reqHandler.handleResponse() manually
291a85c69d3SGilbert Chen     reqHandler.handleResponse(eid, instanceId, PLDM_BASE, PLDM_GET_TID,
292a85c69d3SGilbert Chen                               mockResponseMsg,
293a85c69d3SGilbert Chen                               mockResponse.size() - sizeof(pldm_msg_hdr));
294a85c69d3SGilbert Chen 
295a85c69d3SGilbert Chen     stdexec::sync_wait(scope.on_empty());
296a85c69d3SGilbert Chen }
297