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