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