| #include "host_bifurcation.hpp" |
| |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/bus.hpp> |
| #include <sdbusplus/exception.hpp> |
| |
| #include <fstream> |
| #include <memory> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| using namespace google::pcie_bifurcation; |
| using ::testing::_; |
| using ::testing::ElementsAre; |
| using ::testing::Return; |
| using ::testing::StrEq; |
| |
| class TestHostBifurcation : public HostBifurcation |
| { |
| public: |
| using HostBifurcation::aggregateEachSlot; |
| using HostBifurcation::HostBifurcation; |
| }; |
| |
| TEST(HostBifurcationTest, AggregateBifurcation) |
| { |
| // Setup for 1 slot with 2 buses (PEXA, PEXB) |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"( |
| [ |
| {"slot": 0, "bus": 10}, |
| {"slot": 0, "bus": 11} |
| ] |
| )"); |
| |
| // PEXA x16 + PEXB no cable -> x16 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x16"); |
| // Bus 11 not updated (no cable) |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(16))); |
| } |
| |
| // PEXA x8x8 + PEXB no cable -> x8x8 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x8x8"); |
| // Bus 11 not updated (no cable) |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA x8 + PEXB no cable -> x8x8 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA x4x4 + PEXB no cable -> x4x4x4x4 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x4x4"); |
| EXPECT_THAT(host.aggregateEachSlot(), |
| ElementsAre(ElementsAre(4, 4, 4, 4))); |
| } |
| |
| // PEXA no cable + PEXB x16 -> x16 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x16"); |
| // Bus 11 not updated (no cable) |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(16))); |
| } |
| |
| // PEXA no cable + PEXB x8x8 -> x8x8 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x8x8"); |
| // Bus 11 not updated (no cable) |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA no cable + PEXB x8 -> x8x8 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA no cable + PEXB x4x4 -> x4x4x4x4 (duplicated) |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x4x4"); |
| EXPECT_THAT(host.aggregateEachSlot(), |
| ElementsAre(ElementsAre(4, 4, 4, 4))); |
| } |
| |
| // PEXA x16 + PEXB x16 -> x16 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x16"); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x16"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(16))); |
| } |
| |
| // PEXA x8 + PEXB x8 -> x8x8 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x8"); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA x8x8 + PEXB x8 -> x8x8 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x8x8"); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // PEXA x8 + PEXB x8x8 -> x8x8 |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "PCIe-bifurcation:x8"); |
| host.updateValuesIfAvailable(11, 0, "PCIe-bifurcation:x8x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| |
| // Bad Amphenol FRU Image |
| { |
| TestHostBifurcation host(0, 1, cpuPCIeData, ""); |
| host.updateValuesIfAvailable(10, 0, "\001PCIe-bifurcation:x8"); |
| host.updateValuesIfAvailable(11, 0, "\001PCIe-bifurcation:x8"); |
| EXPECT_THAT(host.aggregateEachSlot(), ElementsAre(ElementsAre(8, 8))); |
| } |
| } |
| |
| TEST(HostBifurcationTest, ConstructorAndAggregate) |
| { |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"( |
| [ |
| { |
| "slot": 0, |
| "bus": 10, |
| "static_value": "x16" |
| }, |
| { |
| "slot": 1, |
| "bus": 11, |
| "static_value": "x8x8" |
| }, |
| { |
| "slot": 0, |
| "bus": 12, |
| "static_value": "x4x4x4x4" |
| } |
| ] |
| )"); |
| |
| try |
| { |
| HostBifurcation hostBifurcation(0, 2, cpuPCIeData, "test_output.bin"); |
| hostBifurcation.updateValuesIfAvailable(10, 0x50, "x16"); |
| hostBifurcation.updateValuesIfAvailable(11, 0x51, "x8x8"); |
| hostBifurcation.updateValuesIfAvailable(12, 0x52, "x4x4x4x4"); |
| SUCCEED(); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| SUCCEED() << "Caught expected sdbusplus exception: " << e.what(); |
| } |
| catch (const std::exception& e) |
| { |
| FAIL() << "Unexpected exception: " << e.what(); |
| } |
| |
| std::remove("test_output.bin"); |
| } |
| |
| TEST(HostBifurcationTest, WriteBifurcationDataToFile) |
| { |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"( |
| [ |
| { |
| "slot": 0, |
| "bus": 10 |
| } |
| ] |
| )"); |
| |
| try |
| { |
| HostBifurcation hostBifurcation(0, 1, cpuPCIeData, "test_output.bin"); |
| std::vector<std::vector<uint8_t>> bifurcationData = {{16}, {8}, {4, 4}}; |
| // hostBifurcation.writeBifurcationDataToFile(bifurcationData); // |
| // Private method |
| SUCCEED(); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| SUCCEED() << "Caught expected sdbusplus exception: " << e.what(); |
| } |
| catch (const std::exception& e) |
| { |
| FAIL() << "Unexpected exception: " << e.what(); |
| } |
| std::ifstream inputFile("test_output.bin", std::ios::binary); |
| EXPECT_FALSE(inputFile.is_open()); |
| std::remove("test_output.bin"); |
| } |
| |
| TEST(HostBifurcationTest, UpdateValuesIfAvailable) |
| { |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"( |
| [ |
| { |
| "slot": 0, |
| "bus": 10 |
| }, |
| { |
| "slot": 1, |
| "bus": 11 |
| } |
| ] |
| )"); |
| |
| try |
| { |
| HostBifurcation hostBifurcation(0, 2, cpuPCIeData, "test_output.bin"); |
| hostBifurcation.updateValuesIfAvailable(10, 0x50, "x8x8"); |
| hostBifurcation.updateValuesIfAvailable(12, 0x52, |
| "x16"); // Bus 12 not in config |
| SUCCEED(); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| SUCCEED() << "Caught expected sdbusplus exception: " << e.what(); |
| } |
| catch (const std::exception& e) |
| { |
| FAIL() << "Unexpected exception: " << e.what(); |
| } |
| std::remove("test_output.bin"); |
| } |
| |
| TEST(HostBifurcationTest, ConstructorSlotOutOfRange) |
| { |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"( |
| [ |
| { |
| "slot": 2, |
| "bus": 10 |
| } |
| ] |
| )"); |
| |
| EXPECT_THROW( |
| { |
| try |
| { |
| HostBifurcation hostBifurcation(0, 2, cpuPCIeData, |
| "test_output.bin"); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| // This might be thrown if D-Bus calls happen before the slot |
| // check |
| SUCCEED() << "Caught expected sdbusplus exception: " |
| << e.what(); |
| } |
| }, |
| std::runtime_error); |
| std::remove("test_output.bin"); |
| } |
| |
| // Helper function to read binary file content |
| static std::vector<uint8_t> readFileContent(const std::string& filename) |
| { |
| std::ifstream file(filename, std::ios::binary); |
| if (!file.is_open()) |
| { |
| return {}; |
| } |
| file.seekg(0, std::ios::end); |
| std::streampos size = file.tellg(); |
| file.seekg(0, std::ios::beg); |
| std::vector<uint8_t> content(size); |
| file.read(reinterpret_cast<char*>(content.data()), size); |
| return content; |
| } |
| |
| TEST(WriteBifurcationDataTest, WriteToNewFile) |
| { |
| const std::string testFile = "new_file.bin"; |
| std::remove(testFile.c_str()); |
| |
| nlohmann::json cpuPCIeData = |
| nlohmann::json::parse(R"([{"slot": 0, "bus": 10}])"); |
| HostBifurcation host(0, 1, cpuPCIeData, testFile); |
| |
| std::vector<std::vector<uint8_t>> bifurcationData = {{8}}; |
| host.writeBifurcationDataToFile(bifurcationData); |
| |
| auto content = readFileContent(testFile); |
| // Expected: |
| // 1 (size of bifurcationData) |
| // 1 (size of slot 0 data) |
| // 8 (data) |
| ASSERT_EQ(content.size(), 3); |
| EXPECT_EQ(content[0], 1); |
| EXPECT_EQ(content[1], 1); // x8x8 -> parsed to {8} -> 1 byte |
| EXPECT_EQ(content[2], 8); |
| |
| std::remove(testFile.c_str()); |
| } |
| |
| TEST(WriteBifurcationDataTest, OverwriteExistingFile) |
| { |
| const std::string testFile = "existing_file.bin"; |
| std::remove(testFile.c_str()); |
| |
| // Create a file with some initial data |
| std::ofstream outfile(testFile, std::ios::binary); |
| outfile.write("GarbageData", 11); |
| outfile.close(); |
| |
| nlohmann::json cpuPCIeData = |
| nlohmann::json::parse(R"([{"slot": 0, "bus": 10}])"); |
| HostBifurcation host(0, 1, cpuPCIeData, testFile); |
| |
| std::vector<std::vector<uint8_t>> bifurcationData = {{16}}; |
| host.writeBifurcationDataToFile(bifurcationData); |
| |
| auto content = readFileContent(testFile); |
| // Expected: |
| // 1 (size) |
| // 1 (slot size) |
| // 16 (data) |
| ASSERT_EQ(content.size(), 3); |
| EXPECT_EQ(content[0], 1); |
| EXPECT_EQ(content[1], 1); |
| EXPECT_EQ(content[2], 16); |
| |
| std::remove(testFile.c_str()); |
| } |
| |
| TEST(WriteBifurcationDataTest, WriteMultipleSlots) |
| { |
| const std::string testFile = "multi_slot.bin"; |
| std::remove(testFile.c_str()); |
| |
| nlohmann::json cpuPCIeData = nlohmann::json::parse(R"([ |
| {"slot": 0, "bus": 10}, |
| {"slot": 1, "bus": 11} |
| ])"); |
| HostBifurcation host(0, 2, cpuPCIeData, testFile); |
| |
| std::vector<std::vector<uint8_t>> bifurcationData = {{4, 4}, {16}}; |
| host.writeBifurcationDataToFile(bifurcationData); |
| |
| auto content = readFileContent(testFile); |
| |
| // Expected: |
| // 1 byte: num slots (2) |
| // Slot 0: |
| // 1 byte: size (2) |
| // 2 bytes: 4, 4 |
| // Slot 1: |
| // 1 byte: size (1) |
| // 1 byte: 16 |
| // Total: 1 + 1 + 2 + 1 + 1 = 6 bytes |
| |
| ASSERT_EQ(content.size(), 6); |
| EXPECT_EQ(content[0], 2); // num slots |
| |
| // Slot 0 |
| EXPECT_EQ(content[1], 2); // size |
| EXPECT_EQ(content[2], 4); |
| EXPECT_EQ(content[3], 4); |
| |
| // Slot 1 |
| EXPECT_EQ(content[4], 1); // size |
| EXPECT_EQ(content[5], 16); |
| |
| std::remove(testFile.c_str()); |
| } |