blob: 2e9367212c275cface27918140969e9e5f84636d [file] [log] [blame]
#include "activation.hpp"
#include "mocked_activation_listener.hpp"
#include "mocked_association_interface.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::Return;
using ::testing::StrEq;
using std::any;
class TestActivation : public ::testing::Test
{
public:
using PropertyType = utils::UtilsInterface::PropertyType;
using Status = Activation::Status;
using RequestedStatus = Activation::RequestedActivations;
TestActivation() :
mockedUtils(
reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
{
// By default make it compatible with the test software
ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
.WillByDefault(Return(any(PropertyType(std::string("TestManu")))));
ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL)))
.WillByDefault(Return(any(PropertyType(std::string("TestModel")))));
ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false));
}
~TestActivation()
{
utils::freeUtils();
}
void onUpdateDone()
{
activation->onUpdateDone();
}
void onUpdateFailed()
{
activation->onUpdateFailed();
}
int getProgress()
{
return activation->activationProgress->progress();
}
const auto& getPsuQueue()
{
return activation->psuQueue;
}
std::string getUpdateService(const std::string& psuInventoryPath)
{
return activation->getUpdateService(psuInventoryPath);
}
sdbusplus::SdBusMock sdbusMock;
sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
const utils::MockedUtils& mockedUtils;
MockedAssociationInterface mockedAssociationInterface;
MockedActivationListener mockedActivationListener;
std::unique_ptr<Activation> activation;
std::string versionId = "abcdefgh";
std::string extVersion = "manufacturer=TestManu,model=TestModel";
std::string filePath = "/tmp/images/abcdefgh";
std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
Status status = Status::Ready;
AssociationList associations;
};
TEST_F(TestActivation, ctordtor)
{
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
}
TEST_F(TestActivation, ctorWithInvalidExtVersion)
{
extVersion = "invalid text";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
}
TEST_F(TestActivation, getUpdateService)
{
std::string psuInventoryPath = "/com/example/inventory/powersupply1";
std::string toCompare = "psu-update@-com-example-inventory-"
"powersupply1\\x20-tmp-images-12345678.service";
versionId = "12345678";
filePath = "/tmp/images/12345678";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
auto service = getUpdateService(psuInventoryPath);
EXPECT_EQ(toCompare, service);
}
TEST_F(TestActivation, doUpdateWhenNoPSU)
{
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({}))); // No PSU inventory
activation->requestedActivation(RequestedStatus::Active);
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
EXPECT_EQ(Status::Failed, activation->activation());
}
TEST_F(TestActivation, doUpdateOnePSUOK)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu0)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Active, activation->activation());
}
TEST_F(TestActivation, doUpdateFourPSUsOK)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto psu2 = "/com/example/inventory/psu2";
constexpr auto psu3 = "/com/example/inventory/psu3";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(
std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(10, getProgress());
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu0)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(30, getProgress());
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu1)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(50, getProgress());
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu2)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(70, getProgress());
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu3)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Active, activation->activation());
}
TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto psu2 = "/com/example/inventory/psu2";
constexpr auto psu3 = "/com/example/inventory/psu3";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(
std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(10, getProgress());
EXPECT_CALL(mockedActivationListener,
onUpdateDone(StrEq(versionId), StrEq(psu0)))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(30, getProgress());
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(0);
EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
onUpdateFailed();
EXPECT_EQ(Status::Failed, activation->activation());
}
TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
.WillByDefault(Return(-1)); // Make sdbus call failure
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Failed, activation->activation());
}
TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
extVersion = "manufacturer=TestManu,model=DifferentModel";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(std::vector<std::string>({psu0})));
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Ready, activation->activation());
}
TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
extVersion = "manufacturer=DifferentManu,model=TestModel";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(std::vector<std::string>({psu0})));
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Ready, activation->activation());
}
TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty)
{
ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
.WillByDefault(Return(any(PropertyType(std::string("")))));
extVersion = "manufacturer=AnyManu,model=TestModel";
// Below is the same as doUpdateOnePSUOK case
constexpr auto psu0 = "/com/example/inventory/psu0";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Active, activation->activation());
}
TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
constexpr auto psu1 = "/com/example/inventory/psu1";
constexpr auto psu2 = "/com/example/inventory/psu2";
constexpr auto psu3 = "/com/example/inventory/psu3";
ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL)))
.WillByDefault(
Return(any(PropertyType(std::string("DifferentModel")))));
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(Return(
std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
activation->requestedActivation(RequestedStatus::Active);
const auto& psuQueue = getPsuQueue();
EXPECT_EQ(3u, psuQueue.size());
// Only 3 PSUs shall be updated, and psu1 shall be skipped
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(10, getProgress());
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(36, getProgress());
onUpdateDone();
EXPECT_EQ(Status::Activating, activation->activation());
EXPECT_EQ(62, getProgress());
EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
.Times(1);
EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
.Times(1);
onUpdateDone();
EXPECT_EQ(Status::Active, activation->activation());
}
TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState)
{
filePath = "";
status = Status::Active; // Typically, a running PSU software is active
// without file path
constexpr auto psu0 = "/com/example/inventory/psu0";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
// There shall be no DBus call to start update service
EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
StrEq("StartUnit")))
.Times(0);
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Active, activation->activation());
}
TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState)
{
filePath = "";
status = Status::Ready; // Usually a Ready activation should have file path,
// but we are testing this case as well
constexpr auto psu0 = "/com/example/inventory/psu0";
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
// There shall be no DBus call to start update service
EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
StrEq("StartUnit")))
.Times(0);
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Ready, activation->activation());
}
TEST_F(TestActivation, doUpdateWhenPSUIsAssociated)
{
constexpr auto psu0 = "/com/example/inventory/psu0";
status = Status::Active; // Typically, a running PSU software is associated
activation = std::make_unique<Activation>(
mockedBus, dBusPath, versionId, extVersion, status, associations,
filePath, &mockedAssociationInterface, &mockedActivationListener);
ON_CALL(mockedUtils, getPSUInventoryPath(_))
.WillByDefault(
Return(std::vector<std::string>({psu0}))); // One PSU inventory
// When PSU is already associated, there shall be no DBus call to start
// update service
EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
.WillOnce(Return(true));
EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
StrEq("StartUnit")))
.Times(0);
activation->requestedActivation(RequestedStatus::Active);
EXPECT_EQ(Status::Active, activation->activation());
}