regulators: Add configure support to Device class

Implemented the configure() method in the Device class.  This method
applies any configuration changes that are defined for the device.  Also
configures all rails within the device.

Also added a missing #include to rail_tests.cpp.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I2faceb7d968b6bb01ee7e1116367afee5864ca28
diff --git a/phosphor-regulators/src/device.cpp b/phosphor-regulators/src/device.cpp
index 3e7d5a4..dd662c9 100644
--- a/phosphor-regulators/src/device.cpp
+++ b/phosphor-regulators/src/device.cpp
@@ -16,6 +16,9 @@
 
 #include "device.hpp"
 
+#include "chassis.hpp"
+#include "system.hpp"
+
 namespace phosphor::power::regulators
 {
 
@@ -31,4 +34,19 @@
     }
 }
 
+void Device::configure(System& system, Chassis& chassis)
+{
+    // If configuration changes are defined for this device, apply them
+    if (configuration)
+    {
+        configuration->execute(system, chassis, *this);
+    }
+
+    // Configure rails
+    for (std::unique_ptr<Rail>& rail : rails)
+    {
+        rail->configure(system, chassis, *this);
+    }
+}
+
 } // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/device.hpp b/phosphor-regulators/src/device.hpp
index 919899a..e9369fc 100644
--- a/phosphor-regulators/src/device.hpp
+++ b/phosphor-regulators/src/device.hpp
@@ -29,6 +29,10 @@
 namespace phosphor::power::regulators
 {
 
+// Forward declarations to avoid circular dependencies
+class Chassis;
+class System;
+
 /**
  * @class Device
  *
@@ -82,6 +86,22 @@
     void addToIDMap(IDMap& idMap);
 
     /**
+     * Configure this device.
+     *
+     * Applies the configuration changes that are defined for this device, if
+     * any.
+     *
+     * Also configures the voltage rails produced by this device, if any.
+     *
+     * This method should be called during the boot before regulators are
+     * enabled.
+     *
+     * @param system system that contains the chassis
+     * @param chassis chassis that contains this device
+     */
+    void configure(System& system, Chassis& chassis);
+
+    /**
      * Returns the configuration changes to apply to this device, if any.
      *
      * @return Pointer to Configuration object.  Will equal nullptr if no
diff --git a/phosphor-regulators/test/device_tests.cpp b/phosphor-regulators/test/device_tests.cpp
index c4875bf..1ce065c 100644
--- a/phosphor-regulators/test/device_tests.cpp
+++ b/phosphor-regulators/test/device_tests.cpp
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 #include "action.hpp"
+#include "chassis.hpp"
 #include "configuration.hpp"
 #include "device.hpp"
 #include "i2c_interface.hpp"
 #include "id_map.hpp"
+#include "journal.hpp"
 #include "mock_action.hpp"
+#include "mock_journal.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
+#include "rule.hpp"
+#include "sensor_monitoring.hpp"
+#include "system.hpp"
 #include "test_utils.hpp"
 
 #include <memory>
@@ -28,11 +34,14 @@
 #include <utility>
 #include <vector>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 using namespace phosphor::power::regulators;
 using namespace phosphor::power::regulators::test_utils;
 
+using ::testing::Return;
+
 TEST(DeviceTests, Constructor)
 {
     // Test where only required parameters are specified
@@ -129,6 +138,120 @@
     EXPECT_THROW(idMap.getRail("vdd2"), std::invalid_argument);
 }
 
+TEST(DeviceTests, Configure)
+{
+    // Test where Configuration and Rails were not specified in constructor
+    {
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "reg1", true, "/system/chassis/motherboard/reg1",
+            std::move(i2cInterface));
+        Device* devicePtr = device.get();
+
+        // Create Chassis that contains Device
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.emplace_back(std::move(device));
+        std::unique_ptr<Chassis> chassis =
+            std::make_unique<Chassis>(1, std::move(devices));
+        Chassis* chassisPtr = chassis.get();
+
+        // Create System that contains Chassis
+        std::vector<std::unique_ptr<Rule>> rules{};
+        std::vector<std::unique_ptr<Chassis>> chassisVec{};
+        chassisVec.emplace_back(std::move(chassis));
+        System system{std::move(rules), std::move(chassisVec)};
+
+        // Call configure().  Should do nothing.
+        journal::clear();
+        devicePtr->configure(system, *chassisPtr);
+        EXPECT_EQ(journal::getDebugMessages().size(), 0);
+        EXPECT_EQ(journal::getErrMessages().size(), 0);
+    }
+
+    // Test where Configuration and Rails were specified in constructor
+    {
+        std::vector<std::unique_ptr<Rail>> rails{};
+
+        // Create Rail vdd0
+        {
+            // Create Configuration for Rail
+            std::optional<double> volts{1.3};
+            std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
+            EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
+            std::vector<std::unique_ptr<Action>> actions{};
+            actions.emplace_back(std::move(action));
+            std::unique_ptr<Configuration> configuration =
+                std::make_unique<Configuration>(volts, std::move(actions));
+
+            // Create Rail
+            std::unique_ptr<Rail> rail =
+                std::make_unique<Rail>("vdd0", std::move(configuration));
+            rails.emplace_back(std::move(rail));
+        }
+
+        // Create Rail vio0
+        {
+            // Create Configuration for Rail
+            std::optional<double> volts{3.2};
+            std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
+            EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
+            std::vector<std::unique_ptr<Action>> actions{};
+            actions.emplace_back(std::move(action));
+            std::unique_ptr<Configuration> configuration =
+                std::make_unique<Configuration>(volts, std::move(actions));
+
+            // Create Rail
+            std::unique_ptr<Rail> rail =
+                std::make_unique<Rail>("vio0", std::move(configuration));
+            rails.emplace_back(std::move(rail));
+        }
+
+        // Create Configuration for Device
+        std::optional<double> volts{};
+        std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
+        EXPECT_CALL(*action, execute).Times(1).WillOnce(Return(true));
+        std::vector<std::unique_ptr<Action>> actions{};
+        actions.emplace_back(std::move(action));
+        std::unique_ptr<Configuration> configuration =
+            std::make_unique<Configuration>(volts, std::move(actions));
+
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<PresenceDetection> presenceDetection{};
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "reg1", true, "/system/chassis/motherboard/reg1",
+            std::move(i2cInterface), std::move(presenceDetection),
+            std::move(configuration), std::move(rails));
+        Device* devicePtr = device.get();
+
+        // Create Chassis that contains Device
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.emplace_back(std::move(device));
+        std::unique_ptr<Chassis> chassis =
+            std::make_unique<Chassis>(1, std::move(devices));
+        Chassis* chassisPtr = chassis.get();
+
+        // Create System that contains Chassis
+        std::vector<std::unique_ptr<Rule>> rules{};
+        std::vector<std::unique_ptr<Chassis>> chassisVec{};
+        chassisVec.emplace_back(std::move(chassis));
+        System system{std::move(rules), std::move(chassisVec)};
+
+        // Call configure().  For the Device and both Rails, should execute the
+        // Configuration and log a debug message.
+        journal::clear();
+        devicePtr->configure(system, *chassisPtr);
+        std::vector<std::string> expectedDebugMessages{
+            "Configuring reg1",
+            "Configuring vdd0: volts=1.300000",
+            "Configuring vio0: volts=3.200000",
+        };
+        EXPECT_EQ(journal::getDebugMessages(), expectedDebugMessages);
+        EXPECT_EQ(journal::getErrMessages().size(), 0);
+    }
+}
+
 TEST(DeviceTests, GetConfiguration)
 {
     // Test where Configuration was not specified in constructor
diff --git a/phosphor-regulators/test/rail_tests.cpp b/phosphor-regulators/test/rail_tests.cpp
index 5781973..b0a3466 100644
--- a/phosphor-regulators/test/rail_tests.cpp
+++ b/phosphor-regulators/test/rail_tests.cpp
@@ -33,6 +33,7 @@
 #include <utility>
 #include <vector>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 using namespace phosphor::power::regulators;