1 #include "activation.hpp" 2 #include "mocked_association_interface.hpp" 3 #include "mocked_utils.hpp" 4 5 #include <sdbusplus/test/sdbus_mock.hpp> 6 7 #include <gmock/gmock.h> 8 #include <gtest/gtest.h> 9 10 using namespace phosphor::software::updater; 11 12 using ::testing::_; 13 using ::testing::Return; 14 using ::testing::StrEq; 15 16 using std::experimental::any; 17 18 class TestActivation : public ::testing::Test 19 { 20 public: 21 using PropertyType = utils::UtilsInterface::PropertyType; 22 using Status = Activation::Status; 23 using RequestedStatus = Activation::RequestedActivations; 24 TestActivation() : 25 mockedUtils( 26 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) 27 { 28 // By default make it compatible with the test software 29 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 30 .WillByDefault(Return(any(PropertyType(std::string("TestManu"))))); 31 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL))) 32 .WillByDefault(Return(any(PropertyType(std::string("TestModel"))))); 33 } 34 ~TestActivation() 35 { 36 } 37 38 void onUpdateDone() 39 { 40 activation->onUpdateDone(); 41 } 42 void onUpdateFailed() 43 { 44 activation->onUpdateFailed(); 45 } 46 int getProgress() 47 { 48 return activation->activationProgress->progress(); 49 } 50 const auto& getPsuQueue() 51 { 52 return activation->psuQueue; 53 } 54 55 sdbusplus::SdBusMock sdbusMock; 56 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock); 57 const utils::MockedUtils& mockedUtils; 58 MockedAssociationInterface mockedAssociationInterface; 59 std::unique_ptr<Activation> activation; 60 std::string versionId = "abcdefgh"; 61 std::string extVersion = "manufacturer=TestManu,model=TestModel"; 62 std::string filePath = ""; 63 std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId; 64 Status status = Status::Ready; 65 AssociationList associations; 66 }; 67 68 TEST_F(TestActivation, ctordtor) 69 { 70 activation = std::make_unique<Activation>( 71 mockedBus, dBusPath, versionId, extVersion, status, associations, 72 &mockedAssociationInterface, filePath); 73 } 74 75 namespace phosphor::software::updater::internal 76 { 77 extern std::string getUpdateService(const std::string& psuInventoryPath, 78 const std::string& versionId); 79 } 80 81 TEST_F(TestActivation, getUpdateService) 82 { 83 std::string psuInventoryPath = "/com/example/inventory/powersupply1"; 84 std::string versionId = "12345678"; 85 std::string toCompare = "psu-update@-com-example-inventory-" 86 "powersupply1\\x20-tmp-images-12345678.service"; 87 88 auto service = phosphor::software::updater::internal::getUpdateService( 89 psuInventoryPath, versionId); 90 EXPECT_EQ(toCompare, service); 91 } 92 93 TEST_F(TestActivation, doUpdateWhenNoPSU) 94 { 95 activation = std::make_unique<Activation>( 96 mockedBus, dBusPath, versionId, extVersion, status, associations, 97 &mockedAssociationInterface, filePath); 98 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 99 .WillByDefault( 100 Return(std::vector<std::string>({}))); // No PSU inventory 101 activation->requestedActivation(RequestedStatus::Active); 102 103 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 104 .Times(0); 105 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 106 .Times(0); 107 EXPECT_EQ(Status::Failed, activation->activation()); 108 } 109 110 TEST_F(TestActivation, doUpdateOnePSUOK) 111 { 112 constexpr auto psu0 = "/com/example/inventory/psu0"; 113 activation = std::make_unique<Activation>( 114 mockedBus, dBusPath, versionId, extVersion, status, associations, 115 &mockedAssociationInterface, filePath); 116 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 117 .WillByDefault( 118 Return(std::vector<std::string>({psu0}))); // One PSU inventory 119 activation->requestedActivation(RequestedStatus::Active); 120 121 EXPECT_EQ(Status::Activating, activation->activation()); 122 123 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 124 .Times(1); 125 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 126 .Times(1); 127 onUpdateDone(); 128 EXPECT_EQ(Status::Active, activation->activation()); 129 } 130 131 TEST_F(TestActivation, doUpdateFourPSUsOK) 132 { 133 constexpr auto psu0 = "/com/example/inventory/psu0"; 134 constexpr auto psu1 = "/com/example/inventory/psu1"; 135 constexpr auto psu2 = "/com/example/inventory/psu2"; 136 constexpr auto psu3 = "/com/example/inventory/psu3"; 137 activation = std::make_unique<Activation>( 138 mockedBus, dBusPath, versionId, extVersion, status, associations, 139 &mockedAssociationInterface, filePath); 140 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 141 .WillByDefault(Return( 142 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 143 activation->requestedActivation(RequestedStatus::Active); 144 145 EXPECT_EQ(Status::Activating, activation->activation()); 146 EXPECT_EQ(10, getProgress()); 147 148 onUpdateDone(); 149 EXPECT_EQ(Status::Activating, activation->activation()); 150 EXPECT_EQ(30, getProgress()); 151 152 onUpdateDone(); 153 EXPECT_EQ(Status::Activating, activation->activation()); 154 EXPECT_EQ(50, getProgress()); 155 156 onUpdateDone(); 157 EXPECT_EQ(Status::Activating, activation->activation()); 158 EXPECT_EQ(70, getProgress()); 159 160 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 161 .Times(1); 162 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 163 .Times(1); 164 165 onUpdateDone(); 166 EXPECT_EQ(Status::Active, activation->activation()); 167 } 168 169 TEST_F(TestActivation, doUpdateFourPSUsFailonSecond) 170 { 171 constexpr auto psu0 = "/com/example/inventory/psu0"; 172 constexpr auto psu1 = "/com/example/inventory/psu1"; 173 constexpr auto psu2 = "/com/example/inventory/psu2"; 174 constexpr auto psu3 = "/com/example/inventory/psu3"; 175 activation = std::make_unique<Activation>( 176 mockedBus, dBusPath, versionId, extVersion, status, associations, 177 &mockedAssociationInterface, filePath); 178 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 179 .WillByDefault(Return( 180 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 181 activation->requestedActivation(RequestedStatus::Active); 182 183 EXPECT_EQ(Status::Activating, activation->activation()); 184 EXPECT_EQ(10, getProgress()); 185 186 onUpdateDone(); 187 EXPECT_EQ(Status::Activating, activation->activation()); 188 EXPECT_EQ(30, getProgress()); 189 190 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 191 .Times(0); 192 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 193 .Times(0); 194 onUpdateFailed(); 195 EXPECT_EQ(Status::Failed, activation->activation()); 196 } 197 198 TEST_F(TestActivation, doUpdateOnExceptionFromDbus) 199 { 200 constexpr auto psu0 = "/com/example/inventory/psu0"; 201 activation = std::make_unique<Activation>( 202 mockedBus, dBusPath, versionId, extVersion, status, associations, 203 &mockedAssociationInterface, filePath); 204 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 205 .WillByDefault( 206 Return(std::vector<std::string>({psu0}))); // One PSU inventory 207 ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr)) 208 .WillByDefault(Return(-1)); // Make sdbus call failure 209 activation->requestedActivation(RequestedStatus::Active); 210 211 EXPECT_EQ(Status::Failed, activation->activation()); 212 } 213 214 TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible) 215 { 216 constexpr auto psu0 = "/com/example/inventory/psu0"; 217 extVersion = "manufacturer=TestManu,model=DifferentModel"; 218 activation = std::make_unique<Activation>( 219 mockedBus, dBusPath, versionId, extVersion, status, associations, 220 &mockedAssociationInterface, filePath); 221 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 222 .WillByDefault(Return(std::vector<std::string>({psu0}))); 223 activation->requestedActivation(RequestedStatus::Active); 224 225 EXPECT_EQ(Status::Failed, activation->activation()); 226 } 227 228 TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible) 229 { 230 constexpr auto psu0 = "/com/example/inventory/psu0"; 231 extVersion = "manufacturer=DifferentManu,model=TestModel"; 232 activation = std::make_unique<Activation>( 233 mockedBus, dBusPath, versionId, extVersion, status, associations, 234 &mockedAssociationInterface, filePath); 235 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 236 .WillByDefault(Return(std::vector<std::string>({psu0}))); 237 activation->requestedActivation(RequestedStatus::Active); 238 239 EXPECT_EQ(Status::Failed, activation->activation()); 240 } 241 242 TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty) 243 { 244 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 245 .WillByDefault(Return(any(PropertyType(std::string(""))))); 246 extVersion = "manufacturer=AnyManu,model=TestModel"; 247 // Below is the same as doUpdateOnePSUOK case 248 constexpr auto psu0 = "/com/example/inventory/psu0"; 249 activation = std::make_unique<Activation>( 250 mockedBus, dBusPath, versionId, extVersion, status, associations, 251 &mockedAssociationInterface, filePath); 252 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 253 .WillByDefault( 254 Return(std::vector<std::string>({psu0}))); // One PSU inventory 255 activation->requestedActivation(RequestedStatus::Active); 256 257 EXPECT_EQ(Status::Activating, activation->activation()); 258 259 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 260 .Times(1); 261 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 262 .Times(1); 263 onUpdateDone(); 264 EXPECT_EQ(Status::Active, activation->activation()); 265 } 266 267 TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible) 268 { 269 constexpr auto psu0 = "/com/example/inventory/psu0"; 270 constexpr auto psu1 = "/com/example/inventory/psu1"; 271 constexpr auto psu2 = "/com/example/inventory/psu2"; 272 constexpr auto psu3 = "/com/example/inventory/psu3"; 273 ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL))) 274 .WillByDefault( 275 Return(any(PropertyType(std::string("DifferentModel"))))); 276 activation = std::make_unique<Activation>( 277 mockedBus, dBusPath, versionId, extVersion, status, associations, 278 &mockedAssociationInterface, filePath); 279 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 280 .WillByDefault(Return( 281 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 282 activation->requestedActivation(RequestedStatus::Active); 283 284 const auto& psuQueue = getPsuQueue(); 285 EXPECT_EQ(3u, psuQueue.size()); 286 287 // Only 3 PSUs shall be updated, and psu1 shall be skipped 288 EXPECT_EQ(Status::Activating, activation->activation()); 289 EXPECT_EQ(10, getProgress()); 290 291 onUpdateDone(); 292 EXPECT_EQ(Status::Activating, activation->activation()); 293 EXPECT_EQ(36, getProgress()); 294 295 onUpdateDone(); 296 EXPECT_EQ(Status::Activating, activation->activation()); 297 EXPECT_EQ(62, getProgress()); 298 299 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 300 .Times(1); 301 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 302 .Times(1); 303 304 onUpdateDone(); 305 EXPECT_EQ(Status::Active, activation->activation()); 306 } 307