1 #include "item_updater.hpp" 2 #include "mocked_utils.hpp" 3 4 #include <sdbusplus/test/sdbus_mock.hpp> 5 6 #include <gmock/gmock.h> 7 #include <gtest/gtest.h> 8 9 using namespace phosphor::software::updater; 10 using ::testing::_; 11 using ::testing::ContainerEq; 12 using ::testing::Pointee; 13 using ::testing::Return; 14 using ::testing::ReturnArg; 15 using ::testing::StrEq; 16 17 using std::experimental::any; 18 19 class TestItemUpdater : public ::testing::Test 20 { 21 public: 22 using Properties = ItemUpdater::Properties; 23 using PropertyType = utils::UtilsInterface::PropertyType; 24 25 TestItemUpdater() : 26 mockedUtils( 27 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) 28 { 29 ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>()); 30 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT))) 31 .WillByDefault(Return(any(PropertyType(true)))); 32 } 33 34 ~TestItemUpdater() 35 { 36 utils::freeUtils(); 37 } 38 39 auto& GetActivations() 40 { 41 return itemUpdater->activations; 42 } 43 44 std::string getObjPath(const std::string& versionId) 45 { 46 return std::string(dBusPath) + "/" + versionId; 47 } 48 49 void onPsuInventoryChanged(const std::string& psuPath, 50 const Properties& properties) 51 { 52 itemUpdater->onPsuInventoryChanged(psuPath, properties); 53 } 54 55 void scanDirectory(const fs::path& p) 56 { 57 itemUpdater->scanDirectory(p); 58 } 59 60 static constexpr auto dBusPath = SOFTWARE_OBJPATH; 61 sdbusplus::SdBusMock sdbusMock; 62 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock); 63 const utils::MockedUtils& mockedUtils; 64 std::unique_ptr<ItemUpdater> itemUpdater; 65 Properties propAdded{{PRESENT, PropertyType(true)}}; 66 Properties propRemoved{{PRESENT, PropertyType(false)}}; 67 Properties propModel{{MODEL, PropertyType(std::string("dummyModel"))}}; 68 }; 69 70 TEST_F(TestItemUpdater, ctordtor) 71 { 72 EXPECT_CALL(mockedUtils, getLatestVersion(_)).Times(1); 73 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 74 } 75 76 TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent) 77 { 78 constexpr auto psuPath = "/com/example/inventory/psu0"; 79 constexpr auto service = "com.example.Software.Psu"; 80 constexpr auto version = "version0"; 81 std::string objPath = getObjPath(version); 82 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 83 .WillOnce(Return(std::vector<std::string>({psuPath}))); 84 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 85 .WillOnce(Return(service)); 86 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 87 .WillOnce(Return(std::string(version))); 88 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 89 _, StrEq(PRESENT))) 90 .WillOnce(Return(any(PropertyType(false)))); // not present 91 92 // The item updater itself 93 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 94 .Times(1); 95 96 // No activation/version objects are created 97 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 98 .Times(0); 99 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 100 } 101 102 TEST_F(TestItemUpdater, CreateOnePSUOnPresent) 103 { 104 constexpr auto psuPath = "/com/example/inventory/psu0"; 105 constexpr auto service = "com.example.Software.Psu"; 106 constexpr auto version = "version0"; 107 std::string objPath = getObjPath(version); 108 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 109 .WillOnce(Return(std::vector<std::string>({psuPath}))); 110 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 111 .WillOnce(Return(service)); 112 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 113 .WillOnce(Return(std::string(version))); 114 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 115 _, StrEq(PRESENT))) 116 .WillOnce(Return(any(PropertyType(true)))); // present 117 118 // The item updater itself 119 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 120 .Times(1); 121 122 // activation and version object will be added 123 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 124 .Times(2); 125 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 126 } 127 128 TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion) 129 { 130 constexpr auto psu0 = "/com/example/inventory/psu0"; 131 constexpr auto psu1 = "/com/example/inventory/psu1"; 132 constexpr auto service = "com.example.Software.Psu"; 133 auto version0 = std::string("version0"); 134 auto version1 = std::string("version0"); 135 auto objPath0 = getObjPath(version0); 136 auto objPath1 = getObjPath(version1); 137 138 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 139 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 140 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 141 .WillOnce(Return(service)); 142 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 143 .WillOnce(Return(service)); 144 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 145 .WillOnce(Return(std::string(version0))); 146 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 147 StrEq(PRESENT))) 148 .WillOnce(Return(any(PropertyType(true)))); // present 149 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 150 .WillOnce(Return(std::string(version1))); 151 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 152 StrEq(PRESENT))) 153 .WillOnce(Return(any(PropertyType(true)))); // present 154 155 // The item updater itself 156 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 157 .Times(1); 158 159 // activation and version object will be added 160 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 161 .Times(2); 162 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 163 164 // Verify there is only one activation and it has two associations 165 const auto& activations = GetActivations(); 166 EXPECT_EQ(1u, activations.size()); 167 const auto& activation = activations.find(version0)->second; 168 const auto& assocs = activation->associations(); 169 EXPECT_EQ(2u, assocs.size()); 170 EXPECT_EQ(psu0, std::get<2>(assocs[0])); 171 EXPECT_EQ(psu1, std::get<2>(assocs[1])); 172 } 173 174 TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion) 175 { 176 constexpr auto psu0 = "/com/example/inventory/psu0"; 177 constexpr auto psu1 = "/com/example/inventory/psu1"; 178 constexpr auto service = "com.example.Software.Psu"; 179 auto version0 = std::string("version0"); 180 auto version1 = std::string("version1"); 181 auto objPath0 = getObjPath(version0); 182 auto objPath1 = getObjPath(version1); 183 184 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 185 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 186 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 187 .WillOnce(Return(service)); 188 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 189 .WillOnce(Return(service)); 190 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 191 .WillOnce(Return(std::string(version0))); 192 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 193 StrEq(PRESENT))) 194 .WillOnce(Return(any(PropertyType(true)))); // present 195 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 196 .WillOnce(Return(std::string(version1))); 197 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 198 StrEq(PRESENT))) 199 .WillOnce(Return(any(PropertyType(true)))); // present 200 201 // The item updater itself 202 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 203 .Times(1); 204 205 // two new activation and version objects will be added 206 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 207 .Times(2); 208 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) 209 .Times(2); 210 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 211 212 // Verify there are two activations and each with one association 213 const auto& activations = GetActivations(); 214 EXPECT_EQ(2u, activations.size()); 215 const auto& activation0 = activations.find(version0)->second; 216 const auto& assocs0 = activation0->associations(); 217 EXPECT_EQ(1u, assocs0.size()); 218 EXPECT_EQ(psu0, std::get<2>(assocs0[0])); 219 220 const auto& activation1 = activations.find(version1)->second; 221 const auto& assocs1 = activation1->associations(); 222 EXPECT_EQ(1u, assocs1.size()); 223 EXPECT_EQ(psu1, std::get<2>(assocs1[0])); 224 } 225 226 TEST_F(TestItemUpdater, OnOnePSURemoved) 227 { 228 constexpr auto psuPath = "/com/example/inventory/psu0"; 229 constexpr auto service = "com.example.Software.Psu"; 230 constexpr auto version = "version0"; 231 std::string objPath = getObjPath(version); 232 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 233 .WillOnce(Return(std::vector<std::string>({psuPath}))); 234 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 235 .WillOnce(Return(service)); 236 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 237 .WillOnce(Return(std::string(version))); 238 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 239 _, StrEq(PRESENT))) 240 .WillOnce(Return(any(PropertyType(true)))); // present 241 242 // The item updater itself 243 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 244 .Times(1); 245 246 // activation and version object will be added 247 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 248 .Times(2); 249 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 250 251 // the activation and version object will be removed 252 Properties p{{PRESENT, PropertyType(false)}}; 253 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 254 .Times(2); 255 onPsuInventoryChanged(psuPath, p); 256 257 // on exit, item updater is removed 258 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 259 .Times(1); 260 } 261 262 TEST_F(TestItemUpdater, OnOnePSUAdded) 263 { 264 constexpr auto psuPath = "/com/example/inventory/psu0"; 265 constexpr auto service = "com.example.Software.Psu"; 266 constexpr auto version = "version0"; 267 std::string objPath = getObjPath(version); 268 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 269 .WillOnce(Return(std::vector<std::string>({psuPath}))); 270 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 271 .WillOnce(Return(service)); 272 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 273 .WillOnce(Return(std::string(version))); 274 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 275 _, StrEq(PRESENT))) 276 .WillOnce(Return(any(PropertyType(false)))); // not present 277 278 // The item updater itself 279 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 280 .Times(1); 281 282 // No activation/version objects are created 283 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 284 .Times(0); 285 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 286 287 // The PSU is present and version is added in a single call 288 Properties propAddedAndModel{ 289 {PRESENT, PropertyType(true)}, 290 {MODEL, PropertyType(std::string("testModel"))}}; 291 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 292 .WillOnce(Return(std::string(version))); 293 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 294 .Times(2); 295 onPsuInventoryChanged(psuPath, propAddedAndModel); 296 } 297 298 TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithLatestVersion) 299 { 300 constexpr auto psuPath = "/com/example/inventory/psu0"; 301 constexpr auto service = "com.example.Software.Psu"; 302 constexpr auto version = "version0"; 303 std::string objPath = getObjPath(version); 304 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 305 .WillByDefault(Return(std::vector<std::string>({psuPath}))); 306 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 307 .WillOnce(Return(service)); 308 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 309 .WillOnce(Return(std::string(version))); 310 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 311 _, StrEq(PRESENT))) 312 .WillOnce(Return(any(PropertyType(true)))); // present 313 314 // The item updater itself 315 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 316 .Times(1); 317 318 // activation and version object will be added 319 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 320 .Times(2); 321 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 322 323 // the activation and version object will be removed 324 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 325 .Times(2); 326 onPsuInventoryChanged(psuPath, propRemoved); 327 328 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 329 .WillOnce(Return(std::string(version))); 330 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 331 .Times(2); 332 333 // On PSU inserted, it shall check if it's the latest version 334 std::set<std::string> expectedVersions = {version}; 335 EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions))) 336 .WillOnce(Return(version)); 337 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psuPath), _)) 338 .WillOnce(Return(true)); 339 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, 340 StrEq("StartUnit"))) 341 .Times(0); 342 onPsuInventoryChanged(psuPath, propAdded); 343 onPsuInventoryChanged(psuPath, propModel); 344 345 // on exit, objects are removed 346 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 347 .Times(2); 348 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 349 .Times(1); 350 } 351 352 TEST_F(TestItemUpdater, 353 TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion) 354 { 355 constexpr auto psu0 = "/com/example/inventory/psu0"; 356 constexpr auto psu1 = "/com/example/inventory/psu1"; 357 constexpr auto service = "com.example.Software.Psu"; 358 auto version0 = std::string("version0"); 359 auto version1 = std::string("version0"); 360 auto objPath0 = getObjPath(version0); 361 auto objPath1 = getObjPath(version1); 362 363 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 364 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 365 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 366 .WillOnce(Return(service)); 367 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 368 .WillOnce(Return(service)); 369 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 370 .WillOnce(Return(std::string(version0))); 371 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 372 StrEq(PRESENT))) 373 .WillOnce(Return(any(PropertyType(true)))); // present 374 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 375 .WillOnce(Return(std::string(version1))); 376 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 377 StrEq(PRESENT))) 378 .WillOnce(Return(any(PropertyType(true)))); // present 379 380 // The item updater itself 381 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 382 .Times(1); 383 384 // activation and version object will be added 385 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 386 .Times(2); 387 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 388 389 // Verify there is only one activation and it has two associations 390 const auto& activations = GetActivations(); 391 EXPECT_EQ(1u, activations.size()); 392 const auto& activation = activations.find(version0)->second; 393 auto assocs = activation->associations(); 394 EXPECT_EQ(2u, assocs.size()); 395 EXPECT_EQ(psu0, std::get<2>(assocs[0])); 396 EXPECT_EQ(psu1, std::get<2>(assocs[1])); 397 398 // PSU0 is removed, only associations shall be updated 399 onPsuInventoryChanged(psu0, propRemoved); 400 assocs = activation->associations(); 401 EXPECT_EQ(1u, assocs.size()); 402 EXPECT_EQ(psu1, std::get<2>(assocs[0])); 403 404 // PSU1 is removed, the activation and version object shall be removed 405 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) 406 .Times(2); 407 onPsuInventoryChanged(psu1, propRemoved); 408 409 // Add PSU0 and PSU1 back, but PSU1 with a different version 410 version1 = "version1"; 411 objPath1 = getObjPath(version1); 412 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 413 .WillOnce(Return(std::string(version0))); 414 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 415 .WillOnce(Return(std::string(version1))); 416 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 417 .Times(2); 418 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) 419 .Times(2); 420 onPsuInventoryChanged(psu0, propAdded); 421 onPsuInventoryChanged(psu1, propModel); 422 onPsuInventoryChanged(psu1, propAdded); 423 onPsuInventoryChanged(psu0, propModel); 424 425 // on exit, objects are removed 426 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) 427 .Times(2); 428 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1))) 429 .Times(2); 430 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 431 .Times(1); 432 } 433 434 TEST_F(TestItemUpdater, scanDirOnNoPSU) 435 { 436 constexpr auto psuPath = "/com/example/inventory/psu0"; 437 constexpr auto service = "com.example.Software.Psu"; 438 constexpr auto version = "version0"; 439 std::string objPath = getObjPath(version); 440 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 441 .WillOnce(Return(std::vector<std::string>({psuPath}))); 442 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 443 .WillOnce(Return(service)); 444 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 445 .WillOnce(Return(std::string(version))); 446 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 447 _, StrEq(PRESENT))) 448 .WillOnce(Return(any(PropertyType(false)))); // not present 449 450 // The item updater itself 451 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 452 .Times(1); 453 454 // No activation/version objects are created 455 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 456 .Times(0); 457 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 458 459 // The valid image in test/psu-images-one-valid-one-invalid/model-1/ 460 auto objPathValid = getObjPath("psu-test.v0.4"); 461 auto objPathInvalid = getObjPath("psu-test.v0.5"); 462 // activation and version object will be added on scan dir 463 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid))) 464 .Times(2); 465 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid))) 466 .Times(0); 467 scanDirectory("./psu-images-one-valid-one-invalid"); 468 } 469 470 TEST_F(TestItemUpdater, scanDirOnSamePSUVersion) 471 { 472 constexpr auto psuPath = "/com/example/inventory/psu0"; 473 constexpr auto service = "com.example.Software.Psu"; 474 constexpr auto version = "version0"; 475 std::string objPath = getObjPath(version); 476 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 477 .WillOnce(Return(std::vector<std::string>({psuPath}))); 478 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 479 .WillOnce(Return(service)); 480 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 481 .WillOnce(Return(std::string(version))); 482 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 483 _, StrEq(PRESENT))) 484 .WillOnce(Return(any(PropertyType(true)))); // present 485 486 // The item updater itself 487 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 488 .Times(1); 489 490 // activation and version object will be added 491 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 492 .Times(2); 493 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 494 495 // The valid image in test/psu-images-valid-version0/model-3/ has the same 496 // version as the running PSU, so no objects will be created, but only the 497 // path will be set to the version object 498 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 499 .Times(0); 500 EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv( 501 _, StrEq(objPath), 502 StrEq("xyz.openbmc_project.Common.FilePath"), 503 Pointee(StrEq("Path")))) 504 .Times(1); 505 scanDirectory("./psu-images-valid-version0"); 506 } 507 508 TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithSameVersion) 509 { 510 // Simulate there are two PSUs with same version, and updated to a new 511 // version 512 constexpr auto psu0 = "/com/example/inventory/psu0"; 513 constexpr auto psu1 = "/com/example/inventory/psu1"; 514 constexpr auto service = "com.example.Software.Psu"; 515 auto version0 = std::string("version0"); 516 auto version1 = std::string("version0"); 517 auto objPath0 = getObjPath(version0); 518 auto objPath1 = getObjPath(version1); 519 520 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 521 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 522 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 523 .WillOnce(Return(service)); 524 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 525 .WillOnce(Return(service)); 526 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 527 .WillOnce(Return(std::string(version0))); 528 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 529 StrEq(PRESENT))) 530 .WillOnce(Return(any(PropertyType(true)))); // present 531 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 532 .WillOnce(Return(std::string(version1))); 533 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 534 StrEq(PRESENT))) 535 .WillOnce(Return(any(PropertyType(true)))); // present 536 537 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 538 539 std::string newVersionId = "NewVersionId"; 540 AssociationList associations; 541 auto dummyActivation = std::make_unique<Activation>( 542 mockedBus, dBusPath, newVersionId, "", Activation::Status::Active, 543 associations, "", itemUpdater.get(), itemUpdater.get()); 544 545 // Now there is one activation and it has two associations 546 auto& activations = GetActivations(); 547 activations.emplace(newVersionId, std::move(dummyActivation)); 548 auto& activation = activations.find(version0)->second; 549 auto assocs = activation->associations(); 550 EXPECT_EQ(2u, assocs.size()); 551 EXPECT_EQ(psu0, std::get<2>(assocs[0])); 552 EXPECT_EQ(psu1, std::get<2>(assocs[1])); 553 554 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) 555 .WillOnce(Return(true)); 556 itemUpdater->onUpdateDone(newVersionId, psu0); 557 558 // Now the activation should have one assoiation 559 assocs = activation->associations(); 560 EXPECT_EQ(1u, assocs.size()); 561 EXPECT_EQ(psu1, std::get<2>(assocs[0])); 562 563 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _)) 564 .WillOnce(Return(true)); 565 itemUpdater->onUpdateDone(newVersionId, psu1); 566 567 // Now the activation shall be erased and only the dummy one is left 568 EXPECT_EQ(1u, activations.size()); 569 EXPECT_NE(activations.find(newVersionId), activations.end()); 570 } 571 572 TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithDifferentVersion) 573 { 574 // Simulate there are two PSUs with different version, and updated to a new 575 // version 576 constexpr auto psu0 = "/com/example/inventory/psu0"; 577 constexpr auto psu1 = "/com/example/inventory/psu1"; 578 constexpr auto service = "com.example.Software.Psu"; 579 auto version0 = std::string("version0"); 580 auto version1 = std::string("version1"); 581 auto objPath0 = getObjPath(version0); 582 auto objPath1 = getObjPath(version1); 583 584 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 585 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 586 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 587 .WillOnce(Return(service)); 588 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 589 .WillOnce(Return(service)); 590 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 591 .WillOnce(Return(std::string(version0))); 592 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 593 StrEq(PRESENT))) 594 .WillOnce(Return(any(PropertyType(true)))); // present 595 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 596 .WillOnce(Return(std::string(version1))); 597 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 598 StrEq(PRESENT))) 599 .WillOnce(Return(any(PropertyType(true)))); // present 600 601 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 602 603 std::string newVersionId = "NewVersionId"; 604 AssociationList associations; 605 auto dummyActivation = std::make_unique<Activation>( 606 mockedBus, dBusPath, newVersionId, "", Activation::Status::Active, 607 associations, "", itemUpdater.get(), itemUpdater.get()); 608 609 auto& activations = GetActivations(); 610 activations.emplace(newVersionId, std::move(dummyActivation)); 611 612 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) 613 .WillOnce(Return(true)); 614 615 // After psu0 is done, two activations should be left 616 itemUpdater->onUpdateDone(newVersionId, psu0); 617 EXPECT_EQ(2u, activations.size()); 618 const auto& activation1 = activations.find(version1)->second; 619 const auto& assocs1 = activation1->associations(); 620 EXPECT_EQ(1u, assocs1.size()); 621 EXPECT_EQ(psu1, std::get<2>(assocs1[0])); 622 623 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _)) 624 .WillOnce(Return(true)); 625 // After psu1 is done, only the dummy activation should be left 626 itemUpdater->onUpdateDone(newVersionId, psu1); 627 EXPECT_EQ(1u, activations.size()); 628 EXPECT_NE(activations.find(newVersionId), activations.end()); 629 } 630 631 TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithOldVersion) 632 { 633 constexpr auto psuPath = "/com/example/inventory/psu0"; 634 constexpr auto service = "com.example.Software.Psu"; 635 constexpr auto version = "version0"; 636 std::string versionId = 637 version; // In testing versionId is the same as version 638 std::string objPath = getObjPath(version); 639 ON_CALL(mockedUtils, getPSUInventoryPath(_)) 640 .WillByDefault(Return(std::vector<std::string>({psuPath}))); 641 ON_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 642 .WillByDefault(Return(service)); 643 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 644 .WillOnce(Return(std::string(version))); 645 ON_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, 646 StrEq(PRESENT))) 647 .WillByDefault(Return(any(PropertyType(true)))); // present 648 649 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 650 651 // Add an association to simulate that it has image in BMC filesystem 652 auto& activation = GetActivations().find(versionId)->second; 653 auto assocs = activation->associations(); 654 assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 655 "SomePath"); 656 activation->associations(assocs); 657 activation->path("SomeFilePath"); 658 659 onPsuInventoryChanged(psuPath, propRemoved); 660 661 // On PSU inserted, it checks and finds a newer version 662 auto oldVersion = "old-version"; 663 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 664 .WillOnce(Return(std::string(oldVersion))); 665 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 666 _, StrEq(MANUFACTURER))) 667 .WillOnce( 668 Return(any(PropertyType(std::string(""))))); // Checking compatible 669 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 670 _, StrEq(MODEL))) 671 .WillOnce( 672 Return(any(PropertyType(std::string(""))))); // Checking compatible 673 std::set<std::string> expectedVersions = {version, oldVersion}; 674 EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions))) 675 .WillOnce(Return(version)); 676 ON_CALL(mockedUtils, isAssociated(StrEq(psuPath), _)) 677 .WillByDefault(Return(false)); 678 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, 679 StrEq("StartUnit"))) 680 .Times(1); 681 onPsuInventoryChanged(psuPath, propAdded); 682 onPsuInventoryChanged(psuPath, propModel); 683 } 684