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::Return; 12 using ::testing::ReturnArg; 13 using ::testing::StrEq; 14 15 using std::experimental::any; 16 17 class TestItemUpdater : public ::testing::Test 18 { 19 public: 20 using Properties = ItemUpdater::Properties; 21 using PropertyType = utils::UtilsInterface::PropertyType; 22 23 TestItemUpdater() : 24 mockedUtils( 25 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) 26 { 27 ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>()); 28 } 29 30 ~TestItemUpdater() 31 { 32 } 33 34 const auto& GetActivations() 35 { 36 return itemUpdater->activations; 37 } 38 39 std::string getObjPath(const std::string& versionId) 40 { 41 return std::string(dBusPath) + "/" + versionId; 42 } 43 44 void onPsuInventoryChanged(const std::string& psuPath, 45 const Properties& properties) 46 { 47 itemUpdater->onPsuInventoryChanged(psuPath, properties); 48 } 49 50 static constexpr auto dBusPath = SOFTWARE_OBJPATH; 51 sdbusplus::SdBusMock sdbusMock; 52 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock); 53 const utils::MockedUtils& mockedUtils; 54 std::unique_ptr<ItemUpdater> itemUpdater; 55 }; 56 57 TEST_F(TestItemUpdater, ctordtor) 58 { 59 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 60 } 61 62 TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent) 63 { 64 constexpr auto psuPath = "/com/example/inventory/psu0"; 65 constexpr auto service = "com.example.Software.Psu"; 66 constexpr auto version = "version0"; 67 std::string objPath = getObjPath(version); 68 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 69 .WillOnce(Return(std::vector<std::string>({psuPath}))); 70 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 71 .WillOnce(Return(service)); 72 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 73 .WillOnce(Return(std::string(version))); 74 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 75 _, StrEq(PRESENT))) 76 .WillOnce(Return(any(PropertyType(false)))); // not present 77 78 // The item updater itself 79 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 80 .Times(1); 81 82 // No activation/version objects are created 83 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 84 .Times(0); 85 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 86 } 87 88 TEST_F(TestItemUpdater, CreateOnePSUOnPresent) 89 { 90 constexpr auto psuPath = "/com/example/inventory/psu0"; 91 constexpr auto service = "com.example.Software.Psu"; 92 constexpr auto version = "version0"; 93 std::string objPath = getObjPath(version); 94 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 95 .WillOnce(Return(std::vector<std::string>({psuPath}))); 96 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 97 .WillOnce(Return(service)); 98 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 99 .WillOnce(Return(std::string(version))); 100 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 101 _, StrEq(PRESENT))) 102 .WillOnce(Return(any(PropertyType(true)))); // present 103 104 // The item updater itself 105 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 106 .Times(1); 107 108 // activation and version object will be added 109 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 110 .Times(2); 111 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 112 } 113 114 TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion) 115 { 116 constexpr auto psu0 = "/com/example/inventory/psu0"; 117 constexpr auto psu1 = "/com/example/inventory/psu1"; 118 constexpr auto service = "com.example.Software.Psu"; 119 auto version0 = std::string("version0"); 120 auto version1 = std::string("version0"); 121 auto objPath0 = getObjPath(version0); 122 auto objPath1 = getObjPath(version1); 123 124 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 125 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 126 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 127 .WillOnce(Return(service)); 128 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 129 .WillOnce(Return(service)); 130 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 131 .WillOnce(Return(std::string(version0))); 132 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 133 StrEq(PRESENT))) 134 .WillOnce(Return(any(PropertyType(true)))); // present 135 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 136 .WillOnce(Return(std::string(version1))); 137 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 138 StrEq(PRESENT))) 139 .WillOnce(Return(any(PropertyType(true)))); // present 140 141 // The item updater itself 142 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 143 .Times(1); 144 145 // activation and version object will be added 146 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 147 .Times(2); 148 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 149 150 // Verify there is only one activation and it has two associations 151 const auto& activations = GetActivations(); 152 EXPECT_EQ(1u, activations.size()); 153 const auto& activation = activations.find(version0)->second; 154 const auto& assocs = activation->associations(); 155 EXPECT_EQ(2u, assocs.size()); 156 EXPECT_EQ(psu0, std::get<2>(assocs[0])); 157 EXPECT_EQ(psu1, std::get<2>(assocs[1])); 158 } 159 160 TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion) 161 { 162 constexpr auto psu0 = "/com/example/inventory/psu0"; 163 constexpr auto psu1 = "/com/example/inventory/psu1"; 164 constexpr auto service = "com.example.Software.Psu"; 165 auto version0 = std::string("version0"); 166 auto version1 = std::string("version1"); 167 auto objPath0 = getObjPath(version0); 168 auto objPath1 = getObjPath(version1); 169 170 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 171 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 172 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 173 .WillOnce(Return(service)); 174 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 175 .WillOnce(Return(service)); 176 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 177 .WillOnce(Return(std::string(version0))); 178 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 179 StrEq(PRESENT))) 180 .WillOnce(Return(any(PropertyType(true)))); // present 181 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 182 .WillOnce(Return(std::string(version1))); 183 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 184 StrEq(PRESENT))) 185 .WillOnce(Return(any(PropertyType(true)))); // present 186 187 // The item updater itself 188 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 189 .Times(1); 190 191 // two new activation and version objects will be added 192 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 193 .Times(2); 194 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) 195 .Times(2); 196 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 197 198 // Verify there are two activations and each with one association 199 const auto& activations = GetActivations(); 200 EXPECT_EQ(2u, activations.size()); 201 const auto& activation0 = activations.find(version0)->second; 202 const auto& assocs0 = activation0->associations(); 203 EXPECT_EQ(1u, assocs0.size()); 204 EXPECT_EQ(psu0, std::get<2>(assocs0[0])); 205 206 const auto& activation1 = activations.find(version1)->second; 207 const auto& assocs1 = activation1->associations(); 208 EXPECT_EQ(1u, assocs1.size()); 209 EXPECT_EQ(psu1, std::get<2>(assocs1[0])); 210 } 211 212 TEST_F(TestItemUpdater, OnOnePSURemoved) 213 { 214 constexpr auto psuPath = "/com/example/inventory/psu0"; 215 constexpr auto service = "com.example.Software.Psu"; 216 constexpr auto version = "version0"; 217 std::string objPath = getObjPath(version); 218 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 219 .WillOnce(Return(std::vector<std::string>({psuPath}))); 220 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 221 .WillOnce(Return(service)); 222 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 223 .WillOnce(Return(std::string(version))); 224 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 225 _, StrEq(PRESENT))) 226 .WillOnce(Return(any(PropertyType(true)))); // present 227 228 // The item updater itself 229 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 230 .Times(1); 231 232 // activation and version object will be added 233 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 234 .Times(2); 235 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 236 237 // the activation and version object will be removed 238 Properties p{{PRESENT, PropertyType(false)}}; 239 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 240 .Times(2); 241 onPsuInventoryChanged(psuPath, p); 242 243 // on exit, item updater is removed 244 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 245 .Times(1); 246 } 247 248 TEST_F(TestItemUpdater, OnOnePSUAdded) 249 { 250 constexpr auto psuPath = "/com/example/inventory/psu0"; 251 constexpr auto service = "com.example.Software.Psu"; 252 constexpr auto version = "version0"; 253 std::string objPath = getObjPath(version); 254 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 255 .WillOnce(Return(std::vector<std::string>({psuPath}))); 256 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 257 .WillOnce(Return(service)); 258 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 259 .WillOnce(Return(std::string(version))); 260 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 261 _, StrEq(PRESENT))) 262 .WillOnce(Return(any(PropertyType(false)))); // not present 263 264 // The item updater itself 265 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 266 .Times(1); 267 268 // No activation/version objects are created 269 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 270 .Times(0); 271 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 272 273 // The PSU is present and version is added in a single call 274 Properties propAdded{{PRESENT, PropertyType(true)}, 275 {VERSION, PropertyType(std::string(version))}}; 276 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 277 .Times(2); 278 onPsuInventoryChanged(psuPath, propAdded); 279 } 280 281 TEST_F(TestItemUpdater, OnOnePSURemovedAndAdded) 282 { 283 constexpr auto psuPath = "/com/example/inventory/psu0"; 284 constexpr auto service = "com.example.Software.Psu"; 285 constexpr auto version = "version0"; 286 std::string objPath = getObjPath(version); 287 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 288 .WillOnce(Return(std::vector<std::string>({psuPath}))); 289 EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) 290 .WillOnce(Return(service)); 291 EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) 292 .WillOnce(Return(std::string(version))); 293 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), 294 _, StrEq(PRESENT))) 295 .WillOnce(Return(any(PropertyType(true)))); // present 296 297 // The item updater itself 298 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 299 .Times(1); 300 301 // activation and version object will be added 302 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 303 .Times(2); 304 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 305 306 // the activation and version object will be removed 307 Properties propRemoved{{PRESENT, PropertyType(false)}}; 308 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 309 .Times(2); 310 onPsuInventoryChanged(psuPath, propRemoved); 311 312 Properties propAdded{{PRESENT, PropertyType(true)}}; 313 Properties propVersion{{VERSION, PropertyType(std::string(version))}}; 314 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) 315 .Times(2); 316 onPsuInventoryChanged(psuPath, propAdded); 317 onPsuInventoryChanged(psuPath, propVersion); 318 319 // on exit, objects are removed 320 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) 321 .Times(2); 322 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 323 .Times(1); 324 } 325 326 TEST_F(TestItemUpdater, 327 TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion) 328 { 329 constexpr auto psu0 = "/com/example/inventory/psu0"; 330 constexpr auto psu1 = "/com/example/inventory/psu1"; 331 constexpr auto service = "com.example.Software.Psu"; 332 auto version0 = std::string("version0"); 333 auto version1 = std::string("version0"); 334 auto objPath0 = getObjPath(version0); 335 auto objPath1 = getObjPath(version1); 336 337 EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) 338 .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); 339 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) 340 .WillOnce(Return(service)); 341 EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) 342 .WillOnce(Return(service)); 343 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) 344 .WillOnce(Return(std::string(version0))); 345 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, 346 StrEq(PRESENT))) 347 .WillOnce(Return(any(PropertyType(true)))); // present 348 EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) 349 .WillOnce(Return(std::string(version1))); 350 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, 351 StrEq(PRESENT))) 352 .WillOnce(Return(any(PropertyType(true)))); // present 353 354 // The item updater itself 355 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) 356 .Times(1); 357 358 // activation and version object will be added 359 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 360 .Times(2); 361 itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); 362 363 // Verify there is only one activation and it has two associations 364 const auto& activations = GetActivations(); 365 EXPECT_EQ(1u, activations.size()); 366 const auto& activation = activations.find(version0)->second; 367 auto assocs = activation->associations(); 368 EXPECT_EQ(2u, assocs.size()); 369 EXPECT_EQ(psu0, std::get<2>(assocs[0])); 370 EXPECT_EQ(psu1, std::get<2>(assocs[1])); 371 372 // PSU0 is removed, only associations shall be updated 373 Properties propRemoved{{PRESENT, PropertyType(false)}}; 374 onPsuInventoryChanged(psu0, propRemoved); 375 assocs = activation->associations(); 376 EXPECT_EQ(1u, assocs.size()); 377 EXPECT_EQ(psu1, std::get<2>(assocs[0])); 378 379 // PSU1 is removed, the activation and version object shall be removed 380 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) 381 .Times(2); 382 onPsuInventoryChanged(psu1, propRemoved); 383 384 // Add PSU0 and PSU1 back, but PSU1 with a different version 385 version1 = "version1"; 386 objPath1 = getObjPath(version1); 387 Properties propAdded0{{PRESENT, PropertyType(true)}, 388 {VERSION, PropertyType(std::string(version0))}}; 389 Properties propAdded1{{PRESENT, PropertyType(true)}, 390 {VERSION, PropertyType(std::string(version1))}}; 391 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) 392 .Times(2); 393 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) 394 .Times(2); 395 onPsuInventoryChanged(psu0, propAdded0); 396 onPsuInventoryChanged(psu1, propAdded1); 397 398 // on exit, objects are removed 399 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) 400 .Times(2); 401 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1))) 402 .Times(2); 403 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) 404 .Times(1); 405 } 406