blob: 2fe1c863810ae9684195b5a947b84538eaf0ef07 [file] [log] [blame]
#include "item_updater.hpp"
#include "mocked_utils.hpp"
#include <sdbusplus/test/sdbus_mock.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace phosphor::software::updater;
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::ReturnArg;
using ::testing::StrEq;
using std::any;
class TestItemUpdater : public ::testing::Test
{
public:
using Properties = ItemUpdater::Properties;
using PropertyType = utils::UtilsInterface::PropertyType;
TestItemUpdater() :
mockedUtils(
reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
{
ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>());
ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT)))
.WillByDefault(Return(any(PropertyType(true))));
}
~TestItemUpdater()
{
utils::freeUtils();
}
auto& GetActivations()
{
return itemUpdater->activations;
}
std::string getObjPath(const std::string& versionId)
{
return std::string(dBusPath) + "/" + versionId;
}
void onPsuInventoryChanged(const std::string& psuPath,
const Properties& properties)
{
itemUpdater->onPsuInventoryChanged(psuPath, properties);
}
void scanDirectory(const fs::path& p)
{
itemUpdater->scanDirectory(p);
}
static constexpr auto dBusPath = SOFTWARE_OBJPATH;
sdbusplus::SdBusMock sdbusMock;
sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
const utils::MockedUtils& mockedUtils;
std::unique_ptr<ItemUpdater> itemUpdater;
Properties propAdded{{PRESENT, PropertyType(true)}};
Properties propRemoved{{PRESENT, PropertyType(false)}};
Properties propModel{{MODEL, PropertyType(std::string("dummyModel"))}};
};
TEST_F(TestItemUpdater, ctordtor)
{
EXPECT_CALL(mockedUtils, getLatestVersion(_)).Times(1);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
}
TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(false)))); // not present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// No activation/version objects are created
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(0);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
}
TEST_F(TestItemUpdater, CreateOnePSUOnPresent)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
}
TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto service = "com.example.Software.Psu";
auto version0 = std::string("version0");
auto version1 = std::string("version0");
auto objPath0 = getObjPath(version0);
auto objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psu0, psu1})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// Verify there is only one activation and it has two associations
const auto& activations = GetActivations();
EXPECT_EQ(1u, activations.size());
const auto& activation = activations.find(version0)->second;
const auto& assocs = activation->associations();
EXPECT_EQ(2u, assocs.size());
EXPECT_EQ(psu0, std::get<2>(assocs[0]));
EXPECT_EQ(psu1, std::get<2>(assocs[1]));
}
TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto service = "com.example.Software.Psu";
auto version0 = std::string("version0");
auto version1 = std::string("version1");
auto objPath0 = getObjPath(version0);
auto objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psu0, psu1})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// two new activation and version objects will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// Verify there are two activations and each with one association
const auto& activations = GetActivations();
EXPECT_EQ(2u, activations.size());
const auto& activation0 = activations.find(version0)->second;
const auto& assocs0 = activation0->associations();
EXPECT_EQ(1u, assocs0.size());
EXPECT_EQ(psu0, std::get<2>(assocs0[0]));
const auto& activation1 = activations.find(version1)->second;
const auto& assocs1 = activation1->associations();
EXPECT_EQ(1u, assocs1.size());
EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
}
TEST_F(TestItemUpdater, OnOnePSURemoved)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// the activation and version object will be removed
Properties p{{PRESENT, PropertyType(false)}};
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
.Times(2);
onPsuInventoryChanged(psuPath, p);
// on exit, item updater is removed
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
.Times(1);
}
TEST_F(TestItemUpdater, OnOnePSUAdded)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(false)))); // not present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// No activation/version objects are created
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(0);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// The PSU is present and version is added in a single call
Properties propAddedAndModel{
{PRESENT, PropertyType(true)},
{MODEL, PropertyType(std::string("testModel"))}};
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
onPsuInventoryChanged(psuPath, propAddedAndModel);
}
TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithLatestVersion)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// the activation and version object will be removed
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
.Times(2);
onPsuInventoryChanged(psuPath, propRemoved);
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
// On PSU inserted, it shall check if it's the latest version
std::set<std::string> expectedVersions = {version};
EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions)))
.WillOnce(Return(version));
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psuPath), _))
.WillOnce(Return(true));
EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
StrEq("StartUnit")))
.Times(0);
onPsuInventoryChanged(psuPath, propAdded);
onPsuInventoryChanged(psuPath, propModel);
// on exit, objects are removed
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
.Times(1);
}
TEST_F(TestItemUpdater,
TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto service = "com.example.Software.Psu";
auto version0 = std::string("version0");
auto version1 = std::string("version0");
auto objPath0 = getObjPath(version0);
auto objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psu0, psu1})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// Verify there is only one activation and it has two associations
const auto& activations = GetActivations();
EXPECT_EQ(1u, activations.size());
const auto& activation = activations.find(version0)->second;
auto assocs = activation->associations();
EXPECT_EQ(2u, assocs.size());
EXPECT_EQ(psu0, std::get<2>(assocs[0]));
EXPECT_EQ(psu1, std::get<2>(assocs[1]));
// PSU0 is removed, only associations shall be updated
onPsuInventoryChanged(psu0, propRemoved);
assocs = activation->associations();
EXPECT_EQ(1u, assocs.size());
EXPECT_EQ(psu1, std::get<2>(assocs[0]));
// PSU1 is removed, the activation and version object shall be removed
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
.Times(2);
onPsuInventoryChanged(psu1, propRemoved);
// Add PSU0 and PSU1 back, but PSU1 with a different version
version1 = "version1";
objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
.Times(2);
onPsuInventoryChanged(psu0, propAdded);
onPsuInventoryChanged(psu1, propModel);
onPsuInventoryChanged(psu1, propAdded);
onPsuInventoryChanged(psu0, propModel);
// on exit, objects are removed
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
.Times(1);
}
TEST_F(TestItemUpdater, scanDirOnNoPSU)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(false)))); // not present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// No activation/version objects are created
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(0);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// The valid image in test/psu-images-one-valid-one-invalid/model-1/
auto objPathValid = getObjPath("psu-test.v0.4");
auto objPathInvalid = getObjPath("psu-test.v0.5");
// activation and version object will be added on scan dir
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid)))
.Times(2);
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid)))
.Times(0);
scanDirectory("./psu-images-one-valid-one-invalid");
}
TEST_F(TestItemUpdater, scanDirOnSamePSUVersion)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string objPath = getObjPath(version);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psuPath})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
// The item updater itself
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
.Times(1);
// activation and version object will be added
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(2);
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// The valid image in test/psu-images-valid-version0/model-3/ has the same
// version as the running PSU, so no objects will be created, but only the
// path will be set to the version object
EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
.Times(0);
EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv(
_, StrEq(objPath),
StrEq("xyz.openbmc_project.Common.FilePath"),
Pointee(StrEq("Path"))))
.Times(1);
scanDirectory("./psu-images-valid-version0");
}
TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithSameVersion)
{
// Simulate there are two PSUs with same version, and updated to a new
// version
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto service = "com.example.Software.Psu";
auto version0 = std::string("version0");
auto version1 = std::string("version0");
auto objPath0 = getObjPath(version0);
auto objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psu0, psu1})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
std::string newVersionId = "NewVersionId";
AssociationList associations;
auto dummyActivation = std::make_unique<Activation>(
mockedBus, dBusPath, newVersionId, "", Activation::Status::Active,
associations, "", itemUpdater.get(), itemUpdater.get());
// Now there is one activation and it has two associations
auto& activations = GetActivations();
activations.emplace(newVersionId, std::move(dummyActivation));
auto& activation = activations.find(version0)->second;
auto assocs = activation->associations();
EXPECT_EQ(2u, assocs.size());
EXPECT_EQ(psu0, std::get<2>(assocs[0]));
EXPECT_EQ(psu1, std::get<2>(assocs[1]));
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
.WillOnce(Return(true));
itemUpdater->onUpdateDone(newVersionId, psu0);
// Now the activation should have one assoiation
assocs = activation->associations();
EXPECT_EQ(1u, assocs.size());
EXPECT_EQ(psu1, std::get<2>(assocs[0]));
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _))
.WillOnce(Return(true));
itemUpdater->onUpdateDone(newVersionId, psu1);
// Now the activation shall be erased and only the dummy one is left
EXPECT_EQ(1u, activations.size());
EXPECT_NE(activations.find(newVersionId), activations.end());
}
TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithDifferentVersion)
{
// Simulate there are two PSUs with different version, and updated to a new
// version
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto service = "com.example.Software.Psu";
auto version0 = std::string("version0");
auto version1 = std::string("version1");
auto objPath0 = getObjPath(version0);
auto objPath1 = getObjPath(version1);
EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
.WillOnce(Return(std::vector<std::string>({psu0, psu1})));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
.WillOnce(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
.WillOnce(Return(std::string(version0)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
.WillOnce(Return(std::string(version1)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
StrEq(PRESENT)))
.WillOnce(Return(any(PropertyType(true)))); // present
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
std::string newVersionId = "NewVersionId";
AssociationList associations;
auto dummyActivation = std::make_unique<Activation>(
mockedBus, dBusPath, newVersionId, "", Activation::Status::Active,
associations, "", itemUpdater.get(), itemUpdater.get());
auto& activations = GetActivations();
activations.emplace(newVersionId, std::move(dummyActivation));
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
.WillOnce(Return(true));
// After psu0 is done, two activations should be left
itemUpdater->onUpdateDone(newVersionId, psu0);
EXPECT_EQ(2u, activations.size());
const auto& activation1 = activations.find(version1)->second;
const auto& assocs1 = activation1->associations();
EXPECT_EQ(1u, assocs1.size());
EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _))
.WillOnce(Return(true));
// After psu1 is done, only the dummy activation should be left
itemUpdater->onUpdateDone(newVersionId, psu1);
EXPECT_EQ(1u, activations.size());
EXPECT_NE(activations.find(newVersionId), activations.end());
}
TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithOldVersion)
{
constexpr auto psuPath = "/com/example/inventory/psu0";
constexpr auto service = "com.example.Software.Psu";
constexpr auto version = "version0";
std::string versionId =
version; // In testing versionId is the same as version
std::string objPath = getObjPath(version);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(std::vector<std::string>({psuPath})));
ON_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
.WillByDefault(Return(service));
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(version)));
ON_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _,
StrEq(PRESENT)))
.WillByDefault(Return(any(PropertyType(true)))); // present
itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
// Add an association to simulate that it has image in BMC filesystem
auto& activation = GetActivations().find(versionId)->second;
auto assocs = activation->associations();
assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
"SomePath");
activation->associations(assocs);
activation->path("SomeFilePath");
onPsuInventoryChanged(psuPath, propRemoved);
// On PSU inserted, it checks and finds a newer version
auto oldVersion = "old-version";
EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
.WillOnce(Return(std::string(oldVersion)));
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(MANUFACTURER)))
.WillOnce(
Return(any(PropertyType(std::string(""))))); // Checking compatible
EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
_, StrEq(MODEL)))
.WillOnce(
Return(any(PropertyType(std::string(""))))); // Checking compatible
std::set<std::string> expectedVersions = {version, oldVersion};
EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions)))
.WillOnce(Return(version));
ON_CALL(mockedUtils, isAssociated(StrEq(psuPath), _))
.WillByDefault(Return(false));
EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
StrEq("StartUnit")))
.Times(3); // There are 3 systemd units are started, enable bmc reboot
// guard, start activation, and disable bmc reboot guard
onPsuInventoryChanged(psuPath, propAdded);
onPsuInventoryChanged(psuPath, propModel);
}