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