Scan directories that store PSU images on start

When the service starts, scan the directories that store PSU images,
including the built-in images, and the saved images during PSU update.

When the scanned image is different than the running images, create
activation/version object;
When the scanned image is the same as the running images, update the
version object's path to indicate the PSU image path, so it could be
used for future update in case a PSU is replaced with a different
software.

Tested: On Witherspoon, fake create a dummy PSU image with a different
        version than running PSU, verify a new object is created on
        restart;
        fake creating a dummy PSU image with a same version as a running
        PSU, verify no new object is created, but the "Path" property is
        set to the PSU image directory.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I860b978250a718eb82d948a1c88bd8f41bb2b2e3
diff --git a/test/test_item_updater.cpp b/test/test_item_updater.cpp
index b020c28..fb6ea93 100644
--- a/test/test_item_updater.cpp
+++ b/test/test_item_updater.cpp
@@ -8,6 +8,7 @@
 
 using namespace phosphor::software::updater;
 using ::testing::_;
+using ::testing::Pointee;
 using ::testing::Return;
 using ::testing::ReturnArg;
 using ::testing::StrEq;
@@ -49,6 +50,11 @@
         itemUpdater->onPsuInventoryChanged(psuPath, properties);
     }
 
+    void scanDirectory(const fs::path& p)
+    {
+        itemUpdater->scanDirectory(p);
+    }
+
     static constexpr auto dBusPath = SOFTWARE_OBJPATH;
     sdbusplus::SdBusMock sdbusMock;
     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
@@ -408,3 +414,77 @@
     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
         .Times(1);
 }
+
+TEST_F(TestItemUpdater, scanDirOnNoPSU)
+{
+    constexpr auto psuPath = "/com/example/inventory/psu0";
+    constexpr auto service = "com.example.Software.Psu";
+    constexpr auto version = "version0";
+    std::string objPath = getObjPath(version);
+    EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillOnce(Return(std::vector<std::string>({psuPath})));
+    EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
+        .WillOnce(Return(service));
+    EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
+        .WillOnce(Return(std::string(version)));
+    EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
+                                             _, StrEq(PRESENT)))
+        .WillOnce(Return(any(PropertyType(false)))); // not present
+
+    // The item updater itself
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
+        .Times(1);
+
+    // No activation/version objects are created
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+        .Times(0);
+    itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
+
+    // The valid image in test/psu-images-one-valid-one-invalid/model-1/
+    auto objPathValid = getObjPath("psu-test.v0.4");
+    auto objPathInvalid = getObjPath("psu-test.v0.5");
+    // activation and version object will be added on scan dir
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid)))
+        .Times(2);
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid)))
+        .Times(0);
+    scanDirectory("./psu-images-one-valid-one-invalid");
+}
+
+TEST_F(TestItemUpdater, scanDirOnSamePSUVersion)
+{
+    constexpr auto psuPath = "/com/example/inventory/psu0";
+    constexpr auto service = "com.example.Software.Psu";
+    constexpr auto version = "version0";
+    std::string objPath = getObjPath(version);
+    EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillOnce(Return(std::vector<std::string>({psuPath})));
+    EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
+        .WillOnce(Return(service));
+    EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
+        .WillOnce(Return(std::string(version)));
+    EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
+                                             _, StrEq(PRESENT)))
+        .WillOnce(Return(any(PropertyType(true)))); // present
+
+    // The item updater itself
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
+        .Times(1);
+
+    // activation and version object will be added
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+        .Times(2);
+    itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
+
+    // The valid image in test/psu-images-valid-version0/model-3/ has the same
+    // version as the running PSU, so no objects will be created, but only the
+    // path will be set to the version object
+    EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+        .Times(0);
+    EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv(
+                               _, StrEq(objPath),
+                               StrEq("xyz.openbmc_project.Common.FilePath"),
+                               Pointee(StrEq("Path"))))
+        .Times(1);
+    scanDirectory("./psu-images-valid-version0");
+}