Test cases for VPD-Manager editor functionalities

Implementation of test cases for editor functionalities provided
by VPD-Manager app.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: I6281f2be898f95e37c9575ea12affe57744a0cab
diff --git a/test/meson.build b/test/meson.build
index eab0c67..3f144b9 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -21,6 +21,7 @@
             'ipz_parser/parser.cpp',
             'keyword_vpd_parser_test/kw_vpd_test.cpp',
             'vpd-manager-test/reader_test.cpp',
+            'vpd-manager-test/editor_test.cpp'
            ]
 
 application_src =['../impl.cpp',
@@ -30,6 +31,7 @@
                    '../utils.cpp',
                    '../vpd-manager/reader_impl.cpp',
                    '../keyword_vpd_parser.cpp',
+                   '../vpd-manager/editor_impl.cpp'
                   ]
 
 foreach t : vpd_test
diff --git a/test/vpd-manager-test/editor_test.cpp b/test/vpd-manager-test/editor_test.cpp
new file mode 100644
index 0000000..c1e98b6
--- /dev/null
+++ b/test/vpd-manager-test/editor_test.cpp
@@ -0,0 +1,179 @@
+#include "editor_impl.hpp"
+#include "parser.hpp"
+
+#include <algorithm>
+#include <nlohmann/json.hpp>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::vpd;
+using namespace openpower::vpd::manager::editor;
+using namespace openpower::vpd::inventory;
+using namespace openpower::vpd::constants;
+
+class vpdManagerEditorTest : public ::testing::Test
+{
+  protected:
+    Binary vpd;
+
+    nlohmann::json jsonFile;
+
+    // map to hold the mapping of location code and inventory path
+    inventory::LocationCodeMap fruLocationCode;
+
+  public:
+    // constructor
+    vpdManagerEditorTest()
+    {
+        processJson();
+    }
+
+    void processJson();
+    void readFile(std::string pathToFile);
+};
+
+void vpdManagerEditorTest::readFile(std::string pathToFile)
+{
+    // read the json file and parse it
+    std::ifstream vpdFile(pathToFile, std::ios::binary);
+
+    if (!vpdFile)
+    {
+        throw std::runtime_error("json file not found");
+    }
+
+    vpd.assign((std::istreambuf_iterator<char>(vpdFile)),
+               std::istreambuf_iterator<char>());
+}
+
+void vpdManagerEditorTest::processJson()
+{
+    // read the json file and parse it
+    std::ifstream json("vpd-manager-test/vpd_editor_test.json",
+                       std::ios::binary);
+
+    if (!json)
+    {
+        throw std::runtime_error("json file not found");
+    }
+
+    jsonFile = nlohmann::json::parse(json);
+
+    const nlohmann::json& groupFRUS =
+        jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
+    for (const auto& itemFRUS : groupFRUS.items())
+    {
+        const std::vector<nlohmann::json>& groupEEPROM =
+            itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
+        for (const auto& itemEEPROM : groupEEPROM)
+        {
+            fruLocationCode.emplace(
+                itemEEPROM["extraInterfaces"][LOCATION_CODE_INF]["LocationCode"]
+                    .get_ref<const nlohmann::json::string_t&>(),
+                itemEEPROM["inventoryPath"]
+                    .get_ref<const nlohmann::json::string_t&>());
+        }
+    }
+}
+
+TEST_F(vpdManagerEditorTest, InvalidFile)
+{
+    Binary dataToUodate{'M', 'O', 'D', 'I', 'F', 'Y',
+                        'D', 'A', 'T', 'A', 'O', 'K'};
+
+    Binary emptyVpdFile;
+    try
+    {
+        // Invalid kwd name
+        EditorImpl edit("VINI", "SN", std::move(emptyVpdFile));
+        edit.updateKeyword(dataToUodate);
+    }
+    catch (const std::exception& e)
+    {
+        EXPECT_EQ(std::string(e.what()), std::string("Invalid File"));
+    }
+}
+
+TEST_F(vpdManagerEditorTest, InvalidHeader)
+{
+    Binary dataToUodate{'M', 'O', 'D', 'I', 'F', 'Y',
+                        'D', 'A', 'T', 'A', 'O', 'K'};
+
+    readFile("vpd-manager-test/invalidHeaderFile.dat");
+    try
+    {
+        // the path is dummy
+        EditorImpl edit("VINI", "SN", std::move(vpd));
+        edit.updateKeyword(dataToUodate);
+    }
+    catch (const std::exception& e)
+    {
+        EXPECT_EQ(std::string(e.what()), std::string("VHDR record not found"));
+    }
+}
+
+TEST_F(vpdManagerEditorTest, InvalidRecordName)
+{
+    Binary dataToUodate{'M', 'O', 'D', 'I', 'F', 'Y',
+                        'D', 'A', 'T', 'A', 'O', 'K'};
+
+    readFile("vpd-manager-test/vpdFile.dat");
+
+    try
+    {
+        // Invalid record name "VIN", path is dummy
+        EditorImpl edit("VIN", "SN", std::move(vpd));
+        edit.updateKeyword(dataToUodate);
+    }
+    catch (const std::exception& e)
+    {
+        EXPECT_EQ(std::string(e.what()), std::string("Record not found"));
+    }
+}
+
+TEST_F(vpdManagerEditorTest, InvalidKWdName)
+{
+    Binary dataToUodate{'M', 'O', 'D', 'I', 'F', 'Y',
+                        'D', 'A', 'T', 'A', 'O', 'K'};
+
+    readFile("vpd-manager-test/vpdFile.dat");
+
+    try
+    {
+        // All valid data
+        EditorImpl edit("VINI", "Sn", std::move(vpd));
+        edit.updateKeyword(dataToUodate);
+    }
+    catch (std::runtime_error& e)
+    {
+        EXPECT_EQ(std::string(e.what()), std::string("Keyword not found"));
+    }
+}
+
+TEST_F(vpdManagerEditorTest, UpdateKwd_Success)
+{
+    Binary dataToUodate{'M', 'O', 'D', 'I', 'F', 'Y',
+                        'D', 'A', 'T', 'A', 'O', 'K'};
+
+    readFile("vpd-manager-test/vpdFile.dat");
+
+    try
+    {
+        // All valid data
+        EditorImpl edit("VINI", "SN", std::move(vpd));
+        edit.updateKeyword(dataToUodate);
+    }
+    catch (std::runtime_error& e)
+    {
+        EXPECT_EQ(std::string(e.what()),
+                  std::string("Data updated successfully"));
+    }
+}
+
+int main(int argc, char** argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/test/vpd-manager-test/invalidHeaderFile.dat b/test/vpd-manager-test/invalidHeaderFile.dat
new file mode 100644
index 0000000..7f8de18
--- /dev/null
+++ b/test/vpd-manager-test/invalidHeaderFile.dat
Binary files differ
diff --git a/test/vpd-manager-test/vpdFile.dat b/test/vpd-manager-test/vpdFile.dat
new file mode 100644
index 0000000..147f63b
--- /dev/null
+++ b/test/vpd-manager-test/vpdFile.dat
Binary files differ
diff --git a/test/vpd-manager-test/vpd_editor_test.json b/test/vpd-manager-test/vpd_editor_test.json
new file mode 100644
index 0000000..f74e05a
--- /dev/null
+++ b/test/vpd-manager-test/vpd_editor_test.json
@@ -0,0 +1,172 @@
+{
+  "commonInterfaces": {
+    "xyz.openbmc_project.Inventory.Decorator.Asset": {
+      "PartNumber": {
+        "recordName": "VINI",
+        "keywordName": "PN"
+      },
+      "SerialNumber": {
+        "recordName": "VINI",
+        "keywordName": "SN"
+      },
+      "BuildDate": {
+        "recordName": "VR10",
+        "keywordName": "DC",
+        "encoding": "DATE"
+      }
+    },
+    "xyz.openbmc_project.Inventory.Item": {
+      "PrettyName": {
+        "recordName": "VINI",
+        "keywordName": "DR"
+      },
+     "Present": true
+    }
+  },
+  "frus": {
+    "/sys/bus/i2c/drivers/at24/8-0050/eeprom": [
+      {
+        "inventoryPath": "/system/chassis/motherboard",
+        "isSystemVpd": true,
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Board.Motherboard": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0"
+          }
+        }
+      },
+      {
+        "inventoryPath": "/system",
+        "inherit": false,
+        "isSystemVpd": true,
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.System": null,
+          "xyz.openbmc_project.Inventory.Decorator.Asset": {
+            "SerialNumber": {
+              "recordName": "VSYS",
+              "keywordName": "SE"
+            },
+            "Model": {
+              "recordName": "VSYS",
+              "keywordName": "TM"
+            }
+          },
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Umts"
+          }
+        }
+      },
+      {
+        "inventoryPath": "/system/chassis",
+        "inherit": false,
+        "isSystemVpd": true,
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Chassis": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs"
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/ebmc_card_bmc",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Bmc": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C5"
+          }
+        }
+      },
+      {
+        "inventoryPath": "/system/chassis/motherboard/ebmc_card_bmc/ethernet0",
+        "inherit": false,
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Ethernet": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C5-T0"
+          },
+          "xyz.openbmc_project.Inventory.Item.NetworkInterface": {
+            "MACAddress": {
+              "recordName": "VCFG",
+              "keywordName": "Z0",
+              "encoding": "MAC"
+            }
+          }
+        }
+      },
+      {
+        "inventoryPath": "/system/chassis/motherboard/ebmc_card_bmc/ethernet1",
+        "inherit": false,
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Ethernet": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C5-T1"
+          },
+          "xyz.openbmc_project.Inventory.Item.NetworkInterface": {
+            "MACAddress": {
+              "recordName": "VCFG",
+              "keywordName": "Z1",
+              "encoding": "MAC"
+            }
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a080.i2c-bus/i2c-0/0-0051/0-00510/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/tpm_wilson",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Tpm": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C22"
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a400.i2c-bus/i2c-7/7-0050/7-00500/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/base_op_panel_blyth",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Panel": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-D0"
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a400.i2c-bus/i2c-7/7-0051/7-00510/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/lcd_op_panel_hill",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Panel": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-D1"
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a500.i2c-bus/i2c-9/9-0050/9-00500/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/vdd_vrm0",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Vrm": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C14"
+          }
+        }
+      }
+    ],
+    "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a580.i2c-bus/i2c-10/10-0050/10-00500/nvmem": [
+      {
+        "inventoryPath": "/system/chassis/motherboard/vdd_vrm1",
+        "extraInterfaces": {
+          "xyz.openbmc_project.Inventory.Item.Vrm": null,
+          "com.ibm.ipzvpd.Location" : {
+              "LocationCode" : "Ufcs-P0-C23"
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index d69d7fa..b9104f0 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -61,7 +61,6 @@
                                        sizeof(ECCOffset) + sizeof(ECCLength));
         }
     }
-
     // imples the record was not found
     throw std::runtime_error("Record not found");
 }
@@ -82,6 +81,19 @@
     std::advance(iteratorToKWdData, thisRecord.kwDataOffset);
     std::copy(iteratorToNewdata, end, iteratorToKWdData);
 
+#ifdef ManagerTest
+    auto startItr = vpdFile.begin();
+    std::advance(iteratorToKWdData, thisRecord.kwDataOffset);
+    auto endItr = startItr;
+    std::advance(endItr, thisRecord.kwdDataLength);
+
+    Binary updatedData(startItr, endItr);
+    if (updatedData == kwdData)
+    {
+        throw std::runtime_error("Data updated successfully");
+    }
+#else
+
     // update data in EEPROM as well. As we will not write complete file back
     vpdFileStream.seekg(thisRecord.kwDataOffset, std::ios::beg);
     iteratorToNewdata = kwdData.cbegin();
@@ -95,6 +107,7 @@
     auto kwdDataEnd = itrToKWdData;
     std::advance(kwdDataEnd, thisRecord.kwdDataLength);
     std::copy(itrToKWdData, kwdDataEnd, thisRecord.kwdUpdatedData.begin());
+#endif
 }
 
 void EditorImpl::checkRecordForKwd()
@@ -172,9 +185,11 @@
     auto end = itrToRecordECC;
     std::advance(end, thisRecord.recECCLength);
 
+#ifndef ManagerTest
     vpdFileStream.seekp(thisRecord.recECCoffset, std::ios::beg);
     std::copy(itrToRecordECC, end,
               std::ostreambuf_iterator<char>(vpdFileStream));
+#endif
 }
 
 auto EditorImpl::getValue(offsets::Offsets offset)
@@ -433,8 +448,11 @@
 
 void EditorImpl::updateKeyword(const Binary& kwdData)
 {
+
+#ifndef ManagerTest
     vpdFileStream.open(vpdFilePath,
                        std::ios::in | std::ios::out | std::ios::binary);
+
     if (!vpdFileStream)
     {
         throw std::runtime_error("unable to open vpd file to edit");
@@ -443,6 +461,13 @@
     Binary completeVPDFile((std::istreambuf_iterator<char>(vpdFileStream)),
                            std::istreambuf_iterator<char>());
     vpdFile = completeVPDFile;
+#else
+    Binary completeVPDFile = vpdFile;
+#endif
+    if (vpdFile.empty())
+    {
+        throw std::runtime_error("Invalid File");
+    }
 
     auto iterator = vpdFile.cbegin();
     std::advance(iterator, IPZ_DATA_START);
@@ -464,14 +489,12 @@
 
         // update the ECC data for the record once data has been updated
         updateRecordECC();
-
+#ifndef ManagerTest
         // update the cache once data has been updated
         updateCache();
-
+#endif
         return;
     }
-
-    throw std::runtime_error("Invalid VPD file type");
 }
 
 } // namespace editor
diff --git a/vpd-manager/editor_impl.hpp b/vpd-manager/editor_impl.hpp
index ac8909c..b3d24f7 100644
--- a/vpd-manager/editor_impl.hpp
+++ b/vpd-manager/editor_impl.hpp
@@ -53,6 +53,17 @@
      *
      *  @param[in] path - Path to the vpd file
      */
+    EditorImpl(const std::string& record, const std::string& kwd,
+               Binary&& vpd) :
+        thisRecord(record, kwd),
+        vpdFile(std::move(vpd))
+    {
+    }
+
+    /** @brief Construct EditorImpl class
+     *
+     *  @param[in] path - Path to the vpd file
+     */
     EditorImpl(const inventory::Path& path, const nlohmann::json& json,
                const std::string& record, const std::string& kwd) :
         vpdFilePath(path),
@@ -140,20 +151,20 @@
                       const std::string& property, const std::variant<T>& data);
 
     // path to the VPD file to edit
-    const inventory::Path& vpdFilePath;
+    const inventory::Path vpdFilePath;
 
     // stream to perform operation on file
     std::fstream vpdFileStream;
 
     // file to store parsed json
-    const nlohmann::json& jsonFile;
+    const nlohmann::json jsonFile;
 
     // structure to hold info about record to edit
     struct RecInfo
     {
         Binary kwdUpdatedData; // need access to it in case encoding is needed
-        const std::string& recName;
-        const std::string& recKWd;
+        const std::string recName;
+        const std::string recKWd;
         openpower::vpd::constants::RecordOffset recOffset;
         openpower::vpd::constants::ECCOffset recECCoffset;
         std::size_t recECCLength;