bios: Implement BIOSStringAttribute

Implement BIOSStringAttribute, most of the code is copied from
bios/bios_parser.cpp.

Implement SetAttrValueOnDbus and constructEntry for string attribute

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ic7c6b35d32738d698d7649f97cb7843606b8a2ba
diff --git a/libpldmresponder/bios_string_attribute.cpp b/libpldmresponder/bios_string_attribute.cpp
new file mode 100644
index 0000000..1d9ec5d
--- /dev/null
+++ b/libpldmresponder/bios_string_attribute.cpp
@@ -0,0 +1,115 @@
+#include "bios_string_attribute.hpp"
+
+#include "utils.hpp"
+
+#include <iostream>
+#include <tuple>
+#include <variant>
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+BIOSStringAttribute::BIOSStringAttribute(const Json& entry,
+                                         DBusHandler* const dbusHandler) :
+    BIOSAttribute(entry, dbusHandler)
+{
+    std::string strTypeTmp = entry.at("string_type");
+    auto iter = strTypeMap.find(strTypeTmp);
+    if (iter == strTypeMap.end())
+    {
+        std::cerr << "Wrong string type, STRING_TYPE=" << strTypeTmp
+                  << " ATTRIBUTE_NAME=" << name << "\n";
+        throw std::invalid_argument("Wrong string type");
+    }
+    stringInfo.stringType = static_cast<uint8_t>(iter->second);
+
+    stringInfo.minLength = entry.at("minimum_string_length");
+    stringInfo.maxLength = entry.at("maximum_string_length");
+    stringInfo.defLength = entry.at("default_string_length");
+    stringInfo.defString = entry.at("default_string");
+
+    pldm_bios_table_attr_entry_string_info info = {
+        0,
+        readOnly,
+        stringInfo.stringType,
+        stringInfo.minLength,
+        stringInfo.maxLength,
+        stringInfo.defLength,
+        stringInfo.defString.data(),
+    };
+
+    const char* errmsg;
+    auto rc = pldm_bios_table_attr_entry_string_info_check(&info, &errmsg);
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Wrong field for string attribute, ATTRIBUTE_NAME=" << name
+                  << " ERRMSG=" << errmsg
+                  << " MINIMUM_STRING_LENGTH=" << stringInfo.minLength
+                  << " MAXIMUM_STRING_LENGTH=" << stringInfo.maxLength
+                  << " DEFAULT_STRING_LENGTH=" << stringInfo.defLength
+                  << " DEFAULT_STRING=" << stringInfo.defString << "\n";
+        throw std::invalid_argument("Wrong field for string attribute");
+    }
+}
+
+void BIOSStringAttribute::setAttrValueOnDbus(
+    const pldm_bios_attr_val_table_entry* attrValueEntry,
+    const pldm_bios_attr_table_entry*, const BIOSStringTable&)
+{
+    if (readOnly)
+    {
+        return;
+    }
+
+    PropertyValue value =
+        table::attribute_value::decodeStringEntry(attrValueEntry);
+    dbusHandler->setDbusProperty(*dBusMap, value);
+}
+
+std::string BIOSStringAttribute::getAttrValue()
+{
+    if (readOnly)
+    {
+        return stringInfo.defString;
+    }
+    try
+    {
+        return dbusHandler->getDbusProperty<std::string>(
+            dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(),
+            dBusMap->interface.c_str());
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Get String Attribute Value Error: AttributeName = "
+                  << name << std::endl;
+        return stringInfo.defString;
+    }
+}
+
+void BIOSStringAttribute::constructEntry(const BIOSStringTable& stringTable,
+                                         Table& attrTable,
+                                         Table& attrValueTable)
+{
+    pldm_bios_table_attr_entry_string_info info = {
+        stringTable.findHandle(name), readOnly,
+        stringInfo.stringType,        stringInfo.minLength,
+        stringInfo.maxLength,         stringInfo.defLength,
+        stringInfo.defString.data(),
+    };
+
+    auto attrTableEntry =
+        table::attribute::constructStringEntry(attrTable, &info);
+    auto [attrHandle, attrType, _] =
+        table::attribute::decodeHeader(attrTableEntry);
+    auto currStr = getAttrValue();
+    table::attribute_value::constructStringEntry(attrValueTable, attrHandle,
+                                                 attrType, currStr);
+}
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios_string_attribute.hpp b/libpldmresponder/bios_string_attribute.hpp
new file mode 100644
index 0000000..48b0d2d
--- /dev/null
+++ b/libpldmresponder/bios_string_attribute.hpp
@@ -0,0 +1,83 @@
+#pragma once
+#include "bios_attribute.hpp"
+
+#include <string>
+
+class TestBIOSStringAttribute;
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+/** @class BIOSStringAttribute
+ *  @brief Associate string entry(attr table and attribute value table) and dbus
+ *         attribute
+ */
+class BIOSStringAttribute : public BIOSAttribute
+{
+  public:
+    friend class ::TestBIOSStringAttribute;
+
+    /** @brief BIOS string types */
+    enum class Encoding : uint8_t
+    {
+        UNKNOWN = 0x00,
+        ASCII = 0x01,
+        HEX = 0x02,
+        UTF_8 = 0x03,
+        UTF_16LE = 0x04,
+        UTF_16BE = 0x05,
+        VENDOR_SPECIFIC = 0xFF
+    };
+
+    /** brief Mapping of string to enum for string type */
+    inline static const std::map<std::string, Encoding> strTypeMap{
+        {"Unknown", Encoding::UNKNOWN},
+        {"ASCII", Encoding::ASCII},
+        {"Hex", Encoding::HEX},
+        {"UTF-8", Encoding::UTF_8},
+        {"UTF-16LE", Encoding::UTF_16LE},
+        {"UTF-16LE", Encoding::UTF_16LE},
+        {"Vendor Specific", Encoding::VENDOR_SPECIFIC}};
+
+    /** @brief Construct a bios string attribute
+     *  @param[in] entry - Json Object
+     *  @param[in] dbusHandler - Dbus Handler
+     */
+    BIOSStringAttribute(const Json& entry, DBusHandler* const dbusHandler);
+
+    /** @brief Set Attribute value On Dbus according to the attribute value
+     *         entry
+     *  @param[in] attrValueEntry - The attribute value entry
+     *  @param[in] attrEntry - The attribute entry corresponding to the
+     *                         attribute value entry
+     *  @param[in] stringTable - The string table
+     */
+    void
+        setAttrValueOnDbus(const pldm_bios_attr_val_table_entry* attrValueEntry,
+                           const pldm_bios_attr_table_entry* attrEntry,
+                           const BIOSStringTable& stringTable) override;
+
+    /** @brief Construct corresponding entries at the end of the attribute table
+     *         and attribute value tables
+     *  @param[in] stringTable - The string Table
+     *  @param[in,out] attrTable - The attribute table
+     *  @param[in,out] attrValueTable - The attribute value table
+     */
+    void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
+                        Table& attrValueTable) override;
+
+  private:
+    /** @brief string field from json */
+    table::attribute::StringField stringInfo;
+
+    /** @brief Get attribute value on dbus */
+    std::string getAttrValue();
+};
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios_table.cpp b/libpldmresponder/bios_table.cpp
index 5fd6ece..e68884b 100644
--- a/libpldmresponder/bios_table.cpp
+++ b/libpldmresponder/bios_table.cpp
@@ -64,7 +64,7 @@
     {
         throw std::invalid_argument("Invalid String Handle");
     }
-    return decodeString(stringEntry);
+    return table::string::decodeString(stringEntry);
 }
 
 uint16_t BIOSStringTable::findHandle(const std::string& name) const
@@ -76,17 +76,21 @@
         throw std::invalid_argument("Invalid String Name");
     }
 
-    return decodeHandle(stringEntry);
+    return table::string::decodeHandle(stringEntry);
 }
 
-uint16_t
-    BIOSStringTable::decodeHandle(const pldm_bios_string_table_entry* entry)
+namespace table
+{
+
+namespace string
+{
+
+uint16_t decodeHandle(const pldm_bios_string_table_entry* entry)
 {
     return pldm_bios_table_string_entry_decode_handle(entry);
 }
 
-std::string
-    BIOSStringTable::decodeString(const pldm_bios_string_table_entry* entry)
+std::string decodeString(const pldm_bios_string_table_entry* entry)
 {
     auto strLength = pldm_bios_table_string_entry_decode_string_length(entry);
     std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
@@ -95,6 +99,91 @@
     return std::string(buffer.data(), buffer.data() + strLength);
 }
 
+} // namespace string
+
+namespace attribute
+{
+
+TableHeader decodeHeader(const pldm_bios_attr_table_entry* entry)
+{
+    auto attrHandle = pldm_bios_table_attr_entry_decode_attribute_handle(entry);
+    auto attrType = pldm_bios_table_attr_entry_decode_attribute_type(entry);
+    auto stringHandle = pldm_bios_table_attr_entry_decode_string_handle(entry);
+    return {attrHandle, attrType, stringHandle};
+}
+
+const pldm_bios_attr_table_entry*
+    constructStringEntry(Table& table,
+                         pldm_bios_table_attr_entry_string_info* info)
+{
+    auto entryLength =
+        pldm_bios_table_attr_entry_string_encode_length(info->def_length);
+
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength, 0);
+    pldm_bios_table_attr_entry_string_encode(table.data() + tableSize,
+                                             entryLength, info);
+    return reinterpret_cast<pldm_bios_attr_table_entry*>(table.data() +
+                                                         tableSize);
+}
+
+StringField decodeStringEntry(const pldm_bios_attr_table_entry* entry)
+{
+    auto strType = pldm_bios_table_attr_entry_string_decode_string_type(entry);
+    auto minLength = pldm_bios_table_attr_entry_string_decode_min_length(entry);
+    auto maxLength = pldm_bios_table_attr_entry_string_decode_max_length(entry);
+    auto defLength =
+        pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
+
+    std::vector<char> buffer(defLength + 1);
+    pldm_bios_table_attr_entry_string_decode_def_string(entry, buffer.data(),
+                                                        buffer.size());
+    return {strType, minLength, maxLength, defLength,
+            std::string(buffer.data(), buffer.data() + defLength)};
+}
+
+} // namespace attribute
+
+namespace attribute_value
+{
+
+TableHeader decodeHeader(const pldm_bios_attr_val_table_entry* entry)
+{
+    auto handle =
+        pldm_bios_table_attr_value_entry_decode_attribute_handle(entry);
+    auto type = pldm_bios_table_attr_value_entry_decode_attribute_type(entry);
+    return {handle, type};
+}
+
+std::string decodeStringEntry(const pldm_bios_attr_val_table_entry* entry)
+{
+    variable_field currentString{};
+    pldm_bios_table_attr_value_entry_string_decode_string(entry,
+                                                          &currentString);
+    return std::string(currentString.ptr,
+                       currentString.ptr + currentString.length);
+}
+
+const pldm_bios_attr_val_table_entry*
+    constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                         const std::string& str)
+{
+    auto strLen = str.size();
+    auto entryLength =
+        pldm_bios_table_attr_value_entry_encode_string_length(strLen);
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength);
+    pldm_bios_table_attr_value_entry_encode_string(
+        table.data() + tableSize, entryLength, attrHandle, attrType, strLen,
+        str.c_str());
+    return reinterpret_cast<pldm_bios_attr_val_table_entry*>(table.data() +
+                                                             tableSize);
+}
+
+} // namespace attribute_value
+
+} // namespace table
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_table.hpp b/libpldmresponder/bios_table.hpp
index 7b28d49..d1dc7c9 100644
--- a/libpldmresponder/bios_table.hpp
+++ b/libpldmresponder/bios_table.hpp
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "libpldm/bios.h"
+#include "libpldm/bios_table.h"
 
 namespace pldm
 {
@@ -129,22 +130,119 @@
      */
     uint16_t findHandle(const std::string& name) const override;
 
-    /** @brief Get the string handle for the entry
-     *  @param[in] entry - Pointer to a bios string table entry
-     *  @return Handle to identify a string in the bios string table
-     */
-    static uint16_t decodeHandle(const pldm_bios_string_table_entry* entry);
-
-    /** @brief Get the string from the entry
-     *  @param[in] entry - Pointer to a bios string table entry
-     *  @return The String
-     */
-    static std::string decodeString(const pldm_bios_string_table_entry* entry);
-
   private:
     Table stringTable;
 };
 
+namespace table
+{
+
+namespace string
+{
+
+/** @brief Get the string handle for the entry
+ *  @param[in] entry - Pointer to a bios string table entry
+ *  @return Handle to identify a string in the bios string table
+ */
+uint16_t decodeHandle(const pldm_bios_string_table_entry* entry);
+
+/** @brief Get the string from the entry
+ *  @param[in] entry - Pointer to a bios string table entry
+ *  @return The String
+ */
+std::string decodeString(const pldm_bios_string_table_entry* entry);
+
+} // namespace string
+
+namespace attribute
+{
+
+/** @struct TableHeader
+ *  @brief Header of attribute table
+ */
+struct TableHeader
+{
+    uint16_t attrHandle;
+    uint8_t attrType;
+    uint16_t stringHandle;
+};
+
+/** @brief Decode header of attribute table entry
+ *  @param[in] entry - Pointer to an attribute table entry
+ *  @return Attribute table header
+ */
+TableHeader decodeHeader(const pldm_bios_attr_table_entry* entry);
+
+/** @struct StringField
+ *  @brief String field of attribute table
+ */
+struct StringField
+{
+    uint8_t stringType;
+    uint16_t minLength;
+    uint16_t maxLength;
+    uint16_t defLength;
+    std::string defString;
+};
+
+/** @brief decode string entry of attribute table
+ *  @param[in] entry - Pointer to an attribute table entry
+ *  @return String field of the entry
+ */
+StringField decodeStringEntry(const pldm_bios_attr_table_entry* entry);
+
+/** @brief construct string entry of attribute table at the end of the given
+ *         table
+ *  @param[in,out] table - The given table
+ *  @param[in] info - string info
+ *  @return pointer to the constructed entry
+ */
+const pldm_bios_attr_table_entry*
+    constructStringEntry(Table& table,
+                         pldm_bios_table_attr_entry_string_info* info);
+
+} // namespace attribute
+
+namespace attribute_value
+{
+
+/** @struct TableHeader
+ *  @brief Header of attribute value table
+ */
+struct TableHeader
+{
+    uint16_t attrHandle;
+    uint8_t attrType;
+};
+
+/** @brief Decode header of attribute value table
+ *  @param[in] entry - Pointer to an attribute value table entry
+ *  @return Attribute value table header
+ */
+TableHeader decodeHeader(const pldm_bios_attr_val_table_entry* entry);
+
+/** @brief Decode string entry of attribute value table
+ *  @param[in] entry - Pointer to an attribute value table entry
+ *  @return The decoded string
+ */
+std::string decodeStringEntry(const pldm_bios_attr_val_table_entry* entry);
+
+/** @brief Construct string entry of attribute value table at the end of the
+ *         given table
+ *  @param[in] table - The given table
+ *  @param[in] attrHandle - attribute handle
+ *  @param[in] attrType - attribute type
+ *  @param[in] str - The string
+ *  @return Pointer to the constructed entry
+ */
+const pldm_bios_attr_val_table_entry*
+    constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                         const std::string& str);
+
+} // namespace attribute_value
+
+} // namespace table
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 2797f51..372cae0 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -11,6 +11,7 @@
   'bios_table.cpp',
   'bios_parser.cpp',
   'bios_attribute.cpp',
+  'bios_string_attribute.cpp',
   'pdr_utils.cpp',
   'pdr.cpp',
   'platform.cpp',
diff --git a/test/libpldmresponder_bios_string_attribute_test.cpp b/test/libpldmresponder_bios_string_attribute_test.cpp
new file mode 100644
index 0000000..747e60f
--- /dev/null
+++ b/test/libpldmresponder_bios_string_attribute_test.cpp
@@ -0,0 +1,200 @@
+#include "libpldmresponder/bios_string_attribute.hpp"
+#include "mocked_bios.hpp"
+#include "mocked_utils.hpp"
+
+#include <memory>
+#include <nlohmann/json.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace pldm::responder::bios;
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Throw;
+
+class TestBIOSStringAttribute : public ::testing::Test
+{
+  public:
+    const auto& getStringInfo(const BIOSStringAttribute& biosStringAttribute)
+    {
+        return biosStringAttribute.stringInfo;
+    }
+};
+
+TEST_F(TestBIOSStringAttribute, CtorTest)
+{
+    auto jsonStringReadOnly = R"(  {
+            "attribute_name" : "str_example3",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string_length" : 2,
+            "default_string" : "ef"
+        })"_json;
+    BIOSStringAttribute stringReadOnly{jsonStringReadOnly, nullptr};
+    EXPECT_EQ(stringReadOnly.name, "str_example3");
+    EXPECT_TRUE(stringReadOnly.readOnly);
+
+    auto& stringInfo = getStringInfo(stringReadOnly);
+    EXPECT_EQ(stringInfo.stringType,
+              static_cast<uint8_t>(BIOSStringAttribute::Encoding::ASCII));
+    EXPECT_EQ(stringInfo.minLength, 1);
+    EXPECT_EQ(stringInfo.maxLength, 100);
+    EXPECT_EQ(stringInfo.defLength, 2);
+    EXPECT_EQ(stringInfo.defString, "ef");
+
+    auto jsonStringReadOnlyError = R"(  {
+            "attribute_name" : "str_example3",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string" : "ef"
+        })"_json; // missing default_string_length
+
+    EXPECT_THROW((BIOSStringAttribute{jsonStringReadOnlyError, nullptr}),
+                 Json::exception);
+
+    auto jsonStringReadWrite = R"({
+            "attribute_name" : "str_example1",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string_length" : 3,
+            "default_string" : "abc",
+            "dbus" : {
+                "object_path" : "/xyz/abc/def",
+                "interface" : "xyz.openbmc_project.str_example1.value",
+                "property_name" : "Str_example1",
+                "property_type" : "string"
+            }
+        })"_json;
+    BIOSStringAttribute stringReadWrite{jsonStringReadWrite, nullptr};
+
+    EXPECT_EQ(stringReadWrite.name, "str_example1");
+    EXPECT_TRUE(!stringReadWrite.readOnly);
+}
+
+TEST_F(TestBIOSStringAttribute, ConstructEntry)
+{
+    MockBIOSStringTable biosStringTable;
+    MockdBusHandler dbusHandler;
+
+    auto jsonStringReadOnly = R"({
+            "attribute_name" : "str_example1",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string_length" : 3,
+            "default_string" : "abc"
+        })"_json;
+
+    std::vector<uint8_t> expectedAttrEntry{
+        0,    0,       /* attr handle */
+        0x81,          /* attr type string read-only */
+        5,    0,       /* attr name handle */
+        1,             /* string type */
+        1,    0,       /* minimum length of the string in bytes */
+        100,  0,       /* maximum length of the string in bytes */
+        3,    0,       /* length of default string in length */
+        'a',  'b', 'c' /* default string  */
+    };
+
+    std::vector<uint8_t> expectedAttrValueEntry{
+        0,    0,        /* attr handle */
+        0x81,           /* attr type string read-only */
+        3,    0,        /* current string length */
+        'a',  'b', 'c', /* defaut value string handle index */
+    };
+
+    ON_CALL(biosStringTable, findHandle(StrEq("str_example1")))
+        .WillByDefault(Return(5));
+    BIOSStringAttribute stringReadOnly{jsonStringReadOnly, nullptr};
+
+    checkConstructEntry(stringReadOnly, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    auto jsonStringReadWrite = R"({
+            "attribute_name" : "str_example1",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string_length" : 3,
+            "default_string" : "abc",
+            "dbus" : {
+                "object_path" : "/xyz/abc/def",
+                "interface" : "xyz.openbmc_project.str_example1.value",
+                "property_name" : "Str_example1",
+                "property_type" : "string"
+            }
+        })"_json;
+    BIOSStringAttribute stringReadWrite{jsonStringReadWrite, &dbusHandler};
+
+    /* Set expected attr type to read-write */
+    expectedAttrEntry[2] = PLDM_BIOS_STRING;
+    expectedAttrValueEntry[2] = PLDM_BIOS_STRING;
+
+    EXPECT_CALL(
+        dbusHandler,
+        getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Str_example1"),
+                               StrEq("xyz.openbmc_project.str_example1.value")))
+        .WillOnce(Throw(std::exception()));
+
+    checkConstructEntry(stringReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    EXPECT_CALL(
+        dbusHandler,
+        getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Str_example1"),
+                               StrEq("xyz.openbmc_project.str_example1.value")))
+        .WillOnce(Return(PropertyValue(std::string("abcd"))));
+
+    expectedAttrValueEntry = {
+        0,   0,             /* attr handle */
+        1,                  /* attr type string read-write */
+        4,   0,             /* current string length */
+        'a', 'b', 'c', 'd', /* defaut value string handle index */
+    };
+
+    checkConstructEntry(stringReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+}
+
+TEST_F(TestBIOSStringAttribute, setAttrValueOnDbus)
+{
+    auto jsonStringReadWrite = R"({
+            "attribute_name" : "str_example1",
+            "string_type" : "ASCII",
+            "minimum_string_length" : 1,
+            "maximum_string_length" : 100,
+            "default_string_length" : 3,
+            "default_string" : "abc",
+            "dbus" : {
+                "object_path" : "/xyz/abc/def",
+                "interface" : "xyz.openbmc_project.str_example1.value",
+                "property_name" : "Str_example1",
+                "property_type" : "string"
+            }
+        })"_json;
+
+    MockdBusHandler dbusHandler;
+    MockBIOSStringTable biosStringTable;
+
+    BIOSStringAttribute stringReadWrite{jsonStringReadWrite, &dbusHandler};
+    DBusMapping dbusMapping{"/xyz/abc/def",
+                            "xyz.openbmc_project.str_example1.value",
+                            "Str_example1", "string"};
+    std::vector<uint8_t> attrValueEntry{
+        0,   0,             /* attr handle */
+        1,                  /* attr type string read-write */
+        4,   0,             /* current string length */
+        'a', 'b', 'c', 'd', /* defaut value string handle index */
+    };
+    auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+        attrValueEntry.data());
+    PropertyValue value = std::string("abcd");
+    EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1);
+    stringReadWrite.setAttrValueOnDbus(entry, nullptr, biosStringTable);
+}
diff --git a/test/meson.build b/test/meson.build
index a7bd88f..827f9fc 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -20,6 +20,7 @@
   'libpldmresponder_base_test',
   'libpldmresponder_bios_test',
   'libpldmresponder_bios_attribute_test',
+  'libpldmresponder_bios_string_attribute_test',
   'libpldmresponder_pdr_state_effecter_test',
   'libpldmresponder_bios_table_test',
   'libpldmresponder_platform_test',
diff --git a/test/mocked_bios.hpp b/test/mocked_bios.hpp
new file mode 100644
index 0000000..eb95c33
--- /dev/null
+++ b/test/mocked_bios.hpp
@@ -0,0 +1,57 @@
+#include "libpldmresponder/bios_table.hpp"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::ElementsAreArray;
+using namespace pldm::responder::bios;
+
+class MockBIOSStringTable : public BIOSStringTable
+{
+  public:
+    MockBIOSStringTable() : BIOSStringTable({})
+    {
+    }
+
+    MOCK_METHOD(uint16_t, findHandle, (const std::string&), (const override));
+};
+
+void checkHeader(const Table& attrEntry, const Table& attrValueEntry)
+{
+    auto attrHeader = table::attribute::decodeHeader(
+        reinterpret_cast<const pldm_bios_attr_table_entry*>(attrEntry.data()));
+    auto attrValueHeader = table::attribute_value::decodeHeader(
+        reinterpret_cast<const pldm_bios_attr_val_table_entry*>(
+            attrValueEntry.data()));
+
+    EXPECT_EQ(attrHeader.attrHandle, attrValueHeader.attrHandle);
+}
+
+void checkEntry(Table& entry, Table& expectedEntry)
+{
+    /** backup the attr handle */
+    auto attr0 = entry[0], eAttr0 = expectedEntry[0];
+    auto attr1 = entry[1], eAttr1 = expectedEntry[1];
+
+    /** attr handle is computed by libpldm, set it to 0 to test */
+    entry[0] = 0, expectedEntry[0] = 0;
+    entry[1] = 0, expectedEntry[1] = 0;
+
+    EXPECT_THAT(entry, ElementsAreArray(expectedEntry));
+
+    /** restore the attr handle */
+    entry[0] = attr0, expectedEntry[0] = eAttr0;
+    entry[1] = attr1, expectedEntry[1] = eAttr1;
+}
+
+void checkConstructEntry(BIOSAttribute& attribute, BIOSStringTable& stringTable,
+                         Table& expectedAttrEntry,
+                         Table& expectedAttrValueEntry)
+{
+    Table attrEntry, attrValueEntry;
+    attribute.constructEntry(stringTable, attrEntry, attrValueEntry);
+
+    checkHeader(attrEntry, attrValueEntry);
+    checkEntry(attrEntry, expectedAttrEntry);
+    checkEntry(attrValueEntry, expectedAttrValueEntry);
+}
\ No newline at end of file
diff --git a/test/mocked_utils.hpp b/test/mocked_utils.hpp
index 39ac8bb..0813830 100644
--- a/test/mocked_utils.hpp
+++ b/test/mocked_utils.hpp
@@ -1,5 +1,3 @@
-#pragma once
-
 #include "utils.hpp"
 
 #include <gmock/gmock.h>
@@ -32,4 +30,7 @@
   public:
     MOCK_METHOD(void, setDbusProperty,
                 (const DBusMapping&, const PropertyValue&), (const override));
+
+    MOCK_METHOD(PropertyValue, getDbusPropertyVariant,
+                (const char*, const char*, const char*), (const override));
 };