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