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