Write bifurcation data to the given output path.

This review contains below changes-
1. Get the bifurcation data from already parsed string.
2. Write the bifurcation data for each CPU in the agreed format.
3. If reading eeprom data fails due to any reason. Then use max_lines
   value of that slot if available.
4. Include new line at end of each file.
5. Nit comments from previous review.
6. Unit tests.

Testing: All unit tests are successful. Able to verify that a
bifurcation file is created on an izumi machine with the change.

Change-Id: I458c286e989fe949c5fd8bab528324458409293a
Signed-off-by: Vamsy Nooney <vamsyknooney@gmail.com>
diff --git a/.clang-format b/.clang-format
index 625a0a0..32f3e04 100644
--- a/.clang-format
+++ b/.clang-format
@@ -76,6 +76,7 @@
 IndentCaseLabels: true
 IndentWidth:     4
 IndentWrappedFunctionNames: true
+InsertNewlineAtEOF: true
 KeepEmptyLinesAtTheStartOfBlocks: true
 MacroBlockBegin: ''
 MacroBlockEnd:   ''
diff --git a/eeprom_path.cpp b/eeprom_path.cpp
index 0308df9..c769bee 100644
--- a/eeprom_path.cpp
+++ b/eeprom_path.cpp
@@ -12,4 +12,4 @@
                 << std::setfill('0') << std::setw(4) << std::hex << addr
                 << "/eeprom";
     return eeprom_path.str();
-}
\ No newline at end of file
+}
diff --git a/eeprom_path.hpp b/eeprom_path.hpp
index c6c27da..7cb9a4b 100644
--- a/eeprom_path.hpp
+++ b/eeprom_path.hpp
@@ -6,4 +6,4 @@
 /*
 Constructs the path of eeprom file using the bus and address values provided.
 This function doesn't check the presence of the eeprom file.*/
-std::string getEepromPath(uint32_t bus, uint32_t addr);
\ No newline at end of file
+std::string getEepromPath(uint32_t bus, uint32_t addr);
diff --git a/fru_utils.cpp b/fru_utils.cpp
index 2b684c7..d84d199 100644
--- a/fru_utils.cpp
+++ b/fru_utils.cpp
@@ -709,4 +709,4 @@
     }
 
     return device;
-}
\ No newline at end of file
+}
diff --git a/meson_options.txt b/meson_options.txt
index 1c5be6e..0fc2767 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1 +1 @@
-option('tests', type: 'feature', description: 'Build tests')
\ No newline at end of file
+option('tests', type: 'feature', description: 'Build tests')
diff --git a/pcie_bifurcation.cpp b/pcie_bifurcation.cpp
index 834bf51..a54eaf5 100644
--- a/pcie_bifurcation.cpp
+++ b/pcie_bifurcation.cpp
@@ -23,38 +23,6 @@
     pcieConfigFilePath(pcieConfigFilePath)
 {}
 
-std::optional<std::vector<std::pair<int, int>>>
-    PCIeBifurcation::getSMBusAddrPairsFromSlotData(
-        const nlohmann::json& slotData)
-{
-    std::vector<std::pair<int, int>> smbusAddrPairs;
-    if (slotData.is_null() || !slotData.is_array() || slotData.size() == 0)
-    {
-        std::cerr << "SlotData is eithor empty or not in expected "
-                     "format\n";
-        return std::nullopt;
-    }
-
-    for (auto it = slotData.begin(); it != slotData.end(); it++)
-    {
-        auto smbus = it->find("smbus");
-        auto addr = it->find("addr");
-        if (smbus != it->end() && smbus->is_number() && addr != it->end() &&
-            addr->is_number())
-        {
-            smbusAddrPairs.push_back({*smbus, *addr});
-        }
-        else
-        {
-            std::cerr << "Invalid bus or addr info in slotData: " << slotData
-                      << std::endl;
-            smbusAddrPairs.clear();
-            return std::nullopt;
-        }
-    }
-    return smbusAddrPairs;
-}
-
 static int64_t readFromEeprom(int fd, off_t offset, size_t len, uint8_t* buf)
 {
     auto result = lseek(fd, offset, SEEK_SET);
@@ -86,8 +54,9 @@
 
 // Reads the eeprom for bus at given address and populates PCIe data for the
 // corresponding slot in the map.
-bool PCIeBifurcation::populatePCIeSlotDataMap(
-    uint32_t bus, uint32_t addr, std::map<uint8_t, std::string>& slotDataMap)
+std::optional<std::string>
+    PCIeBifurcation::getPCIeBifurcationDataFromEeprom(uint32_t bus,
+                                                      uint32_t addr)
 {
     std::cout << "Reading eeprom of " << bus << " and with address " << addr
               << std::endl;
@@ -96,87 +65,182 @@
     if (fd <= 0)
     {
         std::cerr << "Unable to open the eeprom file: " << path << std::endl;
-        return false;
+        return std::nullopt;
     }
     std::string errorMessage =
         "eeprom at " + std::to_string(bus) + " address " + std::to_string(addr);
     auto readFunc = [fd](off_t offset, size_t length, uint8_t* outbuf) {
         return readFromEeprom(fd, offset, length, outbuf);
     };
-    bool returnStatus = true;
+
     std::optional<std::string> pcieData;
     FRUReader reader(std::move(readFunc));
     std::vector<uint8_t> contents = readFRUContents(reader, errorMessage);
+    close(fd);
     std::map<std::string, std::string> result;
     resCodes res = formatIPMIFRU(contents, result);
     if (res == resCodes::resErr)
     {
         std::cerr << "failed to parse FRU for device at bus " << bus
                   << " address " << addr << "\n";
-        returnStatus = false;
-        goto END;
+        return std::nullopt;
     }
-    // Gets the PCIeBifurcation value.
-    pcieData = getPcieBifurcationDataStr(result);
-    if (pcieData == std::nullopt || pcieData->empty())
+    // Get the PCIeBifurcation value and return it.
+    return getPcieBifurcationDataStr(result);
+}
+
+std::optional<std::pair<uint32_t, uint32_t>>
+    PCIeBifurcation::getSMBusAddrPair(const nlohmann::json& slotData)
+{
+    if (slotData.is_null())
     {
-        std::cerr << "PCIe-bifurcation data not available.\n";
-        returnStatus = false;
-        goto END;
+        return std::nullopt;
     }
-    slotDataMap[bus] = *pcieData;
-    if (res == resCodes::resWarn)
+    auto smbus = slotData.find("smbus");
+    auto addr = slotData.find("addr");
+    if (smbus == slotData.end() || !smbus->is_number() ||
+        addr == slotData.end() || !addr->is_number())
     {
-        std::cerr << "there were warnings while parsing FRU for device at bus "
-                  << bus << " address " << addr << "\n";
+        std::cerr << "Invalid smbus/addr params in slotData\n";
+        return std::nullopt;
     }
-END:
-    close(fd);
-    return returnStatus;
+    return std::make_pair(*smbus, *addr);
+}
+
+bool PCIeBifurcation::populatePCIeSlotDataMap(
+    const nlohmann::json& slotData, std::map<uint8_t, std::string>& slotDataMap)
+{
+    std::optional<std::pair<uint32_t, uint32_t>> smbusAddrPair =
+        getSMBusAddrPair(slotData);
+    if (smbusAddrPair == std::nullopt)
+    {
+        std::cerr << "smbusAddrPair not valid\n";
+        return false;
+    }
+    auto slot = slotData.find("slot");
+    if (slot == slotData.end() || !slot->is_number())
+    {
+        std::cerr << "Slot is not valid\n";
+        return false;
+    }
+    std::optional<std::string> pcieBifurcationDataStr =
+        getPCIeBifurcationDataFromEeprom(smbusAddrPair->first,
+                                         smbusAddrPair->second);
+    if (pcieBifurcationDataStr != std::nullopt &&
+        !pcieBifurcationDataStr->empty())
+    {
+        slotDataMap[*slot] = *pcieBifurcationDataStr;
+        return true;
+    }
+
+    // If reading pcie bifurcation data from eeprom failed. Set bifurcation data
+    // to "max_lines". That is, if max_lines is set to 16. Then, pcie
+    // bifurcation string will become x16.
+    if (slotData.find("max_lines") == slotData.end() ||
+        !slotData.find("max_lines")->is_number())
+    {
+        std::cerr << "Invalid maxLines\n";
+        return false;
+    }
+    uint32_t maxLines = slotData["max_lines"];
+    slotDataMap[*slot] = "x" + std::to_string(maxLines);
+    return true;
+}
+
+// Get the PCIe bifurcation data in the format of "x8x8" in string format and
+// populates bifurcation data from it.
+std::vector<uint8_t>
+    PCIeBifurcation::getBifurcationData(const std::string& bifurcation_data)
+{
+    uint8_t num = 0;
+    std::vector<uint8_t> data;
+    for (size_t i = 0; i < bifurcation_data.size(); i++)
+    {
+        if (isdigit(bifurcation_data[i]))
+        {
+            num *= 10;
+            num += bifurcation_data[i] - '0';
+        }
+        else if (num != 0)
+        {
+            data.push_back(num);
+            num = 0;
+        }
+    }
+    if (num != 0)
+    {
+        data.push_back(num);
+        num = 0;
+    }
+    return data;
+}
+
+bool PCIeBifurcation::writeBifurcationDataToFile(
+    const std::string& outputPath,
+    std::vector<uint8_t>& aggregatedBifurcationData)
+{
+    std::remove(outputPath.c_str()); // Delete the file if already exists.
+    std::ofstream outputFile(outputPath, std::ios::binary);
+    if (outputFile.fail())
+    {
+        std::cerr << "Creating file(" << outputPath << ") failed" << std::endl;
+        return false;
+    }
+    outputFile.write(reinterpret_cast<char*>(&aggregatedBifurcationData[0]),
+                     sizeof(uint8_t) * aggregatedBifurcationData.size());
+    outputFile.close();
+    return true;
 }
 
 bool PCIeBifurcation::populateCPUPCIeData(const nlohmann::json& cpuPCIeData)
 {
-    auto slotData = cpuPCIeData.find("slot_data");
-    if (slotData == cpuPCIeData.end())
+    if (cpuPCIeData.find("slot_data") == cpuPCIeData.end())
     {
         std::cerr << "slot_data not found\n";
         return false;
     }
-    auto outputPath = cpuPCIeData.find("output_path");
-    if (outputPath == cpuPCIeData.end())
+
+    if (!cpuPCIeData.find("slot_data")->is_array())
+    {
+        std::cerr << "slot_data is not array\n";
+        return false;
+    }
+
+    if (cpuPCIeData.find("output_path") == cpuPCIeData.end())
     {
         std::cerr << "output_path not found\n";
         return false;
     }
 
-    if (!outputPath->is_string())
+    if (!cpuPCIeData.find("output_path")->is_string())
     {
         std::cerr << "output_path is not string\n";
         return false;
     }
 
-    nlohmann::json slotDataValue = *slotData;
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        getSMBusAddrPairsFromSlotData(slotDataValue);
-    if (smbusAddrPairs == std::nullopt)
-    {
-        std::cerr << "getSMBusAddrPairsFromSlotData failed\n";
-        return false;
-    }
     std::map<uint8_t, std::string> slotDataMap;
-    for (const auto& smbusAddrPair : smbusAddrPairs.value())
+    for (const nlohmann::json& slotData : cpuPCIeData["slot_data"])
     {
-        if (!populatePCIeSlotDataMap(smbusAddrPair.first, smbusAddrPair.second,
-                                     slotDataMap))
+        if (!populatePCIeSlotDataMap(slotData, slotDataMap))
         {
+            std::cerr << "Populating PCIe SlotData Map failed\n";
             return false;
         }
     }
-    // TODO:Now, convert the bifurcationDataStrList to agreed format in
-    // go/pcie-bcg and write it to output_path to be available for any client to
-    // read.
-    return true;
+    std::vector<uint8_t> aggregatedBifurcationData;
+    aggregatedBifurcationData.push_back(slotDataMap.size());
+    for (const auto& slotData : slotDataMap)
+    {
+        std::vector<uint8_t> bifurcationData =
+            getBifurcationData(slotData.second);
+        aggregatedBifurcationData.push_back(bifurcationData.size());
+        for (uint8_t data : bifurcationData)
+        {
+            aggregatedBifurcationData.push_back(data);
+        }
+    }
+    std::string outputPath = cpuPCIeData["output_path"];
+    return writeBifurcationDataToFile(outputPath, aggregatedBifurcationData);
 }
 
 // Refer to pcie_bifurcation.hpp file for sample configuration
@@ -203,15 +267,19 @@
         return false;
     }
 
+    bool returnStatus = true;
     for (nlohmann::json::iterator it = jsonData.begin(); it != jsonData.end();
          it++)
     {
         if (!populateCPUPCIeData(*it))
         {
-            return false;
+            std::cerr << "populating PCIe data of a CPU failed.\n";
+            returnStatus = false;
         }
     }
-    return true;
+
+    // Return true only if writing PCIe data of all CPUs is successful.
+    return returnStatus;
 }
 } // namespace pcie_bifurcation
-} // namespace google
\ No newline at end of file
+} // namespace google
diff --git a/pcie_bifurcation.hpp b/pcie_bifurcation.hpp
index c253886..4618f5d 100644
--- a/pcie_bifurcation.hpp
+++ b/pcie_bifurcation.hpp
@@ -48,16 +48,24 @@
   public:
     PCIeBifurcation(std::string_view pcieConfigFilePath);
 
-    // TODO: Return absl::StatusOr<std::vector<std::pair<int, int>>> here for
-    // returning the status properly.
-    std::optional<std::vector<std::pair<int, int>>>
-        getSMBusAddrPairsFromSlotData(const nlohmann::json& slotData);
+    std::vector<uint8_t>
+        getBifurcationData(const std::string& bifurcation_data);
+
+    bool writeBifurcationDataToFile(
+        const std::string& outputPath,
+        std::vector<uint8_t>& aggregatedBifurcationData);
 
     std::optional<std::string>
         getPcieBifurcationDataStr(std::map<std::string, std::string>& fru_map);
 
-    bool populatePCIeSlotDataMap(uint32_t bus, uint32_t addr,
-                                 std::map<uint8_t, std::string>& slot_data_map);
+    std::optional<std::string> getPCIeBifurcationDataFromEeprom(uint32_t bus,
+                                                                uint32_t addr);
+
+    std::optional<std::pair<uint32_t, uint32_t>>
+        getSMBusAddrPair(const nlohmann::json& slotData);
+
+    bool populatePCIeSlotDataMap(const nlohmann::json& slotData,
+                                 std::map<uint8_t, std::string>& slotDataMap);
 
     bool populateCPUPCIeData(const nlohmann::json& cpuPCIeData);
 
@@ -67,4 +75,4 @@
     std::string_view pcieConfigFilePath;
 };
 } // namespace pcie_bifurcation
-} // namespace google
\ No newline at end of file
+} // namespace google
diff --git a/test/eeprom_path_test.cpp b/test/eeprom_path_test.cpp
index de7a06b..37cf2a2 100644
--- a/test/eeprom_path_test.cpp
+++ b/test/eeprom_path_test.cpp
@@ -13,4 +13,4 @@
     eeprom_path << "/tmp/" << bus << "-" << std::right << std::setfill('0')
                 << std::setw(4) << std::hex << addr;
     return eeprom_path.str();
-}
\ No newline at end of file
+}
diff --git a/test/fru_reader_unittest.cpp b/test/fru_reader_unittest.cpp
index 02baee5..b54f3fc 100644
--- a/test/fru_reader_unittest.cpp
+++ b/test/fru_reader_unittest.cpp
@@ -166,4 +166,4 @@
     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
     data.resize(blockSize);
     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
-}
\ No newline at end of file
+}
diff --git a/test/meson.build b/test/meson.build
index e65540b..8977a8c 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -11,7 +11,7 @@
   gtest,
   gmock,
   pcie_bifurcation_deps
-  ]
+]
 
 foreach t : tests
   test(t, executable(t.underscorify(),
diff --git a/test/pcie_bifurcation_unittest.cpp b/test/pcie_bifurcation_unittest.cpp
index df9439e..92bc024 100644
--- a/test/pcie_bifurcation_unittest.cpp
+++ b/test/pcie_bifurcation_unittest.cpp
@@ -15,111 +15,77 @@
 
 using namespace ::testing;
 
-TEST(getSMBusAddrPairsFromSlotData, success)
+TEST(getSMBusAddrPair, validSlotData)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse(
-        "[{\"slot\": 0,\"smbus\": 2,\"addr\": 82},{\"slot\": "
-        "1,\"smbus\": 4,\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    std::vector<std::pair<int, int>> expectedSmbusAddrPairs = {
-        std::make_pair<int, int>(2, 82), std::make_pair<int, int>(4, 82)};
-    ASSERT_TRUE(smbusAddrPairs);
-    EXPECT_EQ(expectedSmbusAddrPairs, smbusAddrPairs.value());
+    nlohmann::json slotData = nlohmann::json::parse(R"(
+        {
+                    "addr": 82,
+                    "slot": 0,
+                    "smbus": 3
+        }
+    )");
+    std::optional<std::pair<uint32_t, uint32_t>> expectedSMBusAddrPair =
+        std::make_pair<uint32_t, uint32_t>(3, 82);
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData),
+              expectedSMBusAddrPair);
 }
 
-TEST(getSMBusAddrPairsFromSlotData, nullJSON)
+TEST(getSMBusAddrPair, smbusNotFound)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData;
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
+    nlohmann::json slotData = nlohmann::json::parse(R"(
+        {
+                    "addr": 82,
+                    "slot": 0
+        }
+    )");
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData), std::nullopt);
 }
 
-TEST(getSMBusAddrPairsFromSlotData, emptySlotData)
+TEST(getSMBusAddrPair, smbusNotNumber)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse("[]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
-}
-TEST(getSMBusAddrPairsFromSlotData, slotDataNotArray)
-{
-    PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData =
-        nlohmann::json::parse("{\"slot\": 0,\"smbus\": 2,\"addr\": 82}");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
+    nlohmann::json slotData = nlohmann::json::parse(R"(
+        {
+                    "addr": 82,
+                    "slot": 0,
+                    "smbus": "3"
+        }
+    )");
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData), std::nullopt);
 }
 
-TEST(getSMBusAddrPairsFromSlotData, smbusNotFound)
+TEST(getSMBusAddrPair, addrNotFound)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData =
-        nlohmann::json::parse("[{\"slot\": 0,\"addr\": 82},{\"slot\": "
-                              "1,\"smbus\": 4,\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
+    nlohmann::json slotData = nlohmann::json::parse(R"(
+        {
+                    "slot": 0,
+                    "smbus": 3
+        }
+    )");
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData), std::nullopt);
 }
 
-TEST(getSMBusAddrPairsFromSlotData, smbusNotNumber)
+TEST(getSMBusAddrPair, addrNotNumber)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse(
-        "[{\"slot\": 0,\"smbus\": \"2\",\"addr\": 82},{\"slot\": "
-        "1,\"smbus\": 4,\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
+    nlohmann::json slotData = nlohmann::json::parse(R"(
+        {
+                    "addr": "82",
+                    "slot": 0,
+                    "smbus": 3
+        }
+    )");
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData), std::nullopt);
 }
 
-TEST(getSMBusAddrPairsFromSlotData, secondSMBusNotNumber)
+TEST(getSMBusAddrPair, slotDataNull)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse(
-        "[{\"slot\": 0,\"smbus\": 2,\"addr\": 82},{\"slot\": "
-        "1,\"smbus\": \"4\",\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
-}
-
-TEST(getSMBusAddrPairsFromSlotData, addrNotFound)
-{
-    PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData =
-        nlohmann::json::parse("[{\"slot\": 0,\"smbus\": 2},{\"slot\": "
-                              "1,\"smbus\": 4,\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
-}
-
-TEST(getSMBusAddrPairsFromSlotData, addrNotNumber)
-{
-    PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse(
-        "[{\"slot\": 0,\"smbus\": 2,\"addr\": \"82\"},{\"slot\": "
-        "1,\"smbus\": 4,\"addr\": 82}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
-}
-
-TEST(getSMBusAddrPairsFromSlotData, secondAddrNotNumber)
-{
-    PCIeBifurcation pcieBifurcation("dummypath");
-    nlohmann::json jsonData = nlohmann::json::parse(
-        "[{\"slot\": 0,\"smbus\": 2,\"addr\": 82},{\"slot\": "
-        "1,\"smbus\": 4,\"addr\": \"82\"}]");
-    std::optional<std::vector<std::pair<int, int>>> smbusAddrPairs =
-        pcieBifurcation.getSMBusAddrPairsFromSlotData(jsonData);
-    EXPECT_EQ(smbusAddrPairs, std::nullopt);
+    nlohmann::json slotData;
+    EXPECT_EQ(pcieBifurcation.getSMBusAddrPair(slotData), std::nullopt);
 }
 
 uint8_t
@@ -233,23 +199,59 @@
     empty_eeprom_file.close();
 }
 
+bool matchFileContents(std::string originalContentFile,
+                       std::string expectedContentFile)
+{
+    if (std::filesystem::file_size(originalContentFile) !=
+        std::filesystem::file_size(expectedContentFile))
+    {
+        return false;
+    }
+    std::ifstream file1(originalContentFile);
+    std::ifstream file2(expectedContentFile);
+    char byte1, byte2;
+    while (file1.get(byte1) && file2.get(byte2))
+    {
+        if (byte1 != byte2)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+void makeBinaryFile(std::string fileName, std::vector<uint8_t> content)
+{
+    std::ofstream file(fileName, std::ios::binary);
+    file.write(reinterpret_cast<char*>(&content[0]), content.size());
+    file.close();
+}
+
+bool checkIfFileExists(std::string fileName)
+{
+    std::ifstream file(fileName);
+    return file.good();
+}
+
 TEST(populateCPUPCIeData, validCPUPCIeData)
 {
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.bin",
+            "slot_data":
             [
                 {
                     "addr": 82,
                     "slot": 0,
-                    "smbus": 3
+                    "smbus": 3,
+                    "max_lines": 16
                 },
                 {
                     "addr": 82,
                     "slot": 1,
-                    "smbus": 4
+                    "smbus": 4,
+                    "max_lines": 8
                 }
             ]
         })");
@@ -258,7 +260,75 @@
     EXPECT_TRUE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
     std::remove(getEepromPath(3, 0x52).c_str());
     std::remove(getEepromPath(4, 0x52).c_str());
-    // TODO: Verify that bifurcation data is written to output_path
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/bifurcation_data.bin");
+    std::remove("/tmp/expectedContentFile.bin");
+}
+
+TEST(populateCPUPCIeData, firstSlotNotPresentMaxLinesAvailable)
+{
+    PCIeBifurcation pcieBifurcation("dummypath");
+    nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
+        {
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
+            [
+                {
+                    "addr": 82,
+                    "slot": 0,
+                    "smbus": 3,
+                    "max_lines": 16
+                },
+                {
+                    "addr": 82,
+                    "slot": 1,
+                    "smbus": 4,
+                    "max_lines": 8
+                }
+            ]
+        })");
+    createEepromFile(4, 0x52, {8, 8});
+    EXPECT_TRUE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
+    std::remove(getEepromPath(4, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 1, 16, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/bifurcation_data.txt",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/bifurcation_data.txt");
+    std::remove("/tmp/expectedContentFile.bin");
+}
+
+TEST(populateCPUPCIeData, secondSlotNotPresentMaxLinesAvailable)
+{
+    PCIeBifurcation pcieBifurcation("dummypath");
+    nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
+        {
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
+            [
+                {
+                    "addr": 82,
+                    "slot": 0,
+                    "smbus": 3,
+                    "max_lines": 16
+                },
+                {
+                    "addr": 82,
+                    "slot": 1,
+                    "smbus": 4,
+                    "max_lines": 8
+                }
+            ]
+        })");
+    createEepromFile(3, 0x52, {4, 4, 8});
+    EXPECT_TRUE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
+    std::remove(getEepromPath(3, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 1, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/bifurcation_data.txt",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/bifurcation_data.txt");
+    std::remove("/tmp/expectedContentFile.bin");
 }
 
 TEST(populateCPUPCIeData, undefinedSlotInfo)
@@ -266,8 +336,8 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
             [
                 {
                     "addr": 82,
@@ -282,7 +352,7 @@
             ]
         })");
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
-    // TODO: Verify that no output is written to output_path
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, firstSlotNotPresent)
@@ -290,8 +360,8 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
             [
                 {
                     "addr": 82,
@@ -308,7 +378,7 @@
     createEepromFile(4, 0x52, {8, 8});
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
     std::remove(getEepromPath(4, 0x52).c_str());
-    // TODO: Verify that no output is written to even 2nd slot output_path
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, secondSlotNotPresent)
@@ -316,8 +386,8 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
             [
                 {
                     "addr": 82,
@@ -334,7 +404,7 @@
     createEepromFile(3, 0x52, {4, 4, 8});
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
     std::remove(getEepromPath(3, 0x52).c_str());
-    // TODO: Verify that no output is written to even 1st slot output_path
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, noPCIeBifurcationData)
@@ -342,8 +412,8 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
             [
                 {
                     "addr": 82,
@@ -360,7 +430,7 @@
     createEepromFile(3, 0x52, {});
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
     std::remove(getEepromPath(3, 0x52).c_str());
-    // TODO: Verify that no output is written to even 1st slot output_path
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, emptyEepromFile)
@@ -368,8 +438,8 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
-            "output_path": "path",
-            "slot_data": 
+            "output_path": "/tmp/bifurcation_data.txt",
+            "slot_data":
             [
                 {
                     "addr": 82,
@@ -386,7 +456,7 @@
     createEmptyEepromFile(3, 0x52);
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
     std::remove(getEepromPath(3, 0x52).c_str());
-    // TODO: Verify that no output is written
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, slotDataNotFound)
@@ -394,9 +464,10 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
             {
-                "output_path": "path"
+                "output_path": "/tmp/bifurcation_data.txt"
             })");
     EXPECT_FALSE(pcieBifurcation.populateCPUPCIeData(cpuPCIeData));
+    ASSERT_FALSE(checkIfFileExists("/tmp/bifurcation_data.txt"));
 }
 
 TEST(populateCPUPCIeData, outputPathNotFound)
@@ -426,6 +497,7 @@
     PCIeBifurcation pcieBifurcation("dummypath");
     nlohmann::json cpuPCIeData = nlohmann::json::parse(R"(
         {
+            "output_path": 123,
             "slot_data":
             [
                 {
@@ -475,22 +547,22 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu1_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
                 },
                 {
                     "slot_data": [
                         {
-                            "slot": 0,
+                            "slot": 2,
                             "smbus": 5,
                             "addr": 82
                         },
                         {
-                            "slot": 1,
+                            "slot": 3,
                             "smbus": 6,
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu2_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu2_bifurcation_data.bin"
                 }
             ])");
     createEepromFile(3, 0x52, {4, 4, 8});
@@ -504,6 +576,16 @@
     std::remove(getEepromPath(4, 0x52).c_str());
     std::remove(getEepromPath(5, 0x52).c_str());
     std::remove(getEepromPath(6, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu1_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 1, 16, 3, 8, 4, 4});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu2_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    std::remove("/tmp/cpu1_bifurcation_data.bin");
+    std::remove("/tmp/cpu2_bifurcation_data.bin");
 }
 
 TEST(populatePCIeBifurcationData, validConfData_1CPU)
@@ -525,7 +607,7 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu1_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
                 }
             ])");
     createEepromFile(3, 0x52, {4, 4, 8});
@@ -535,6 +617,11 @@
     std::remove(filePath.c_str());
     std::remove(getEepromPath(3, 0x52).c_str());
     std::remove(getEepromPath(4, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu1_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    std::remove("/tmp/cpu1_bifurcation_data.bin");
 }
 
 TEST(populatePCIeBifurcationData, invalidConfDataCPU1)
@@ -556,22 +643,22 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu1_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
                 },
                 {
                     "slot_data": [
                         {
-                            "slot": 0,
+                            "slot": 2,
                             "smbus": 5,
                             "addr": 82
                         },
                         {
-                            "slot": 1,
+                            "slot": 3,
                             "smbus": 6,
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu2_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu2_bifurcation_data.bin"
                 }
             ])");
     createEepromFile(5, 0x52, {16});
@@ -581,6 +668,12 @@
     std::remove(filePath.c_str());
     std::remove(getEepromPath(5, 0x52).c_str());
     std::remove(getEepromPath(6, 0x52).c_str());
+    ASSERT_FALSE(checkIfFileExists("/tmp/cpu1_bifurcation_data.bin"));
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 1, 16, 3, 8, 4, 4});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu2_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    std::remove("/tmp/cpu2_bifurcation_data.bin");
 }
 
 TEST(populatePCIeBifurcationData, invalidConfDataCPU2)
@@ -602,22 +695,22 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu1_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
                 },
                 {
                     "slot_data": [
                         {
-                            "slot": 0,
+                            "slot": 2,
                             "smbus": 5,
                             "addr": 82
                         },
                         {
-                            "slot": 1,
+                            "slot": 3,
                             "smbus": 6,
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu2_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu2_bifurcation_data.bin"
                 }
             ])");
     createEepromFile(3, 0x52, {4, 4, 8});
@@ -627,6 +720,72 @@
     std::remove(filePath.c_str());
     std::remove(getEepromPath(3, 0x52).c_str());
     std::remove(getEepromPath(4, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu1_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    std::remove("/tmp/cpu1_bifurcation_data.bin");
+    ASSERT_FALSE(checkIfFileExists("/tmp/cpu2_bifurcation_data.bin"));
+}
+
+TEST(populatePCIeBifurcationData, eepromNotFoundMaxLinesExists)
+{
+    std::string filePath = "/tmp/valid_conf_data.json";
+    createTestConfFile(filePath, R"(
+            [
+                {
+                    "slot_data":
+                    [
+                        {
+                            "slot": 0,
+                            "smbus": 3,
+                            "addr": 82,
+                            "max_lines": 16
+                        },
+                        {
+                            "slot": 1,
+                            "smbus": 4,
+                            "addr": 82,
+                            "max_lines": 8
+                        }
+                    ],
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
+                },
+                {
+                    "slot_data": [
+                        {
+                            "slot": 2,
+                            "smbus": 5,
+                            "addr": 82,
+                            "max_lines": 8
+                        },
+                        {
+                            "slot": 3,
+                            "smbus": 6,
+                            "addr": 82,
+                            "max_lines": 16
+                        }
+                    ],
+                    "output_path": "/tmp/cpu2_bifurcation_data.bin"
+                }
+            ])");
+    createEepromFile(3, 0x52, {4, 4, 8});
+    createEepromFile(4, 0x52, {8, 8});
+    PCIeBifurcation pcieBifurcation(filePath);
+    EXPECT_TRUE(pcieBifurcation.populatePCIeBifurcationData());
+    std::remove(filePath.c_str());
+    std::remove(getEepromPath(3, 0x52).c_str());
+    std::remove(getEepromPath(4, 0x52).c_str());
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 3, 4, 4, 8, 2, 8, 8});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu1_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    makeBinaryFile("/tmp/expectedContentFile.bin", {2, 1, 8, 1, 16});
+    ASSERT_TRUE(matchFileContents("/tmp/cpu2_bifurcation_data.bin",
+                                  "/tmp/expectedContentFile.bin"));
+    std::remove("/tmp/expectedContentFile.bin");
+    std::remove("/tmp/cpu1_bifurcation_data.bin");
+    std::remove("/tmp/cpu2_bifurcation_data.bin");
 }
 
 TEST(populatePCIeBifurcationData, emptyConfFile)
@@ -650,10 +809,23 @@
 TEST(populatePCIeBifurcationData, jsonDataNotArray)
 {
     std::string filePath = "/tmp/json_data_not_array.json";
-    createTestConfFile(
-        filePath,
-        "{\"slot_data\": [{\"slot\": 0,\"smbus\": 2,\"addr\": 82},{\"slot\": "
-        "1,\"smbus\": 4,\"addr\": 82}]}");
+    createTestConfFile(filePath, R"(
+            {
+                "slot_data":
+                [
+                    {
+                        "slot": 0,
+                        "smbus": 2,
+                        "addr": 82
+                    },
+                    {
+                        "slot": 1,
+                        "smbus": 4,
+                        "addr": 82
+                    }
+                ]
+            }
+        )");
     PCIeBifurcation pcieBifurcation(filePath);
     EXPECT_FALSE(pcieBifurcation.populatePCIeBifurcationData());
     std::remove(filePath.c_str());
@@ -678,7 +850,7 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu1_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu1_bifurcation_data.bin"
                 },
                 {
                     "slot_data": [
@@ -693,18 +865,12 @@
                             "addr": 82
                         }
                     ],
-                    "output_path": "/tmp/cpu2_bifurcation_info.txt"
+                    "output_path": "/tmp/cpu2_bifurcation_data.bin"
                 }
             ])");
-    createTestConfFile(filePath,
-                       "[{\"slot_data\": [{\"slot\": 0,\"smbus\": 2,\"addr\": "
-                       "82},{\"slot\": 1,\"smbus\": 4,\"addr\": "
-                       "82}],\"output_path\": 123},{\"slot_data\": [{\"slot\": "
-                       "0,\"smbus\": 5,\"addr\": 82},{\"slot\": 1,\"smbus\": "
-                       "6,\"addr\": 82}],\"output_path\": \"path\"}]");
     PCIeBifurcation pcieBifurcation(filePath);
     EXPECT_FALSE(pcieBifurcation.populatePCIeBifurcationData());
     std::remove(filePath.c_str());
 }
 } // namespace pcie_bifurcation
-} // namespace google
\ No newline at end of file
+} // namespace google