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