libpldm: Add encode/decode APIs for GetFRURecordByOption

This commit implements the encode/decode APIs for
GetFRURecordByOption command which is defined in
DSP0257 v1.0.0 Table 12

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: If3649ac5e9b8e32a7b64bb26d443396d42b8c827
diff --git a/libpldm/fru.c b/libpldm/fru.c
index 242fc28..64c1aba 100644
--- a/libpldm/fru.c
+++ b/libpldm/fru.c
@@ -1,4 +1,6 @@
+#include <assert.h>
 #include <endian.h>
+#include <stdbool.h>
 #include <string.h>
 
 #include "fru.h"
@@ -199,6 +201,154 @@
 	return PLDM_SUCCESS;
 }
 
+int encode_get_fru_record_by_option_req(
+    uint8_t instance_id, uint32_t data_transfer_handle,
+    uint16_t fru_table_handle, uint16_t record_set_identifier,
+    uint8_t record_type, uint8_t field_type, uint8_t transfer_op_flag,
+    struct pldm_msg *msg, size_t payload_length)
+{
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length !=
+	    sizeof(struct pldm_get_fru_record_by_option_req)) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_header_info header = {0};
+	header.instance = instance_id;
+	header.msg_type = PLDM_REQUEST;
+	header.pldm_type = PLDM_FRU;
+	header.command = PLDM_GET_FRU_RECORD_BY_OPTION;
+	int rc = pack_pldm_header(&header, &(msg->hdr));
+	if (rc != PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_get_fru_record_by_option_req *req =
+	    (struct pldm_get_fru_record_by_option_req *)msg->payload;
+
+	req->data_transfer_handle = htole32(data_transfer_handle);
+	req->fru_table_handle = htole16(fru_table_handle);
+	req->record_set_identifier = htole16(record_set_identifier);
+	req->record_type = record_type;
+	req->field_type = field_type;
+	req->transfer_op_flag = transfer_op_flag;
+
+	return PLDM_SUCCESS;
+}
+
+int decode_get_fru_record_by_option_req(
+    const struct pldm_msg *msg, size_t payload_length,
+    uint32_t *data_transfer_handle, uint16_t *fru_table_handle,
+    uint16_t *record_set_identifier, uint8_t *record_type, uint8_t *field_type,
+    uint8_t *transfer_op_flag)
+{
+	if (msg == NULL || data_transfer_handle == NULL ||
+	    fru_table_handle == NULL || record_set_identifier == NULL ||
+	    record_type == NULL || field_type == NULL ||
+	    transfer_op_flag == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length !=
+	    sizeof(struct pldm_get_fru_record_by_option_req)) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_get_fru_record_by_option_req *req =
+	    (struct pldm_get_fru_record_by_option_req *)msg->payload;
+
+	*data_transfer_handle = le32toh(req->data_transfer_handle);
+	*fru_table_handle = le16toh(req->fru_table_handle);
+	*record_set_identifier = le16toh(req->record_set_identifier);
+	*record_type = req->record_type;
+	*field_type = req->field_type;
+	*transfer_op_flag = req->transfer_op_flag;
+	return PLDM_SUCCESS;
+}
+
+int encode_get_fru_record_by_option_resp(uint8_t instance_id,
+					 uint8_t completion_code,
+					 uint32_t next_data_transfer_handle,
+					 uint8_t transfer_flag,
+					 const void *fru_structure_data,
+					 size_t data_size, struct pldm_msg *msg,
+					 size_t payload_length)
+{
+	if (msg == NULL || fru_structure_data == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_header_info header = {0};
+	header.instance = instance_id;
+	header.msg_type = PLDM_RESPONSE;
+	header.pldm_type = PLDM_FRU;
+	header.command = PLDM_GET_FRU_RECORD_BY_OPTION;
+	int rc = pack_pldm_header(&header, &(msg->hdr));
+	if (rc != PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_get_fru_record_by_option_resp *resp =
+	    (struct pldm_get_fru_record_by_option_resp *)msg->payload;
+
+	resp->completion_code = completion_code;
+	resp->next_data_transfer_handle = htole32(next_data_transfer_handle);
+	resp->transfer_flag = transfer_flag;
+
+	if (completion_code != PLDM_SUCCESS) {
+		return PLDM_SUCCESS;
+	}
+
+	if (payload_length !=
+	    PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + data_size) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	memcpy(resp->fru_structure_data, fru_structure_data, data_size);
+
+	return PLDM_SUCCESS;
+}
+
+int decode_get_fru_record_by_option_resp(
+    const struct pldm_msg *msg, size_t payload_length, uint8_t *completion_code,
+    uint32_t *next_transfer_handle, uint8_t *transfer_flag,
+    struct variable_field *fru_structure_data)
+{
+	if (msg == NULL || completion_code == NULL ||
+	    next_transfer_handle == NULL || transfer_flag == NULL ||
+	    fru_structure_data == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	*completion_code = msg->payload[0];
+	if (PLDM_SUCCESS != *completion_code) {
+		return PLDM_SUCCESS;
+	}
+
+	if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_get_fru_record_by_option_resp *resp =
+	    (struct pldm_get_fru_record_by_option_resp *)msg->payload;
+
+	*next_transfer_handle = le32toh(resp->next_data_transfer_handle);
+	*transfer_flag = resp->transfer_flag;
+	fru_structure_data->ptr = resp->fru_structure_data;
+	fru_structure_data->length =
+	    payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES;
+
+	return PLDM_SUCCESS;
+}
+
 int encode_get_fru_record_table_req(uint8_t instance_id,
 				    uint32_t data_transfer_handle,
 				    uint8_t transfer_operation_flag,
diff --git a/libpldm/fru.h b/libpldm/fru.h
index 4a8205e..3ec9e43 100644
--- a/libpldm/fru.h
+++ b/libpldm/fru.h
@@ -10,11 +10,13 @@
 #include <stdint.h>
 
 #include "base.h"
+#include "utils.h"
 
 #define PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES 0
 #define PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES 19
 #define PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES 5
 #define PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES 6
+#define PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES 6
 
 #define FRU_TABLE_CHECKSUM_SIZE 4
 
@@ -104,6 +106,22 @@
 	uint8_t fru_record_table_data[1];
 } __attribute__((packed));
 
+struct pldm_get_fru_record_by_option_req {
+	uint32_t data_transfer_handle;
+	uint16_t fru_table_handle;
+	uint16_t record_set_identifier;
+	uint8_t record_type;
+	uint8_t field_type;
+	uint8_t transfer_op_flag;
+} __attribute__((packed));
+
+struct pldm_get_fru_record_by_option_resp {
+	uint8_t completion_code;
+	uint32_t next_data_transfer_handle;
+	uint8_t transfer_flag;
+	uint8_t fru_structure_data[1];
+} __attribute__((packed));
+
 /** @struct pldm_fru_record_tlv
  *
  *  Structure representing each FRU field entry (type, length, value)
@@ -235,6 +253,53 @@
 				     uint32_t next_data_transfer_handle,
 				     uint8_t transfer_flag,
 				     struct pldm_msg *msg);
+
+/* GetFRURecordByOption */
+
+/** @brief Decode GetFRURecordByOption request data
+ *
+ *  @param[in] msg - PLDM request message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] data_transfer_handle - A handle, used to identify a FRU Record
+ *              Table data transfer
+ *  @param[out] fru_table_handle - A handle, used to identify a FRU DATA
+ *              records
+ *  @param[out] record_set_identifier - FRU record set identifier
+ *  @param[out] record_type - FRU record type
+ *  @param[out] field_type - FRU field type
+ *  @param[out] transfer_op_flag - A flag that indicates whether this is
+ *              the start of the transfer
+ *  @return pldm_completion_codes
+ */
+int decode_get_fru_record_by_option_req(
+    const struct pldm_msg *msg, size_t payload_length,
+    uint32_t *data_transfer_handle, uint16_t *fru_table_handle,
+    uint16_t *record_set_identifier, uint8_t *record_type, uint8_t *field_type,
+    uint8_t *transfer_op_flag);
+
+/** @brief Encode GetFRURecordByOption response data
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] next_data_transfer_handle - A handle that is used to identify the
+ *             next portion of the transfer
+ *  @param[in] transfer_flag - The transfer flag that indicates what part of the
+ *             transfer this response represents
+ *  @param[in] fru_structure_data - FRU Structure Data
+ *  @param[in] data_size - Size of FRU Structrue Data
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg',
+ *         and for appending the FRU table to the msg.
+ */
+int encode_get_fru_record_by_option_resp(uint8_t instance_id,
+					 uint8_t completion_code,
+					 uint32_t next_data_transfer_handle,
+					 uint8_t transfer_flag,
+					 const void *fru_structure_data,
+					 size_t data_size, struct pldm_msg *msg,
+					 size_t payload_length);
+
 /* Requester */
 
 /* GetFruRecordTable */
@@ -300,6 +365,48 @@
 		      uint8_t num_frus, uint8_t encoding, uint8_t *tlvs,
 		      size_t tlvs_size);
 
+/* GetFRURecordByOption */
+
+/** @brief Create a PLDM request message for GetFRURecordByOption
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] data_transfer_handle - A handle, used to identify a FRU Record
+ *             Table data transfer
+ *  @param[in] fru_table_handle - A handle, used to identify a FRU DATA records
+ *  @param[in] record_set_identifier - FRU record set identifier
+ *  @param[in] record_type - FRU record type
+ *  @param[in] field_type - FRU field type
+ *  @param[in] transfer_op_flag - A flag that indicates whether this is
+ *             the start of the transfer
+ *  @param[in,out] msg - Message will be written to this
+ *  @param[in] payload_length - Length of request message payload
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int encode_get_fru_record_by_option_req(
+    uint8_t instance_id, uint32_t data_transfer_handle,
+    uint16_t fru_table_handle, uint16_t record_set_identifier,
+    uint8_t record_type, uint8_t field_type, uint8_t transfer_op_flag,
+    struct pldm_msg *msg, size_t payload_length);
+
+/** @brief Decode GetFRURecordByOption response data
+ *
+ *  @param[in] msg - Response message
+ *  @param[in] payload_length - Length of response message payload
+ *  @param[out] completion_code - Pointer to response msg's PLDM completion code
+ *  @param[out] next_data_transfer_handle - A handle used to identify the next
+ *              portion of the transfer
+ *  @param[out] transfer_flag - The transfer flag that indicates what part of
+ *              the transfer this response represents
+ *  @param[out] fru_structure_data - FRU Structure Data
+ *  @return pldm_completion_codes
+ */
+int decode_get_fru_record_by_option_resp(
+    const struct pldm_msg *msg, size_t payload_length, uint8_t *completion_code,
+    uint32_t *next_transfer_handle, uint8_t *transfer_flag,
+    struct variable_field *fru_structure_data);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpldm/tests/libpldm_fru_test.cpp b/libpldm/tests/libpldm_fru_test.cpp
index d79df4b..68d68a6 100644
--- a/libpldm/tests/libpldm_fru_test.cpp
+++ b/libpldm/tests/libpldm_fru_test.cpp
@@ -1,6 +1,7 @@
 #include <string.h>
 
 #include <array>
+#include <cstring>
 
 #include "libpldm/base.h"
 #include "libpldm/fru.h"
@@ -514,3 +515,260 @@
 
     ASSERT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
+
+TEST(GetFRURecordByOption, testGoodEncodeRequest)
+{
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint16_t fruTableHandle = 4;
+    uint16_t recordSetIdentifier = 5;
+    uint8_t recordType = 6;
+    uint8_t fieldType = 7;
+    uint8_t transferOpFlag = 0;
+
+    constexpr auto payLoadLength = sizeof(pldm_get_fru_record_by_option_req);
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> request;
+    auto reqMsg = reinterpret_cast<pldm_msg*>(request.data());
+
+    auto rc = encode_get_fru_record_by_option_req(
+        instanceId, dataTransferHandle, fruTableHandle, recordSetIdentifier,
+        recordType, fieldType, transferOpFlag, reqMsg, payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(instanceId, reqMsg->hdr.instance_id);
+
+    auto payLoadMsg =
+        reinterpret_cast<pldm_get_fru_record_by_option_req*>(reqMsg->payload);
+
+    EXPECT_EQ(le32toh(payLoadMsg->data_transfer_handle), dataTransferHandle);
+    EXPECT_EQ(le16toh(payLoadMsg->fru_table_handle), fruTableHandle);
+    EXPECT_EQ(le16toh(payLoadMsg->record_set_identifier), recordSetIdentifier);
+    EXPECT_EQ(payLoadMsg->record_type, recordType);
+    EXPECT_EQ(payLoadMsg->field_type, fieldType);
+    EXPECT_EQ(payLoadMsg->transfer_op_flag, transferOpFlag);
+}
+
+TEST(GetFRURecordByOption, testBadEncodeRequest)
+{
+
+    constexpr auto payLoadLength = sizeof(pldm_get_fru_record_by_option_req);
+
+    auto rc = encode_get_fru_record_by_option_req(1, 2, 3, 4, 5, 6, 0, nullptr,
+                                                  payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> request;
+    auto reqMsg = reinterpret_cast<pldm_msg*>(request.data());
+
+    rc = encode_get_fru_record_by_option_req(1, 2, 3, 4, 5, 6, 0, reqMsg,
+                                             payLoadLength - 1);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(GetFRURecordByOption, testGoodDecodeRequest)
+{
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint16_t fruTableHandle = 4;
+    uint16_t recordSetIdentifier = 5;
+    uint8_t recordType = 6;
+    uint8_t fieldType = 7;
+    uint8_t transferOpFlag = 0;
+
+    constexpr auto payLoadLength = sizeof(pldm_get_fru_record_by_option_req);
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> request;
+    auto reqMsg = reinterpret_cast<pldm_msg*>(request.data());
+
+    auto rc = encode_get_fru_record_by_option_req(
+        instanceId, dataTransferHandle, fruTableHandle, recordSetIdentifier,
+        recordType, fieldType, transferOpFlag, reqMsg, payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    uint32_t retDataTransferHandle{};
+    uint16_t retFruTableHandle{};
+    uint16_t retRecordSetIdentifier{};
+    uint8_t retRecordType{};
+    uint8_t retFieldType{};
+    uint8_t retTransferOpFlag{};
+
+    rc = decode_get_fru_record_by_option_req(
+        reqMsg, payLoadLength, &retDataTransferHandle, &retFruTableHandle,
+        &retRecordSetIdentifier, &retRecordType, &retFieldType,
+        &retTransferOpFlag);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(retDataTransferHandle, dataTransferHandle);
+    EXPECT_EQ(retFruTableHandle, fruTableHandle);
+    EXPECT_EQ(retRecordSetIdentifier, recordSetIdentifier);
+    EXPECT_EQ(retRecordType, recordType);
+    EXPECT_EQ(retFieldType, fieldType);
+    EXPECT_EQ(retTransferOpFlag, transferOpFlag);
+}
+
+TEST(GetFRURecordByOption, testBadDecodeRequest)
+{
+    constexpr auto payLoadLength = sizeof(pldm_get_fru_record_by_option_req);
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> request;
+    auto reqMsg = reinterpret_cast<pldm_msg*>(request.data());
+
+    uint32_t retDataTransferHandle{};
+    uint16_t retFruTableHandle{};
+    uint16_t retRecordSetIdentifier{};
+    uint8_t retRecordType{};
+    uint8_t retFieldType{};
+    uint8_t retTransferOpFlag{};
+
+    auto rc = decode_get_fru_record_by_option_req(
+        reqMsg, payLoadLength - 1, &retDataTransferHandle, &retFruTableHandle,
+        &retRecordSetIdentifier, &retRecordType, &retFieldType,
+        &retTransferOpFlag);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+
+    rc = decode_get_fru_record_by_option_req(
+        reqMsg, payLoadLength - 1, nullptr, &retFruTableHandle,
+        &retRecordSetIdentifier, &retRecordType, &retFieldType,
+        &retTransferOpFlag);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(GetFruRecordByOption, testGoodEncodeResponse)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint8_t transferFlag = 5;
+
+    std::array<uint8_t, 5> fruData = {1, 2, 3, 4, 5};
+    constexpr auto payLoadLength =
+        sizeof(pldm_get_fru_record_by_option_resp) - 1 + fruData.size();
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> response;
+    auto respMsg = reinterpret_cast<pldm_msg*>(response.data());
+
+    auto rc = encode_get_fru_record_by_option_resp(
+        instanceId, completionCode, dataTransferHandle, transferFlag,
+        fruData.data(), fruData.size(), respMsg, payLoadLength);
+
+    auto payLoadMsg =
+        reinterpret_cast<pldm_get_fru_record_by_option_resp*>(respMsg->payload);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(payLoadMsg->completion_code, completionCode);
+    EXPECT_EQ(payLoadMsg->next_data_transfer_handle,
+              htole32(dataTransferHandle));
+    EXPECT_EQ(payLoadMsg->transfer_flag, transferFlag);
+
+    EXPECT_EQ(std::memcmp(payLoadMsg->fru_structure_data, fruData.data(),
+                          fruData.size()),
+              0);
+}
+
+TEST(GetFruRecordByOption, testBadEncodeResponse)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint8_t transferFlag = 5;
+
+    std::array<uint8_t, 5> fruData = {1, 2, 3, 4, 5};
+    constexpr auto payLoadLength =
+        sizeof(pldm_get_fru_record_by_option_resp) - 1 + fruData.size();
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> response;
+    auto respMsg = reinterpret_cast<pldm_msg*>(response.data());
+
+    auto rc = encode_get_fru_record_by_option_resp(
+        instanceId, completionCode, dataTransferHandle, transferFlag, nullptr,
+        fruData.size(), respMsg, payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = encode_get_fru_record_by_option_resp(
+        instanceId, completionCode, dataTransferHandle, transferFlag,
+        fruData.data(), fruData.size(), respMsg, payLoadLength - 1);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(GetFruRecordByOption, testGoodDecodeResponse)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint8_t transferFlag = 5;
+
+    std::array<uint8_t, 5> fruData = {1, 2, 3, 4, 5};
+    constexpr auto payLoadLength =
+        sizeof(pldm_get_fru_record_by_option_resp) - 1 + fruData.size();
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> response;
+    auto respMsg = reinterpret_cast<pldm_msg*>(response.data());
+
+    auto rc = encode_get_fru_record_by_option_resp(
+        instanceId, completionCode, dataTransferHandle, transferFlag,
+        fruData.data(), fruData.size(), respMsg, payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    uint8_t retCompletionCode;
+    uint32_t retDataTransferHandle;
+    uint8_t retTransferFlag;
+    variable_field retFruData;
+
+    rc = decode_get_fru_record_by_option_resp(
+        respMsg, payLoadLength, &retCompletionCode, &retDataTransferHandle,
+        &retTransferFlag, &retFruData);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(retCompletionCode, completionCode);
+    EXPECT_EQ(retDataTransferHandle, dataTransferHandle);
+    EXPECT_EQ(retTransferFlag, transferFlag);
+    EXPECT_EQ(retFruData.length, fruData.size());
+    EXPECT_EQ(std::memcmp(fruData.data(), retFruData.ptr, fruData.size()), 0);
+}
+
+TEST(GetFruRecordByOption, testBadDecodeResponse)
+{
+
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint8_t instanceId = 2;
+    uint32_t dataTransferHandle = 3;
+    uint8_t transferFlag = 5;
+
+    std::array<uint8_t, 5> fruData = {1, 2, 3, 4, 5};
+    constexpr auto payLoadLength =
+        sizeof(pldm_get_fru_record_by_option_resp) - 1 + fruData.size();
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payLoadLength> response;
+    auto respMsg = reinterpret_cast<pldm_msg*>(response.data());
+
+    auto rc = encode_get_fru_record_by_option_resp(
+        instanceId, completionCode, dataTransferHandle, transferFlag,
+        fruData.data(), fruData.size(), respMsg, payLoadLength);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    uint8_t retCompletionCode;
+    uint32_t retDataTransferHandle;
+    uint8_t retTransferFlag;
+    variable_field retFruData;
+
+    rc = decode_get_fru_record_by_option_resp(respMsg, payLoadLength, nullptr,
+                                              &retDataTransferHandle,
+                                              &retTransferFlag, &retFruData);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_get_fru_record_by_option_resp(
+        respMsg, PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES - 1,
+        &retCompletionCode, &retDataTransferHandle, &retTransferFlag,
+        &retFruData);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}