power-utils: Initially use i2c in updater

Create I2CDevice in updater and invoke read() in doUpdate(), that could
be used in future.
Use mocked I2CInterface in updater's unit test case.

Tested: Manually verify on Witherspoon that the i2c device is opened
        and closed during PSU code update.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: Ie3d9f0565a2ceb000f489647a58ca967a2ef0c38
diff --git a/tools/power-utils/test/meson.build b/tools/power-utils/test/meson.build
index 45052ad..1f23ad0 100644
--- a/tools/power-utils/test/meson.build
+++ b/tools/power-utils/test/meson.build
@@ -9,7 +9,7 @@
             phosphor_logging,
         ],
         implicit_include_directories: false,
-        include_directories: '../../..',
+        include_directories: libpower_inc,
         link_with: [
             libpower,
         ],
@@ -25,10 +25,11 @@
         '../updater.cpp',
         dependencies: [
             gtest,
+            gmock,
             phosphor_logging,
         ],
         implicit_include_directories: false,
-        include_directories: '../../..',
+        include_directories: [libpower_inc, libi2c_inc],
         link_with: [
             libpower,
         ],
diff --git a/tools/power-utils/test/test_updater.cpp b/tools/power-utils/test/test_updater.cpp
index 2786986..4d051f7 100644
--- a/tools/power-utils/test/test_updater.cpp
+++ b/tools/power-utils/test/test_updater.cpp
@@ -14,22 +14,92 @@
  * limitations under the License.
  */
 #include "../updater.hpp"
+#include "test/mocked_i2c_interface.hpp"
+
+#include <filesystem>
 
 #include <gtest/gtest.h>
 
+namespace fs = std::filesystem;
+
+using ::testing::_;
+using ::testing::An;
+
 namespace updater
 {
 namespace internal
 {
 
 std::string getDeviceName(std::string devPath);
+std::pair<uint8_t, uint8_t> parseDeviceName(const std::string& devName);
 
 } // namespace internal
 } // namespace updater
 
 using namespace updater;
 
-TEST(Updater, getDeviceName)
+class TestUpdater : public ::testing::Test
+{
+  public:
+    using MockedI2CInterface = i2c::MockedI2CInterface;
+    using I2CInterface = i2c::I2CInterface;
+
+    TestUpdater()
+    {
+        setupDeviceSysfs();
+    }
+    ~TestUpdater()
+    {
+        fs::remove_all(tmpDir);
+    }
+
+    void setupDeviceSysfs()
+    {
+        auto tmpPath = fs::temp_directory_path();
+        tmpDir = (tmpPath / "test_XXXXXX");
+        if (!mkdtemp(tmpDir.data()))
+        {
+            throw "Failed to create temp dir";
+        }
+        // Create device path with symbol link
+        realDevicePath = fs::path(tmpDir) / "devices/3-0068";
+        devPath = fs::path(tmpDir) / "i2c";
+        fs::create_directories(realDevicePath);
+        fs::create_directories(realDevicePath / "driver");
+        fs::create_directories(devPath);
+        devPath /= "3-0068";
+        fs::create_directory_symlink(realDevicePath, devPath);
+    }
+
+    MockedI2CInterface& getMockedI2c()
+    {
+        return *reinterpret_cast<MockedI2CInterface*>(updater->i2c.get());
+    }
+
+    std::shared_ptr<I2CInterface> stolenI2C;
+    std::unique_ptr<Updater> updater;
+    fs::path realDevicePath;
+    fs::path devPath;
+    std::string tmpDir;
+    std::string psuInventoryPath = "/com/example/psu";
+    std::string imageDir = "/tmp/image/xxx";
+};
+
+TEST_F(TestUpdater, ctordtor)
+{
+    updater = std::make_unique<Updater>(psuInventoryPath, devPath, imageDir);
+}
+
+TEST_F(TestUpdater, doUpdate)
+{
+    updater = std::make_unique<Updater>(psuInventoryPath, devPath, imageDir);
+    updater->createI2CDevice();
+    auto& i2c = getMockedI2c();
+    EXPECT_CALL(i2c, read(_, An<uint8_t&>()));
+    updater->doUpdate();
+}
+
+TEST_F(TestUpdater, getDeviceName)
 {
     auto ret = internal::getDeviceName("");
     EXPECT_TRUE(ret.empty());
@@ -40,3 +110,18 @@
     ret = internal::getDeviceName("/sys/bus/i2c/devices/3-0069/");
     EXPECT_EQ("3-0069", ret);
 }
+
+TEST_F(TestUpdater, parseDeviceName)
+{
+    auto [id, addr] = internal::parseDeviceName("3-0068");
+    EXPECT_EQ(3, id);
+    EXPECT_EQ(0x68, addr);
+
+    std::tie(id, addr) = internal::parseDeviceName("11-0069");
+    EXPECT_EQ(11, id);
+    EXPECT_EQ(0x69, addr);
+
+    EXPECT_THROW(internal::parseDeviceName("no-number"), std::invalid_argument);
+
+    EXPECT_DEATH(internal::parseDeviceName("invalid"), "");
+}