blob: 2d8c99c862c0bda034bcc17f3bad206730b805aa [file] [log] [blame] [edit]
#include "bus_bifurcation.hpp"
#include "pcie_bifurcation_utils.hpp"
#include <sdbusplus/bus.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/test/sdbus_mock.hpp>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace google::pcie_bifurcation
{
namespace
{
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgReferee;
using ::testing::StrEq;
TEST(BusBifurcationTest, ConstructorAndGetters)
{
BusPCIeBifurcation busBifurcation(1, 0x10, "x4x4", false);
const std::vector<uint8_t> expected = {4, 4};
EXPECT_EQ(busBifurcation.getBifurcation(), expected);
}
TEST(BusBifurcationTest, UpdateAddress)
{
BusPCIeBifurcation busBifurcation(2, 0x20, "x8x8", false);
busBifurcation.updateAddress(0x25);
SUCCEED();
}
TEST(BusBifurcationTest, UpdateEMPath)
{
BusPCIeBifurcation busBifurcation(3, 0x30, "x16", false);
busBifurcation.updateEMPath("/phys/PE0");
SUCCEED();
}
TEST(BusBifurcationTest, UpdateValueAndPopulate)
{
// Initially empty bifurcation
BusPCIeBifurcation busBifurcation(4, 0x40, "", false);
EXPECT_TRUE(busBifurcation.getBifurcation().empty());
// First updateValue call should populate as bifurcations_ is empty
busBifurcation.updateValue("PCIe-bifurcation:x16");
const std::vector<uint8_t> expected1 = {16};
EXPECT_EQ(busBifurcation.getBifurcation(), expected1);
// Subsequent updateValue calls SHOULD change bifurcations_
// Priority should be along the lines of (From highest -> lowest):
// Entity Manager -> FRU -> default_config
busBifurcation.updateValue("PCIe-bifurcation:x4x4");
const std::vector<uint8_t> expected2 = {4, 4};
EXPECT_EQ(busBifurcation.getBifurcation(), expected2); // Should change
// populateBifurcation won't change bifurcations_ since EM path doesn't
// exist
busBifurcation.populateBifurcation();
EXPECT_EQ(busBifurcation.getBifurcation(), expected2); // Still {4, 4}
// Invalid updateValue call should not change bifurcations_
busBifurcation.updateValue("invalid:x16");
EXPECT_EQ(busBifurcation.getBifurcation(), expected2); // Still {4, 4}
// Valid updateValue call but invalid parseBifurcationString will clear
// bifurcations_. Can be x16 or x8x8 due to legacy reasons. Can't be x8x4x4
// or x4x4x8 as this would indicate that the A/B slot is more than 8 lanes
busBifurcation.updateValue("PCIe-bifurcation:x4x4x8");
EXPECT_TRUE(busBifurcation.getBifurcation().empty());
busBifurcation.updateValue("PCIe-bifurcation:x8x4x4");
EXPECT_TRUE(busBifurcation.getBifurcation().empty());
}
TEST(BusBifurcationTest, PopulateBifurcationDefault)
{
BusPCIeBifurcation busBifurcation(5, 0x50, "", false);
EXPECT_TRUE(busBifurcation.getBifurcation().empty());
busBifurcation.updateValue("PCIe-bifurcation:x8");
busBifurcation.populateBifurcation();
EXPECT_THAT(busBifurcation.getBifurcation(), ElementsAre(8));
}
TEST(BusBifurcationTest, PopulateBifurcationHardcoded)
{
// Slot 0, Bus 0 with static_value of x1x4. static_value is invalid
EXPECT_TRUE(
BusPCIeBifurcation(0, 0, "x1x4", false).getBifurcation().empty());
// Slot 0, no Bus with static_value of x1x4. static_value is valid
BusPCIeBifurcation busBifurcation(0, -1, "x1x4", true);
const std::vector<uint8_t> expected = {1, 4};
EXPECT_EQ(busBifurcation.getBifurcation(), expected);
}
TEST(BusBifurcationTest, ParseBifurcationString)
{
// For NIC case.
EXPECT_EQ(parseBifurcationString("x16"), std::vector<uint8_t>({16}));
// For S3Fist case.
EXPECT_EQ(parseBifurcationString("x8"), std::vector<uint8_t>({8}));
// For IH case.
EXPECT_EQ(parseBifurcationString("x4x4"), std::vector<uint8_t>({4, 4}));
// For legacy RP case.
EXPECT_EQ(parseBifurcationString("x8x8"), std::vector<uint8_t>({8}));
// expecting sum of them to be x8.
EXPECT_EQ(parseBifurcationString("x1"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x2"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x4"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x2x2"), std::vector<uint8_t>({}));
// For PCIe utilty clusters which don't add up to x8 or x16
EXPECT_EQ(parseBifurcationString("x1x4"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x1x4", true),
std::vector<uint8_t>({1, 4}));
// In theory, it's support-able on BMC but we don't have these case yet.
EXPECT_EQ(parseBifurcationString("x1x1x1x1x1x1x1x1"),
std::vector<uint8_t>({1, 1, 1, 1, 1, 1, 1, 1}));
EXPECT_EQ(parseBifurcationString("x2x2x2x2"),
std::vector<uint8_t>({2, 2, 2, 2}));
EXPECT_EQ(parseBifurcationString("x4x2x2"),
std::vector<uint8_t>({4, 2, 2}));
// We don't support single A/B slot handle bifurcation being greater than 8.
EXPECT_EQ(parseBifurcationString("x8x4x4"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x4x4x8"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x4x4x4x4"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x2x2x2x2x2x2x2x2"),
std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x16x4"), std::vector<uint8_t>({}));
// Syntax issue cases
EXPECT_EQ(parseBifurcationString(""), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x16x"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("xx8"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x8xx8"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x16 "), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString(" x16"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("x1a2"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString("X16"), std::vector<uint8_t>({}));
EXPECT_EQ(parseBifurcationString(" x 16 "), std::vector<uint8_t>({}));
}
class BusBifurcationDBusTest : public ::testing::Test
{
protected:
sdbusplus::SdBusMock sdbusMock;
std::unique_ptr<sdbusplus::bus::bus> bus;
void SetUp() override
{
bus = std::make_unique<sdbusplus::bus::bus>(
sdbusplus::get_mocked_new(&sdbusMock));
// Set default actions for all mocked sd_bus functions to prevent
// crashes
ON_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, _))
.WillByDefault(Return(0));
ON_CALL(sdbusMock, sd_bus_message_ref(_))
.WillByDefault(Return(nullptr));
ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, _)).WillByDefault(Return(0));
ON_CALL(sdbusMock, sd_bus_message_read_basic(_, _, _))
.WillByDefault(Return(0));
ON_CALL(sdbusMock, sd_bus_message_enter_container(_, _, _))
.WillByDefault(Return(0));
ON_CALL(sdbusMock, sd_bus_message_exit_container(_))
.WillByDefault(Return(0));
ON_CALL(sdbusMock, sd_bus_message_at_end(_, _))
.WillByDefault(Return(1));
ON_CALL(sdbusMock, sd_bus_error_set_const(_, _, _))
.WillByDefault(Return(0));
}
};
TEST_F(BusBifurcationDBusTest, PhysicalAssociationsSuccess)
{
// This test remains complex to mock fully without exact mock interface
// knowledge. We'll just check that the function can be called without
// crashing.
try
{
auto paths = physicalAssociations(*bus, "/phys/PE0");
EXPECT_TRUE(paths.empty()); // Expect empty due to mock setup
}
catch (const std::exception& e)
{
FAIL() << "Unexpected exception: " << e.what();
}
}
TEST_F(BusBifurcationDBusTest, PopulateBifurcationWithDbusValue)
{
BusPCIeBifurcation busBifurcation(6, 0x60, "x16", false);
busBifurcation.updateEMPath("/xyz/test/em/path");
// Similar to above, we just check for no crashes
try
{
busBifurcation.populateBifurcation();
EXPECT_THAT(busBifurcation.getBifurcation(),
ElementsAre(16)); // Should remain unchanged
}
catch (const std::exception& e)
{
FAIL() << "Unexpected exception: " << e.what();
}
}
} // namespace
} // namespace google::pcie_bifurcation