bios: Implement BIOSEnumAttribute

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

Implement SetAttrValueOnDbus and constructEntry for enum attribute

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ia6230485fd2d6d05f9f2ddb3a80b81f6556aee9f
diff --git a/test/libpldmresponder_bios_config_test.cpp b/test/libpldmresponder_bios_config_test.cpp
index 05a3ced..b6ec9a0 100644
--- a/test/libpldmresponder_bios_config_test.cpp
+++ b/test/libpldmresponder_bios_config_test.cpp
@@ -29,6 +29,7 @@
         std::vector<fs::path> paths = {
             "./bios_jsons/string_attrs.json",
             "./bios_jsons/integer_attrs.json",
+            "./bios_jsons/enum_attrs.json",
         };
 
         for (auto& path : paths)
@@ -163,6 +164,29 @@
                           jsonEntry->at("default_value").get<uint64_t>());
                 break;
             }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto [pvHdls, defInds] =
+                    table::attribute::decodeEnumEntry(entry);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+                std::vector<std::string> strings;
+                for (auto pv : pvHdls)
+                {
+                    auto s = biosStringTable.findString(pv);
+                    strings.emplace_back(s);
+                }
+                EXPECT_EQ(strings, possibleValues);
+                EXPECT_EQ(defInds.size(), 1);
+
+                auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
+                auto defaultValues = jsonEntry->at("default_values")
+                                         .get<std::vector<std::string>>();
+                EXPECT_EQ(defValue, defaultValues[0]);
+
+                break;
+            }
             default:
                 EXPECT_TRUE(false);
                 break;
@@ -198,6 +222,19 @@
                 EXPECT_EQ(value, defValue);
                 break;
             }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto indices = table::attribute_value::decodeEnumEntry(entry);
+                EXPECT_EQ(indices.size(), 1);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+
+                auto defValues = jsonEntry->at("default_values")
+                                     .get<std::vector<std::string>>();
+                EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
+                break;
+            }
             default:
                 EXPECT_TRUE(false);
                 break;
diff --git a/test/libpldmresponder_bios_enum_attribute_test.cpp b/test/libpldmresponder_bios_enum_attribute_test.cpp
new file mode 100644
index 0000000..4cd5809
--- /dev/null
+++ b/test/libpldmresponder_bios_enum_attribute_test.cpp
@@ -0,0 +1,212 @@
+#include "libpldmresponder/bios_enum_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 ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Throw;
+
+class TestBIOSEnumAttribute : public ::testing::Test
+{
+  public:
+    const auto& getPossibleValues(const BIOSEnumAttribute& attribute)
+    {
+        return attribute.possibleValues;
+    }
+
+    const auto& getDefaultValue(const BIOSEnumAttribute& attribute)
+    {
+        return attribute.defaultValue;
+    }
+};
+
+TEST_F(TestBIOSEnumAttribute, CtorTest)
+{
+    auto jsonEnumReadOnly = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Concurrent" ]
+      })"_json;
+
+    BIOSEnumAttribute enumReadOnly{jsonEnumReadOnly, nullptr};
+    EXPECT_EQ(enumReadOnly.name, "CodeUpdatePolicy");
+    EXPECT_TRUE(enumReadOnly.readOnly);
+    EXPECT_THAT(getPossibleValues(enumReadOnly),
+                ElementsAreArray({"Concurrent", "Disruptive"}));
+    EXPECT_EQ(getDefaultValue(enumReadOnly), "Concurrent");
+
+    auto jsonEnumReadOnlyError = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_value" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Concurrent" ]
+      })"_json; // possible_value -> possible_values
+    EXPECT_THROW((BIOSEnumAttribute{jsonEnumReadOnlyError, nullptr}),
+                 Json::exception);
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "FWBootSide",
+         "possible_values" : [ "Perm", "Temp" ],
+         "default_values" : [ "Perm" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.FWBoot.Side",
+               "property_name" : "Side",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+            }
+      })"_json;
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, nullptr};
+    EXPECT_EQ(enumReadWrite.name, "FWBootSide");
+    EXPECT_TRUE(!enumReadWrite.readOnly);
+}
+
+TEST_F(TestBIOSEnumAttribute, ConstructEntry)
+{
+    MockBIOSStringTable biosStringTable;
+    MockdBusHandler dbusHandler;
+
+    auto jsonEnumReadOnly = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ]
+      })"_json;
+
+    std::vector<uint8_t> expectedAttrEntry{
+        0,    0, /* attr handle */
+        0x80,    /* attr type enum read-only*/
+        4,    0, /* attr name handle */
+        2,       /* number of possible value */
+        2,    0, /* possible value handle */
+        3,    0, /* possible value handle */
+        1,       /* number of default value */
+        1        /* defaut value string handle index */
+    };
+
+    std::vector<uint8_t> expectedAttrValueEntry{
+        0, 0, /* attr handle */
+        0x80, /* attr type enum read-only*/
+        1,    /* number of current value */
+        1     /* current value string handle index */
+    };
+
+    BIOSEnumAttribute enumReadOnly{jsonEnumReadOnly, nullptr};
+
+    ON_CALL(biosStringTable, findHandle(StrEq("Concurrent")))
+        .WillByDefault(Return(2));
+    ON_CALL(biosStringTable, findHandle(StrEq("Disruptive")))
+        .WillByDefault(Return(3));
+    ON_CALL(biosStringTable, findHandle(StrEq("CodeUpdatePolicy")))
+        .WillByDefault(Return(4));
+
+    checkConstructEntry(enumReadOnly, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.abc.def",
+               "property_name" : "Policy",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+          }
+      })"_json;
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, &dbusHandler};
+
+    EXPECT_CALL(dbusHandler,
+                getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Policy"),
+                                       StrEq("xyz.openbmc.abc.def")))
+        .WillOnce(Throw(std::exception()));
+
+    /* Set expected attr type to read-write */
+    expectedAttrEntry[2] = PLDM_BIOS_ENUMERATION;
+    expectedAttrValueEntry[2] = PLDM_BIOS_ENUMERATION;
+
+    checkConstructEntry(enumReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    EXPECT_CALL(dbusHandler,
+                getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Policy"),
+                                       StrEq("xyz.openbmc.abc.def")))
+        .WillOnce(Return(PropertyValue(true)));
+
+    expectedAttrValueEntry = {
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-write*/
+        1,    /* number of current value */
+        0     /* current value string handle index */
+    };
+
+    checkConstructEntry(enumReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+}
+
+TEST_F(TestBIOSEnumAttribute, setAttrValueOnDbus)
+{
+    MockBIOSStringTable biosStringTable;
+    MockdBusHandler dbusHandler;
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.abc.def",
+               "property_name" : "Policy",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+          }
+      })"_json;
+    DBusMapping dbusMapping{"/xyz/abc/def", "xyz.openbmc.abc.def", "Policy",
+                            "bool"};
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, &dbusHandler};
+
+    std::vector<uint8_t> attrEntry{
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-only*/
+        4, 0, /* attr name handle */
+        2,    /* number of possible value */
+        2, 0, /* possible value handle */
+        3, 0, /* possible value handle */
+        1,    /* number of default value */
+        1     /* defaut value string handle index */
+    };
+
+    ON_CALL(biosStringTable, findString(2))
+        .WillByDefault(Return(std::string("Concurrent")));
+    ON_CALL(biosStringTable, findString(3))
+        .WillByDefault(Return(std::string("Disruptive")));
+
+    std::vector<uint8_t> attrValueEntry{
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-only*/
+        1,    /* number of current value */
+        0     /* current value string handle index */
+    };
+
+    EXPECT_CALL(dbusHandler,
+                setDbusProperty(dbusMapping, PropertyValue{bool(true)}))
+        .Times(1);
+    enumReadWrite.setAttrValueOnDbus(
+        reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+            attrValueEntry.data()),
+        reinterpret_cast<pldm_bios_attr_table_entry*>(attrEntry.data()),
+        biosStringTable);
+}
\ No newline at end of file
diff --git a/test/meson.build b/test/meson.build
index 0210833..159cfc2 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -23,6 +23,7 @@
   'libpldmresponder_bios_string_attribute_test',
   'libpldmresponder_bios_config_test',
   'libpldmresponder_bios_integer_attribute_test',
+  'libpldmresponder_bios_enum_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
index eb95c33..568fad9 100644
--- a/test/mocked_bios.hpp
+++ b/test/mocked_bios.hpp
@@ -14,6 +14,8 @@
     }
 
     MOCK_METHOD(uint16_t, findHandle, (const std::string&), (const override));
+
+    MOCK_METHOD(std::string, findString, (const uint16_t), (const override));
 };
 
 void checkHeader(const Table& attrEntry, const Table& attrValueEntry)