regulators: Add closeDevices() to Chassis class

Add a closeDevices() method to the Chassis class.  This will close all
devices within the chassis.  The devices should be closed when monitoring
is disabled and the system is being powered off.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I713bf470392aa49632a34fa42bf849fa2f1c4c01
diff --git a/phosphor-regulators/src/chassis.cpp b/phosphor-regulators/src/chassis.cpp
index 3390ec5..a0d0c2b 100644
--- a/phosphor-regulators/src/chassis.cpp
+++ b/phosphor-regulators/src/chassis.cpp
@@ -31,6 +31,18 @@
     }
 }
 
+void Chassis::closeDevices()
+{
+    // Log debug message in journal
+    journal::logDebug("Closing devices in chassis " + std::to_string(number));
+
+    // Close devices
+    for (std::unique_ptr<Device>& device : devices)
+    {
+        device->close();
+    }
+}
+
 void Chassis::configure(System& system)
 {
     // Log info message in journal; important for verifying success of boot
diff --git a/phosphor-regulators/src/chassis.hpp b/phosphor-regulators/src/chassis.hpp
index 354da30..3fd3792 100644
--- a/phosphor-regulators/src/chassis.hpp
+++ b/phosphor-regulators/src/chassis.hpp
@@ -85,6 +85,11 @@
     void addToIDMap(IDMap& idMap);
 
     /**
+     * Close the devices within this chassis, if any.
+     */
+    void closeDevices();
+
+    /**
      * Configure the devices within this chassis, if any.
      *
      * This method should be called during the boot before regulators are
diff --git a/phosphor-regulators/test/chassis_tests.cpp b/phosphor-regulators/test/chassis_tests.cpp
index 25722ae..5f8266e 100644
--- a/phosphor-regulators/test/chassis_tests.cpp
+++ b/phosphor-regulators/test/chassis_tests.cpp
@@ -21,6 +21,7 @@
 #include "id_map.hpp"
 #include "journal.hpp"
 #include "mock_journal.hpp"
+#include "mocked_i2c_interface.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
 #include "rule.hpp"
@@ -38,6 +39,8 @@
 using namespace phosphor::power::regulators;
 using namespace phosphor::power::regulators::test_utils;
 
+using ::testing::Return;
+
 TEST(ChassisTests, Constructor)
 {
     // Test where works: Only required parameters are specified
@@ -104,6 +107,71 @@
     EXPECT_THROW(idMap.getRail("rail3"), std::invalid_argument);
 }
 
+TEST(ChassisTests, CloseDevices)
+{
+    // Test where no devices were specified in constructor
+    {
+        // Create Chassis
+        Chassis chassis{2};
+
+        // Call closeDevices()
+        journal::clear();
+        chassis.closeDevices();
+        EXPECT_EQ(journal::getErrMessages().size(), 0);
+        EXPECT_EQ(journal::getInfoMessages().size(), 0);
+        std::vector<std::string> expectedDebugMessages{
+            "Closing devices in chassis 2"};
+        EXPECT_EQ(journal::getDebugMessages(), expectedDebugMessages);
+    }
+
+    // Test where devices were specified in constructor
+    {
+        std::vector<std::unique_ptr<Device>> devices{};
+
+        // Create Device vdd0_reg
+        {
+            // Create mock I2CInterface: isOpen() and close() should be called
+            std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
+                std::make_unique<i2c::MockedI2CInterface>();
+            EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
+            EXPECT_CALL(*i2cInterface, close).Times(1);
+
+            // Create Device
+            std::unique_ptr<Device> device = std::make_unique<Device>(
+                "vdd0_reg", true, "/system/chassis/motherboard/vdd0_reg",
+                std::move(i2cInterface));
+            devices.emplace_back(std::move(device));
+        }
+
+        // Create Device vdd1_reg
+        {
+            // Create mock I2CInterface: isOpen() and close() should be called
+            std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
+                std::make_unique<i2c::MockedI2CInterface>();
+            EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
+            EXPECT_CALL(*i2cInterface, close).Times(1);
+
+            // Create Device
+            std::unique_ptr<Device> device = std::make_unique<Device>(
+                "vdd1_reg", true, "/system/chassis/motherboard/vdd1_reg",
+                std::move(i2cInterface));
+            devices.emplace_back(std::move(device));
+        }
+
+        // Create Chassis
+        Chassis chassis{1, std::move(devices)};
+
+        // Call closeDevices()
+        journal::clear();
+        chassis.closeDevices();
+        EXPECT_EQ(journal::getErrMessages().size(), 0);
+        EXPECT_EQ(journal::getInfoMessages().size(), 0);
+        std::vector<std::string> expectedDebugMessages{
+            "Closing devices in chassis 1"};
+        EXPECT_EQ(journal::getDebugMessages(), expectedDebugMessages);
+    }
+}
+
 TEST(ChassisTests, Configure)
 {
     // Test where no devices were specified in constructor