1 #include "activation.hpp" 2 #include "mocked_activation_listener.hpp" 3 #include "mocked_association_interface.hpp" 4 #include "mocked_utils.hpp" 5 6 #include <sdbusplus/test/sdbus_mock.hpp> 7 8 #include <gmock/gmock.h> 9 #include <gtest/gtest.h> 10 11 using namespace phosphor::software::updater; 12 13 using ::testing::_; 14 using ::testing::Return; 15 using ::testing::StrEq; 16 17 using std::any; 18 19 class TestActivation : public ::testing::Test 20 { 21 public: 22 using PropertyType = utils::UtilsInterface::PropertyType; 23 using Status = Activation::Status; 24 using RequestedStatus = Activation::RequestedActivations; 25 26 TestActivation(const TestActivation&) = delete; 27 TestActivation& operator=(const TestActivation&) = delete; 28 TestActivation(TestActivation&&) = delete; 29 TestActivation& operator=(TestActivation&&) = delete; 30 31 TestActivation() : 32 mockedUtils( 33 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) 34 { 35 // By default make it compatible with the test software 36 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 37 .WillByDefault(Return(any(PropertyType(std::string("TestManu"))))); 38 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL))) 39 .WillByDefault(Return(any(PropertyType(std::string("TestModel"))))); 40 ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false)); 41 } 42 ~TestActivation() override 43 { 44 utils::freeUtils(); 45 } 46 47 void onUpdateDone() const 48 { 49 activation->onUpdateDone(); 50 } 51 void onUpdateFailed() const 52 { 53 activation->onUpdateFailed(); 54 } 55 int getProgress() const 56 { 57 return activation->activationProgress->progress(); 58 } 59 const auto& getPsuQueue() const 60 { 61 return activation->psuQueue; 62 } 63 std::string getUpdateService(const std::string& psuInventoryPath) const 64 { 65 return activation->getUpdateService(psuInventoryPath); 66 } 67 68 sdbusplus::SdBusMock sdbusMock; 69 sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock); 70 const utils::MockedUtils& mockedUtils; 71 MockedAssociationInterface mockedAssociationInterface; 72 MockedActivationListener mockedActivationListener; 73 std::unique_ptr<Activation> activation; 74 std::string versionId = "abcdefgh"; 75 std::string extVersion = "manufacturer=TestManu,model=TestModel"; 76 std::string filePath = "/tmp/images/abcdefgh"; 77 std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId; 78 Status status = Status::Ready; 79 AssociationList associations; 80 }; 81 82 TEST_F(TestActivation, ctordtor) 83 { 84 activation = std::make_unique<Activation>( 85 mockedBus, dBusPath, versionId, extVersion, status, associations, 86 filePath, &mockedAssociationInterface, &mockedActivationListener); 87 } 88 89 TEST_F(TestActivation, ctorWithInvalidExtVersion) 90 { 91 extVersion = "invalid text"; 92 activation = std::make_unique<Activation>( 93 mockedBus, dBusPath, versionId, extVersion, status, associations, 94 filePath, &mockedAssociationInterface, &mockedActivationListener); 95 } 96 97 TEST_F(TestActivation, getUpdateService) 98 { 99 std::string psuInventoryPath = "/com/example/inventory/powersupply1"; 100 std::string toCompare = "psu-update@-com-example-inventory-" 101 "powersupply1\\x20-tmp-images-12345678.service"; 102 versionId = "12345678"; 103 filePath = "/tmp/images/12345678"; 104 105 activation = std::make_unique<Activation>( 106 mockedBus, dBusPath, versionId, extVersion, status, associations, 107 filePath, &mockedAssociationInterface, &mockedActivationListener); 108 109 auto service = getUpdateService(psuInventoryPath); 110 EXPECT_EQ(toCompare, service); 111 } 112 113 TEST_F(TestActivation, doUpdateWhenNoPSU) 114 { 115 activation = std::make_unique<Activation>( 116 mockedBus, dBusPath, versionId, extVersion, status, associations, 117 filePath, &mockedAssociationInterface, &mockedActivationListener); 118 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 119 .WillByDefault( 120 Return(std::vector<std::string>({}))); // No PSU inventory 121 activation->requestedActivation(RequestedStatus::Active); 122 123 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 124 .Times(0); 125 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 126 .Times(0); 127 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 128 .Times(0); 129 EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0); 130 EXPECT_EQ(Status::Failed, activation->activation()); 131 } 132 133 TEST_F(TestActivation, doUpdateOnePSUOK) 134 { 135 constexpr auto psu0 = "/com/example/inventory/psu0"; 136 activation = std::make_unique<Activation>( 137 mockedBus, dBusPath, versionId, extVersion, status, associations, 138 filePath, &mockedAssociationInterface, &mockedActivationListener); 139 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 140 .WillByDefault( 141 Return(std::vector<std::string>({psu0}))); // One PSU inventory 142 activation->requestedActivation(RequestedStatus::Active); 143 144 EXPECT_EQ(Status::Activating, activation->activation()); 145 146 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 147 .Times(1); 148 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 149 .Times(1); 150 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 151 .Times(1); 152 EXPECT_CALL(mockedActivationListener, 153 onUpdateDone(StrEq(versionId), StrEq(psu0))) 154 .Times(1); 155 onUpdateDone(); 156 EXPECT_EQ(Status::Active, activation->activation()); 157 } 158 159 TEST_F(TestActivation, doUpdateFourPSUsOK) 160 { 161 constexpr auto psu0 = "/com/example/inventory/psu0"; 162 constexpr auto psu1 = "/com/example/inventory/psu1"; 163 constexpr auto psu2 = "/com/example/inventory/psu2"; 164 constexpr auto psu3 = "/com/example/inventory/psu3"; 165 activation = std::make_unique<Activation>( 166 mockedBus, dBusPath, versionId, extVersion, status, associations, 167 filePath, &mockedAssociationInterface, &mockedActivationListener); 168 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 169 .WillByDefault(Return( 170 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 171 activation->requestedActivation(RequestedStatus::Active); 172 173 EXPECT_EQ(Status::Activating, activation->activation()); 174 EXPECT_EQ(10, getProgress()); 175 176 EXPECT_CALL(mockedActivationListener, 177 onUpdateDone(StrEq(versionId), StrEq(psu0))) 178 .Times(1); 179 onUpdateDone(); 180 EXPECT_EQ(Status::Activating, activation->activation()); 181 EXPECT_EQ(30, getProgress()); 182 183 EXPECT_CALL(mockedActivationListener, 184 onUpdateDone(StrEq(versionId), StrEq(psu1))) 185 .Times(1); 186 onUpdateDone(); 187 EXPECT_EQ(Status::Activating, activation->activation()); 188 EXPECT_EQ(50, getProgress()); 189 190 EXPECT_CALL(mockedActivationListener, 191 onUpdateDone(StrEq(versionId), StrEq(psu2))) 192 .Times(1); 193 onUpdateDone(); 194 EXPECT_EQ(Status::Activating, activation->activation()); 195 EXPECT_EQ(70, getProgress()); 196 197 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 198 .Times(1); 199 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 200 .Times(1); 201 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 202 .Times(1); 203 204 EXPECT_CALL(mockedActivationListener, 205 onUpdateDone(StrEq(versionId), StrEq(psu3))) 206 .Times(1); 207 onUpdateDone(); 208 EXPECT_EQ(Status::Active, activation->activation()); 209 } 210 211 TEST_F(TestActivation, doUpdateFourPSUsFailonSecond) 212 { 213 constexpr auto psu0 = "/com/example/inventory/psu0"; 214 constexpr auto psu1 = "/com/example/inventory/psu1"; 215 constexpr auto psu2 = "/com/example/inventory/psu2"; 216 constexpr auto psu3 = "/com/example/inventory/psu3"; 217 activation = std::make_unique<Activation>( 218 mockedBus, dBusPath, versionId, extVersion, status, associations, 219 filePath, &mockedAssociationInterface, &mockedActivationListener); 220 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 221 .WillByDefault(Return( 222 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 223 activation->requestedActivation(RequestedStatus::Active); 224 225 EXPECT_EQ(Status::Activating, activation->activation()); 226 EXPECT_EQ(10, getProgress()); 227 228 EXPECT_CALL(mockedActivationListener, 229 onUpdateDone(StrEq(versionId), StrEq(psu0))) 230 .Times(1); 231 onUpdateDone(); 232 EXPECT_EQ(Status::Activating, activation->activation()); 233 EXPECT_EQ(30, getProgress()); 234 235 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 236 .Times(0); 237 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 238 .Times(0); 239 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 240 .Times(0); 241 EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0); 242 onUpdateFailed(); 243 EXPECT_EQ(Status::Failed, activation->activation()); 244 } 245 246 TEST_F(TestActivation, doUpdateOnExceptionFromDbus) 247 { 248 constexpr auto psu0 = "/com/example/inventory/psu0"; 249 activation = std::make_unique<Activation>( 250 mockedBus, dBusPath, versionId, extVersion, status, associations, 251 filePath, &mockedAssociationInterface, &mockedActivationListener); 252 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 253 .WillByDefault( 254 Return(std::vector<std::string>({psu0}))); // One PSU inventory 255 ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr)) 256 .WillByDefault(Return(-1)); // Make sdbus call failure 257 activation->requestedActivation(RequestedStatus::Active); 258 259 EXPECT_EQ(Status::Failed, activation->activation()); 260 } 261 262 TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible) 263 { 264 constexpr auto psu0 = "/com/example/inventory/psu0"; 265 extVersion = "manufacturer=TestManu,model=DifferentModel"; 266 activation = std::make_unique<Activation>( 267 mockedBus, dBusPath, versionId, extVersion, status, associations, 268 filePath, &mockedAssociationInterface, &mockedActivationListener); 269 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 270 .WillByDefault(Return(std::vector<std::string>({psu0}))); 271 activation->requestedActivation(RequestedStatus::Active); 272 273 EXPECT_EQ(Status::Ready, activation->activation()); 274 } 275 276 TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible) 277 { 278 constexpr auto psu0 = "/com/example/inventory/psu0"; 279 extVersion = "manufacturer=DifferentManu,model=TestModel"; 280 activation = std::make_unique<Activation>( 281 mockedBus, dBusPath, versionId, extVersion, status, associations, 282 filePath, &mockedAssociationInterface, &mockedActivationListener); 283 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 284 .WillByDefault(Return(std::vector<std::string>({psu0}))); 285 activation->requestedActivation(RequestedStatus::Active); 286 287 EXPECT_EQ(Status::Ready, activation->activation()); 288 } 289 290 TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty) 291 { 292 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) 293 .WillByDefault(Return(any(PropertyType(std::string(""))))); 294 extVersion = "manufacturer=AnyManu,model=TestModel"; 295 // Below is the same as doUpdateOnePSUOK case 296 constexpr auto psu0 = "/com/example/inventory/psu0"; 297 activation = std::make_unique<Activation>( 298 mockedBus, dBusPath, versionId, extVersion, status, associations, 299 filePath, &mockedAssociationInterface, &mockedActivationListener); 300 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 301 .WillByDefault( 302 Return(std::vector<std::string>({psu0}))); // One PSU inventory 303 activation->requestedActivation(RequestedStatus::Active); 304 305 EXPECT_EQ(Status::Activating, activation->activation()); 306 307 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 308 .Times(1); 309 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 310 .Times(1); 311 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 312 .Times(1); 313 onUpdateDone(); 314 EXPECT_EQ(Status::Active, activation->activation()); 315 } 316 317 TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible) 318 { 319 constexpr auto psu0 = "/com/example/inventory/psu0"; 320 constexpr auto psu1 = "/com/example/inventory/psu1"; 321 constexpr auto psu2 = "/com/example/inventory/psu2"; 322 constexpr auto psu3 = "/com/example/inventory/psu3"; 323 ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL))) 324 .WillByDefault( 325 Return(any(PropertyType(std::string("DifferentModel"))))); 326 activation = std::make_unique<Activation>( 327 mockedBus, dBusPath, versionId, extVersion, status, associations, 328 filePath, &mockedAssociationInterface, &mockedActivationListener); 329 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 330 .WillByDefault(Return( 331 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs 332 activation->requestedActivation(RequestedStatus::Active); 333 334 const auto& psuQueue = getPsuQueue(); 335 EXPECT_EQ(3U, psuQueue.size()); 336 337 // Only 3 PSUs shall be updated, and psu1 shall be skipped 338 EXPECT_EQ(Status::Activating, activation->activation()); 339 EXPECT_EQ(10, getProgress()); 340 341 onUpdateDone(); 342 EXPECT_EQ(Status::Activating, activation->activation()); 343 EXPECT_EQ(36, getProgress()); 344 345 onUpdateDone(); 346 EXPECT_EQ(Status::Activating, activation->activation()); 347 EXPECT_EQ(62, getProgress()); 348 349 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) 350 .Times(1); 351 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) 352 .Times(1); 353 EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) 354 .Times(1); 355 356 onUpdateDone(); 357 EXPECT_EQ(Status::Active, activation->activation()); 358 } 359 360 TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState) 361 { 362 filePath = ""; 363 status = Status::Active; // Typically, a running PSU software is active 364 // without file path 365 constexpr auto psu0 = "/com/example/inventory/psu0"; 366 activation = std::make_unique<Activation>( 367 mockedBus, dBusPath, versionId, extVersion, status, associations, 368 filePath, &mockedAssociationInterface, &mockedActivationListener); 369 370 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 371 .WillByDefault( 372 Return(std::vector<std::string>({psu0}))); // One PSU inventory 373 374 // There shall be no DBus call to start update service 375 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, 376 StrEq("StartUnit"))) 377 .Times(0); 378 379 activation->requestedActivation(RequestedStatus::Active); 380 EXPECT_EQ(Status::Active, activation->activation()); 381 } 382 383 TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState) 384 { 385 filePath = ""; 386 status = Status::Ready; // Usually a Ready activation should have file path, 387 // but we are testing this case as well 388 constexpr auto psu0 = "/com/example/inventory/psu0"; 389 activation = std::make_unique<Activation>( 390 mockedBus, dBusPath, versionId, extVersion, status, associations, 391 filePath, &mockedAssociationInterface, &mockedActivationListener); 392 393 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 394 .WillByDefault( 395 Return(std::vector<std::string>({psu0}))); // One PSU inventory 396 397 // There shall be no DBus call to start update service 398 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, 399 StrEq("StartUnit"))) 400 .Times(0); 401 402 activation->requestedActivation(RequestedStatus::Active); 403 EXPECT_EQ(Status::Ready, activation->activation()); 404 } 405 406 TEST_F(TestActivation, doUpdateWhenPSUIsAssociated) 407 { 408 constexpr auto psu0 = "/com/example/inventory/psu0"; 409 status = Status::Active; // Typically, a running PSU software is associated 410 activation = std::make_unique<Activation>( 411 mockedBus, dBusPath, versionId, extVersion, status, associations, 412 filePath, &mockedAssociationInterface, &mockedActivationListener); 413 414 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 415 .WillByDefault( 416 Return(std::vector<std::string>({psu0}))); // One PSU inventory 417 418 // When PSU is already associated, there shall be no DBus call to start 419 // update service 420 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) 421 .WillOnce(Return(true)); 422 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, 423 StrEq("StartUnit"))) 424 .Times(0); 425 426 activation->requestedActivation(RequestedStatus::Active); 427 EXPECT_EQ(Status::Active, activation->activation()); 428 } 429