blob: 95a0d975dd73fca3c6c87344579c7ad4883f9cac [file] [log] [blame] [edit]
#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());
}