regulators: Add hardware presence service

The regulators application needs to determine whether hardware is
present or absent.  Some voltage regulators are optional, and
configuration should only be performed if the regulator is present.

Add a new class to obtain hardware presence data from the D-Bus
xyz.openbmc_project.Inventory.Item interface.

Also define an abstract base class and a mock implementation to enable
use of gmock in test cases related to hardware presence.

Tested:
* Tested where inventory path is present
* Tested where inventory path is not present
* Tested where inventory path is invalid and results in an exception
* Verified that mock implementation could be used in a gmock test case
* Full test plan available at
  https://gist.github.com/smccarney/2b6ea6ecbbeaf5a8b1793e2321799972

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ia25a92815efc506c3ef4ac72844c17120c4ce9b7
diff --git a/phosphor-regulators/src/meson.build b/phosphor-regulators/src/meson.build
index b20e0bd..bfcacb1 100644
--- a/phosphor-regulators/src/meson.build
+++ b/phosphor-regulators/src/meson.build
@@ -15,6 +15,7 @@
     'id_map.cpp',
     'journal.cpp',
     'pmbus_utils.cpp',
+    'presence_service.cpp',
     'rail.cpp',
     'sensor_monitoring.cpp',
     'system.cpp',
diff --git a/phosphor-regulators/src/presence_service.cpp b/phosphor-regulators/src/presence_service.cpp
new file mode 100644
index 0000000..7233d28
--- /dev/null
+++ b/phosphor-regulators/src/presence_service.cpp
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "presence_service.hpp"
+
+#include "types.hpp"
+#include "utility.hpp"
+
+namespace phosphor::power::regulators
+{
+
+bool DBusPresenceService::isPresent(const std::string& inventoryPath)
+{
+    bool present{false};
+
+    // Try to find cached presence value
+    auto it = cache.find(inventoryPath);
+    if (it != cache.end())
+    {
+        present = it->second;
+    }
+    else
+    {
+        // Get presence from D-Bus interface/property
+        util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
+                          INVENTORY_MGR_IFACE, bus, present);
+
+        // Cache presence value
+        cache[inventoryPath] = present;
+    }
+
+    return present;
+}
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/presence_service.hpp b/phosphor-regulators/src/presence_service.hpp
new file mode 100644
index 0000000..142e132
--- /dev/null
+++ b/phosphor-regulators/src/presence_service.hpp
@@ -0,0 +1,112 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <map>
+#include <string>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class PresenceService
+ *
+ * Abstract base class that provides an interface to hardware presence data.
+ *
+ * The interface is used to determine whether hardware is present.
+ */
+class PresenceService
+{
+  public:
+    // Specify which compiler-generated methods we want
+    PresenceService() = default;
+    PresenceService(const PresenceService&) = delete;
+    PresenceService(PresenceService&&) = delete;
+    PresenceService& operator=(const PresenceService&) = delete;
+    PresenceService& operator=(PresenceService&&) = delete;
+    virtual ~PresenceService() = default;
+
+    /**
+     * Clears any cached hardware presence data.
+     */
+    virtual void clearCache(void) = 0;
+
+    /**
+     * Returns whether the hardware with the specified inventory path is
+     * present.
+     *
+     * May return a cached value if one is available to improve performance.
+     *
+     * Throws an exception if an error occurs while obtaining the presence
+     * value.
+     *
+     * @param inventoryPath D-Bus inventory path of the hardware
+     * @return true if hardware is present, false otherwise
+     */
+    virtual bool isPresent(const std::string& inventoryPath) = 0;
+};
+
+/**
+ * @class DBusPresenceService
+ *
+ * Implementation of the PresenceService interface using D-Bus method calls.
+ */
+class DBusPresenceService : public PresenceService
+{
+  public:
+    // Specify which compiler-generated methods we want
+    DBusPresenceService() = delete;
+    DBusPresenceService(const DBusPresenceService&) = delete;
+    DBusPresenceService(DBusPresenceService&&) = delete;
+    DBusPresenceService& operator=(const DBusPresenceService&) = delete;
+    DBusPresenceService& operator=(DBusPresenceService&&) = delete;
+    virtual ~DBusPresenceService() = default;
+
+    /**
+     * Constructor.
+     *
+     * @param bus D-Bus bus object
+     */
+    explicit DBusPresenceService(sdbusplus::bus::bus& bus) : bus{bus}
+    {
+    }
+
+    /** @copydoc PresenceService::clearCache() */
+    virtual void clearCache(void) override
+    {
+        cache.clear();
+    }
+
+    /** @copydoc PresenceService::isPresent() */
+    virtual bool isPresent(const std::string& inventoryPath) override;
+
+  private:
+    /**
+     * D-Bus bus object.
+     */
+    sdbusplus::bus::bus& bus;
+
+    /**
+     * Cached presence data.
+     *
+     * Map from inventory paths to presence values.
+     */
+    std::map<std::string, bool> cache{};
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/services.hpp b/phosphor-regulators/src/services.hpp
index 1e38e7c..8d6fc0d 100644
--- a/phosphor-regulators/src/services.hpp
+++ b/phosphor-regulators/src/services.hpp
@@ -17,6 +17,7 @@
 
 #include "error_logging.hpp"
 #include "journal.hpp"
+#include "presence_service.hpp"
 
 #include <sdbusplus/bus.hpp>
 
@@ -63,6 +64,13 @@
      * @return journal interface
      */
     virtual Journal& getJournal() = 0;
+
+    /**
+     * Returns the interface to hardware presence data.
+     *
+     * @return hardware presence interface
+     */
+    virtual PresenceService& getPresenceService() = 0;
 };
 
 /**
@@ -86,7 +94,8 @@
      *
      * @param bus D-Bus bus object
      */
-    explicit BMCServices(sdbusplus::bus::bus& bus) : bus{bus}, errorLogging{bus}
+    explicit BMCServices(sdbusplus::bus::bus& bus) :
+        bus{bus}, errorLogging{bus}, presenceService{bus}
     {
     }
 
@@ -108,6 +117,12 @@
         return journal;
     }
 
+    /** @copydoc Services::getPresenceService() */
+    virtual PresenceService& getPresenceService() override
+    {
+        return presenceService;
+    }
+
   private:
     /**
      * D-Bus bus object.
@@ -124,6 +139,11 @@
      * journal.
      */
     SystemdJournal journal{};
+
+    /**
+     * Implementation of the PresenceService interface using D-Bus method calls.
+     */
+    DBusPresenceService presenceService;
 };
 
 } // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/mock_presence_service.hpp b/phosphor-regulators/test/mock_presence_service.hpp
new file mode 100644
index 0000000..7d2881d
--- /dev/null
+++ b/phosphor-regulators/test/mock_presence_service.hpp
@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "presence_service.hpp"
+
+#include <gmock/gmock.h>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class MockPresenceService
+ *
+ * Mock implementation of the PresenceService interface.
+ */
+class MockPresenceService : public PresenceService
+{
+  public:
+    // Specify which compiler-generated methods we want
+    MockPresenceService() = default;
+    MockPresenceService(const MockPresenceService&) = delete;
+    MockPresenceService(MockPresenceService&&) = delete;
+    MockPresenceService& operator=(const MockPresenceService&) = delete;
+    MockPresenceService& operator=(MockPresenceService&&) = delete;
+    virtual ~MockPresenceService() = default;
+
+    MOCK_METHOD(void, clearCache, (), (override));
+
+    MOCK_METHOD(bool, isPresent, (const std::string& inventoryPath),
+                (override));
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/mock_services.hpp b/phosphor-regulators/test/mock_services.hpp
index 075768c..1ef7192 100644
--- a/phosphor-regulators/test/mock_services.hpp
+++ b/phosphor-regulators/test/mock_services.hpp
@@ -19,6 +19,8 @@
 #include "journal.hpp"
 #include "mock_error_logging.hpp"
 #include "mock_journal.hpp"
+#include "mock_presence_service.hpp"
+#include "presence_service.hpp"
 #include "services.hpp"
 
 #include <sdbusplus/bus.hpp>
@@ -60,6 +62,12 @@
         return journal;
     }
 
+    /** @copydoc Services::getPresenceService() */
+    virtual PresenceService& getPresenceService() override
+    {
+        return presenceService;
+    }
+
     /**
      * Returns the MockErrorLogging object that implements the ErrorLogging
      * interface.
@@ -85,6 +93,19 @@
         return journal;
     }
 
+    /**
+     * Returns the MockPresenceService object that implements the
+     * PresenceService interface.
+     *
+     * This allows test cases to use the object in EXPECT_CALL() statements.
+     *
+     * @return mock presence service object
+     */
+    virtual MockPresenceService& getMockPresenceService()
+    {
+        return presenceService;
+    }
+
   private:
     /**
      * D-Bus bus object.
@@ -100,6 +121,11 @@
      * Mock implementation of the Journal interface.
      */
     MockJournal journal{};
+
+    /**
+     * Mock implementation of the PresenceService interface.
+     */
+    MockPresenceService presenceService{};
 };
 
 } // namespace phosphor::power::regulators