regulators: Complete SensorMonitoring class

Complete the SensorMonitoring class.  This class implements the
"sensor_monitoring" object in the JSON config file.

Add calls to the startRail() and endRail() methods of the Sensors
service.

Add ErrorHistory data member so that errors are only logged once per
boot since sensor monitoring occurs repeatedly once per second.

Add error count data member to limit the number of error messages
written to the journal since monitoring occurs repeatedly.

Update all affected test cases.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I0bd20d03ccea9e15cd0f97529f6ccdad2fa015c8
diff --git a/phosphor-regulators/test/chassis_tests.cpp b/phosphor-regulators/test/chassis_tests.cpp
index c04e917..def281c 100644
--- a/phosphor-regulators/test/chassis_tests.cpp
+++ b/phosphor-regulators/test/chassis_tests.cpp
@@ -19,13 +19,15 @@
 #include "device.hpp"
 #include "i2c_interface.hpp"
 #include "id_map.hpp"
+#include "mock_action.hpp"
 #include "mock_journal.hpp"
+#include "mock_sensors.hpp"
 #include "mock_services.hpp"
 #include "mocked_i2c_interface.hpp"
-#include "pmbus_read_sensor_action.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
 #include "rule.hpp"
+#include "sensor_monitoring.hpp"
 #include "sensors.hpp"
 #include "system.hpp"
 #include "test_utils.hpp"
@@ -361,16 +363,16 @@
 {
     // Test where no devices were specified in constructor
     {
-        // Create mock services.  No logging should occur.
+        // Create mock services.  No Sensors methods should be called.
         MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(0);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail).Times(0);
 
         // Create Chassis
-        std::vector<std::unique_ptr<Device>> devices{};
-        std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
-            1, defaultInventoryPath, std::move(devices));
+        std::unique_ptr<Chassis> chassis =
+            std::make_unique<Chassis>(1, defaultInventoryPath);
         Chassis* chassisPtr = chassis.get();
 
         // Create System that contains Chassis
@@ -385,57 +387,89 @@
 
     // Test where devices were specified in constructor
     {
-        // Create mock services.  No logging should occur.
+        // Create mock services.  Set Sensors service expectations.
         MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail("vdd0",
+                                       "/xyz/openbmc_project/inventory/system/"
+                                       "chassis/motherboard/vdd0_reg",
+                                       defaultInventoryPath))
+            .Times(1);
+        EXPECT_CALL(sensors, startRail("vdd1",
+                                       "/xyz/openbmc_project/inventory/system/"
+                                       "chassis/motherboard/vdd1_reg",
+                                       defaultInventoryPath))
+            .Times(1);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(false)).Times(2);
 
         std::vector<std::unique_ptr<Device>> devices{};
 
-        // Create PMBusReadSensorAction
-        SensorType type{SensorType::iout};
-        uint8_t command = 0x8C;
-        pmbus_utils::SensorDataFormat format{
-            pmbus_utils::SensorDataFormat::linear_11};
-        std::optional<int8_t> exponent{};
-        std::unique_ptr<PMBusReadSensorAction> action =
-            std::make_unique<PMBusReadSensorAction>(type, command, format,
-                                                    exponent);
+        // Create Device vdd0_reg
+        {
+            // Create SensorMonitoring for Rail
+            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<SensorMonitoring> sensorMonitoring =
+                std::make_unique<SensorMonitoring>(std::move(actions));
 
-        // Create mock I2CInterface.  A two-byte read should occur.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
-            .Times(1);
+            // Create Rail
+            std::unique_ptr<Configuration> configuration{};
+            std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+                "vdd0", std::move(configuration), std::move(sensorMonitoring));
 
-        // Create SensorMonitoring
-        std::vector<std::unique_ptr<Action>> actions{};
-        actions.emplace_back(std::move(action));
-        std::unique_ptr<SensorMonitoring> sensorMonitoring =
-            std::make_unique<SensorMonitoring>(std::move(actions));
+            // Create Device
+            std::unique_ptr<i2c::I2CInterface> i2cInterface =
+                createI2CInterface();
+            std::unique_ptr<PresenceDetection> presenceDetection{};
+            std::unique_ptr<Configuration> deviceConfiguration{};
+            std::vector<std::unique_ptr<Rail>> rails{};
+            rails.emplace_back(std::move(rail));
+            std::unique_ptr<Device> device = std::make_unique<Device>(
+                "vdd0_reg", true,
+                "/xyz/openbmc_project/inventory/system/chassis/motherboard/"
+                "vdd0_reg",
+                std::move(i2cInterface), std::move(presenceDetection),
+                std::move(deviceConfiguration), std::move(rails));
+            devices.emplace_back(std::move(device));
+        }
 
-        // Create Rail
-        std::vector<std::unique_ptr<Rail>> rails{};
-        std::unique_ptr<Configuration> configuration{};
-        std::unique_ptr<Rail> rail = std::make_unique<Rail>(
-            "vdd0", std::move(configuration), std::move(sensorMonitoring));
-        rails.emplace_back(std::move(rail));
+        // Create Device vdd1_reg
+        {
+            // Create SensorMonitoring for Rail
+            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<SensorMonitoring> sensorMonitoring =
+                std::make_unique<SensorMonitoring>(std::move(actions));
 
-        // Create Device
-        std::unique_ptr<PresenceDetection> presenceDetection{};
-        std::unique_ptr<Configuration> deviceConfiguration{};
-        std::unique_ptr<Device> device = std::make_unique<Device>(
-            "reg1", true,
-            "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
-            std::move(i2cInterface), std::move(presenceDetection),
-            std::move(deviceConfiguration), std::move(rails));
+            // Create Rail
+            std::unique_ptr<Configuration> configuration{};
+            std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+                "vdd1", std::move(configuration), std::move(sensorMonitoring));
 
-        // Create Chassis
-        devices.emplace_back(std::move(device));
+            // Create Device
+            std::unique_ptr<i2c::I2CInterface> i2cInterface =
+                createI2CInterface();
+            std::unique_ptr<PresenceDetection> presenceDetection{};
+            std::unique_ptr<Configuration> deviceConfiguration{};
+            std::vector<std::unique_ptr<Rail>> rails{};
+            rails.emplace_back(std::move(rail));
+            std::unique_ptr<Device> device = std::make_unique<Device>(
+                "vdd1_reg", true,
+                "/xyz/openbmc_project/inventory/system/chassis/motherboard/"
+                "vdd1_reg",
+                std::move(i2cInterface), std::move(presenceDetection),
+                std::move(deviceConfiguration), std::move(rails));
+            devices.emplace_back(std::move(device));
+        }
+
+        // Create Chassis that contains Devices
         std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
-            1, defaultInventoryPath, std::move(devices));
+            2, defaultInventoryPath, std::move(devices));
         Chassis* chassisPtr = chassis.get();
 
         // Create System that contains Chassis
diff --git a/phosphor-regulators/test/device_tests.cpp b/phosphor-regulators/test/device_tests.cpp
index 0c386fb..21eecc9 100644
--- a/phosphor-regulators/test/device_tests.cpp
+++ b/phosphor-regulators/test/device_tests.cpp
@@ -22,12 +22,13 @@
 #include "mock_action.hpp"
 #include "mock_error_logging.hpp"
 #include "mock_journal.hpp"
+#include "mock_sensors.hpp"
 #include "mock_services.hpp"
 #include "mocked_i2c_interface.hpp"
-#include "pmbus_read_sensor_action.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
 #include "rule.hpp"
+#include "sensor_monitoring.hpp"
 #include "sensors.hpp"
 #include "system.hpp"
 #include "test_utils.hpp"
@@ -732,87 +733,40 @@
 TEST(DeviceTests, MonitorSensors)
 {
     // Test where device is not present
-    // TODO: Add this test when sensoring monitoring is fully implemented
-
-    // Test where Rails were not specified in constructor
     {
-        // Create mock services.  No logging should occur.
+        // Create mock services.  No Sensors methods should be called.
         MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(0);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail).Times(0);
 
-        // Create mock I2CInterface.  A two-byte read should NOT occur.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint16_t&>())).Times(0);
-
-        // Create Device
-        std::unique_ptr<Device> device = std::make_unique<Device>(
-            "reg1", true,
-            "/xyz/openbmc_project/inventory/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, chassisInvPath, 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 monitorSensors().
-        devicePtr->monitorSensors(services, system, *chassisPtr);
-    }
-
-    // Test where Rails were specified in constructor
-    {
-        // Create mock services.  No logging should occur.
-        MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
-
-        std::vector<std::unique_ptr<Rail>> rails{};
-
-        // Create PMBusReadSensorAction
-        SensorType type{SensorType::iout};
-        uint8_t command = 0x8C;
-        pmbus_utils::SensorDataFormat format{
-            pmbus_utils::SensorDataFormat::linear_11};
-        std::optional<int8_t> exponent{};
-        std::unique_ptr<PMBusReadSensorAction> action =
-            std::make_unique<PMBusReadSensorAction>(type, command, format,
-                                                    exponent);
-
-        // Create mock I2CInterface.  A two-byte read should occur.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
-            .Times(1);
-
-        // Create SensorMonitoring
-        std::vector<std::unique_ptr<Action>> actions{};
-        actions.emplace_back(std::move(action));
+        // Create SensorMonitoring.  Action inside it should not be executed.
+        std::unique_ptr<MockAction> sensAction = std::make_unique<MockAction>();
+        EXPECT_CALL(*sensAction, execute).Times(0);
+        std::vector<std::unique_ptr<Action>> sensActions{};
+        sensActions.emplace_back(std::move(sensAction));
         std::unique_ptr<SensorMonitoring> sensorMonitoring =
-            std::make_unique<SensorMonitoring>(std::move(actions));
+            std::make_unique<SensorMonitoring>(std::move(sensActions));
 
         // Create Rail
         std::unique_ptr<Configuration> configuration{};
         std::unique_ptr<Rail> rail = std::make_unique<Rail>(
-            "vdd0", std::move(configuration), std::move(sensorMonitoring));
-        rails.emplace_back(std::move(rail));
+            "vddr1", std::move(configuration), std::move(sensorMonitoring));
+
+        // Create PresenceDetection.  Indicates device is not present.
+        std::unique_ptr<MockAction> presAction = std::make_unique<MockAction>();
+        EXPECT_CALL(*presAction, execute).Times(1).WillOnce(Return(false));
+        std::vector<std::unique_ptr<Action>> presActions{};
+        presActions.emplace_back(std::move(presAction));
+        std::unique_ptr<PresenceDetection> presenceDetection =
+            std::make_unique<PresenceDetection>(std::move(presActions));
 
         // Create Device
-        std::unique_ptr<PresenceDetection> presenceDetection{};
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
         std::unique_ptr<Configuration> deviceConfiguration{};
+        std::vector<std::unique_ptr<Rail>> rails{};
+        rails.emplace_back(std::move(rail));
         std::unique_ptr<Device> device = std::make_unique<Device>(
             "reg1", true,
             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
@@ -833,7 +787,123 @@
         chassisVec.emplace_back(std::move(chassis));
         System system{std::move(rules), std::move(chassisVec)};
 
-        // Call monitorSensors().
+        // Call monitorSensors().  Should do nothing.
+        devicePtr->monitorSensors(services, system, *chassisPtr);
+    }
+
+    // Test where Rails were not specified in constructor
+    {
+        // Create mock services.  No Sensors methods should be called.
+        MockServices services{};
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(0);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail).Times(0);
+
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "reg1", true,
+            "/xyz/openbmc_project/inventory/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, chassisInvPath, 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 monitorSensors().  Should do nothing.
+        devicePtr->monitorSensors(services, system, *chassisPtr);
+    }
+
+    // Test where Rails were specified in constructor
+    {
+        // Create mock services.  Set Sensors service expectations.
+        MockServices services{};
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail("vdd0",
+                                       "/xyz/openbmc_project/inventory/system/"
+                                       "chassis/motherboard/reg1",
+                                       chassisInvPath))
+            .Times(1);
+        EXPECT_CALL(sensors, startRail("vio0",
+                                       "/xyz/openbmc_project/inventory/system/"
+                                       "chassis/motherboard/reg1",
+                                       chassisInvPath))
+            .Times(1);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(false)).Times(2);
+
+        std::vector<std::unique_ptr<Rail>> rails{};
+
+        // Create Rail vdd0
+        {
+            // Create SensorMonitoring for Rail
+            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<SensorMonitoring> sensorMonitoring =
+                std::make_unique<SensorMonitoring>(std::move(actions));
+
+            // Create Rail
+            std::unique_ptr<Configuration> configuration{};
+            std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+                "vdd0", std::move(configuration), std::move(sensorMonitoring));
+            rails.emplace_back(std::move(rail));
+        }
+
+        // Create Rail vio0
+        {
+            // Create SensorMonitoring for Rail
+            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<SensorMonitoring> sensorMonitoring =
+                std::make_unique<SensorMonitoring>(std::move(actions));
+
+            // Create Rail
+            std::unique_ptr<Configuration> configuration{};
+            std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+                "vio0", std::move(configuration), std::move(sensorMonitoring));
+            rails.emplace_back(std::move(rail));
+        }
+
+        // Create Device that contains Rails
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<PresenceDetection> presenceDetection{};
+        std::unique_ptr<Configuration> configuration{};
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "reg1", true,
+            "/xyz/openbmc_project/inventory/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, chassisInvPath, 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 monitorSensors().  Should monitor sensors in both rails.
         devicePtr->monitorSensors(services, system, *chassisPtr);
     }
 }
diff --git a/phosphor-regulators/test/rail_tests.cpp b/phosphor-regulators/test/rail_tests.cpp
index 4f52701..d852a06 100644
--- a/phosphor-regulators/test/rail_tests.cpp
+++ b/phosphor-regulators/test/rail_tests.cpp
@@ -20,9 +20,9 @@
 #include "i2c_interface.hpp"
 #include "mock_action.hpp"
 #include "mock_journal.hpp"
+#include "mock_sensors.hpp"
 #include "mock_services.hpp"
 #include "mocked_i2c_interface.hpp"
-#include "pmbus_read_sensor_action.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
 #include "rule.hpp"
@@ -222,22 +222,20 @@
 {
     // Test where SensorMonitoring was not specified in constructor
     {
-        // Create mock services.  No logging should occur.
+        // Create mock services.  No Sensors methods should be called.
         MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
-
-        // Create mock I2CInterface.  A two-byte read should NOT occur.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint16_t&>())).Times(0);
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(0);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail).Times(0);
 
         // Create Rail
         std::unique_ptr<Rail> rail = std::make_unique<Rail>("vdd0");
         Rail* railPtr = rail.get();
 
         // Create Device that contains Rail
+        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
+            std::make_unique<i2c::MockedI2CInterface>();
         std::unique_ptr<PresenceDetection> presenceDetection{};
         std::unique_ptr<Configuration> deviceConfiguration{};
         std::vector<std::unique_ptr<Rail>> rails{};
@@ -262,36 +260,27 @@
         chassisVec.emplace_back(std::move(chassis));
         System system{std::move(rules), std::move(chassisVec)};
 
-        // Call monitorSensors().
+        // Call monitorSensors()
         railPtr->monitorSensors(services, system, *chassisPtr, *devicePtr);
     }
 
     // Test where SensorMonitoring was specified in constructor
     {
-        // Create mock services.  No logging should occur.
+        // Create mock services.  Set Sensors service expectations.
         MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
-
-        // Create PMBusReadSensorAction
-        SensorType type{SensorType::iout};
-        uint8_t command = 0x8C;
-        pmbus_utils::SensorDataFormat format{
-            pmbus_utils::SensorDataFormat::linear_11};
-        std::optional<int8_t> exponent{};
-        std::unique_ptr<PMBusReadSensorAction> action =
-            std::make_unique<PMBusReadSensorAction>(type, command, format,
-                                                    exponent);
-
-        // Create mock I2CInterface.  A two-byte read should occur.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors,
+                    startRail("vddr1",
+                              "/xyz/openbmc_project/inventory/system/chassis/"
+                              "motherboard/reg1",
+                              chassisInvPath))
             .Times(1);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(false)).Times(1);
 
         // Create SensorMonitoring
+        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<SensorMonitoring> sensorMonitoring =
@@ -304,6 +293,8 @@
         Rail* railPtr = rail.get();
 
         // Create Device that contains Rail
+        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
+            std::make_unique<i2c::MockedI2CInterface>();
         std::unique_ptr<PresenceDetection> presenceDetection{};
         std::unique_ptr<Configuration> deviceConfiguration{};
         std::vector<std::unique_ptr<Rail>> rails{};
@@ -328,7 +319,7 @@
         chassisVec.emplace_back(std::move(chassis));
         System system{std::move(rules), std::move(chassisVec)};
 
-        // Call monitorSensors().
+        // Call monitorSensors()
         railPtr->monitorSensors(services, system, *chassisPtr, *devicePtr);
     }
 }
diff --git a/phosphor-regulators/test/sensor_monitoring_tests.cpp b/phosphor-regulators/test/sensor_monitoring_tests.cpp
index 4dfd415..6ed7e86 100644
--- a/phosphor-regulators/test/sensor_monitoring_tests.cpp
+++ b/phosphor-regulators/test/sensor_monitoring_tests.cpp
@@ -21,6 +21,7 @@
 #include "mock_action.hpp"
 #include "mock_error_logging.hpp"
 #include "mock_journal.hpp"
+#include "mock_sensors.hpp"
 #include "mock_services.hpp"
 #include "mocked_i2c_interface.hpp"
 #include "pmbus_read_sensor_action.hpp"
@@ -35,6 +36,8 @@
 #include <cstdint>
 #include <memory>
 #include <optional>
+#include <string>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -47,11 +50,69 @@
 using ::testing::A;
 using ::testing::Ref;
 using ::testing::Return;
+using ::testing::SetArgReferee;
 using ::testing::Throw;
 using ::testing::TypedEq;
 
-static const std::string chassisInvPath{
-    "/xyz/openbmc_project/inventory/system/chassis"};
+/**
+ * Creates the parent objects that normally contain a SensorMonitoring object.
+ *
+ * A SensorMonitoring object is normally contained within a hierarchy of System,
+ * Chassis, Device, and Rail objects.  These objects are required in order to
+ * call the execute() method.
+ *
+ * Creates the System, Chassis, Device, and Rail objects.  The SensorMonitoring
+ * object is moved into the Rail object.
+ *
+ * @param monitoring SensorMonitoring object to move into object hierarchy
+ * @return Tuple containing pointers the parent objects and the
+ *         MockedI2CInterface object.  They are all contained within the System
+ *         object and will be automatically destructed.
+ */
+std::tuple<std::unique_ptr<System>, Chassis*, Device*, i2c::MockedI2CInterface*,
+           Rail*>
+    createParentObjects(std::unique_ptr<SensorMonitoring> monitoring)
+{
+    // Create Rail that contains SensorMonitoring
+    std::unique_ptr<Configuration> configuration{};
+    std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+        "vdd", std::move(configuration), std::move(monitoring));
+    Rail* railPtr = rail.get();
+
+    // Create mock I2CInterface
+    std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
+        std::make_unique<i2c::MockedI2CInterface>();
+    i2c::MockedI2CInterface* i2cInterfacePtr = i2cInterface.get();
+
+    // Create Device that contains Rail
+    std::unique_ptr<PresenceDetection> presenceDetection{};
+    std::unique_ptr<Configuration> deviceConfiguration{};
+    std::vector<std::unique_ptr<Rail>> rails{};
+    rails.emplace_back(std::move(rail));
+    std::unique_ptr<Device> device = std::make_unique<Device>(
+        "vdd_reg", true,
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2",
+        std::move(i2cInterface), std::move(presenceDetection),
+        std::move(deviceConfiguration), 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, "/xyz/openbmc_project/inventory/system/chassis", 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));
+    std::unique_ptr<System> system =
+        std::make_unique<System>(std::move(rules), std::move(chassisVec));
+
+    return std::make_tuple(std::move(system), chassisPtr, devicePtr,
+                           i2cInterfacePtr, railPtr);
+}
 
 TEST(SensorMonitoringTests, Constructor)
 {
@@ -62,157 +123,207 @@
     EXPECT_EQ(sensorMonitoring.getActions().size(), 1);
 }
 
+TEST(SensorMonitoringTests, ClearErrorHistory)
+{
+    // Create PMBusReadSensorAction
+    SensorType type{SensorType::iout};
+    uint8_t command{0x8C};
+    SensorDataFormat format{SensorDataFormat::linear_11};
+    std::optional<int8_t> exponent{};
+    std::unique_ptr<PMBusReadSensorAction> action =
+        std::make_unique<PMBusReadSensorAction>(type, command, format,
+                                                exponent);
+
+    // Create SensorMonitoring
+    std::vector<std::unique_ptr<Action>> actions{};
+    actions.emplace_back(std::move(action));
+    SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions));
+
+    // Create parent objects that contain SensorMonitoring
+    auto [system, chassis, device, i2cInterface, rail] =
+        createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring});
+
+    // Set I2CInterface expectations
+    EXPECT_CALL(*i2cInterface, isOpen).WillRepeatedly(Return(true));
+    EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
+        .WillRepeatedly(Throw(
+            i2c::I2CException{"Failed to read word data", "/dev/i2c-1", 0x70}));
+
+    // Perform sensor monitoring 10 times to set error history data members
+    {
+        // Create mock services
+        MockServices services{};
+
+        // Set Sensors service expectations.  SensorMonitoring will be executed
+        // 10 times.
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(10);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(true)).Times(10);
+
+        // Set Journal service expectations.  SensorMonitoring should log error
+        // messages 3 times and then stop.
+        MockJournal& journal = services.getMockJournal();
+        EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
+            .Times(3);
+        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(3);
+
+        // Set ErrorLogging service expectations.  SensorMonitoring should log
+        // an error only once.
+        MockErrorLogging& errorLogging = services.getMockErrorLogging();
+        EXPECT_CALL(errorLogging, logI2CError).Times(1);
+
+        // Execute SensorMonitoring 10 times
+        for (int i = 1; i <= 10; ++i)
+        {
+            monitoring->execute(services, *system, *chassis, *device, *rail);
+        }
+    }
+
+    // Clear error history
+    monitoring->clearErrorHistory();
+
+    // Perform sensor monitoring one more time.  Should log errors again.
+    {
+        // Create mock services
+        MockServices services{};
+
+        // Set Sensors service expectations.  SensorMonitoring will be executed
+        // 1 time.
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors, startRail).Times(1);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(true)).Times(1);
+
+        // Set Journal service expectations.  SensorMonitoring should log error
+        // messages 1 time.
+        MockJournal& journal = services.getMockJournal();
+        EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
+            .Times(1);
+        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(1);
+
+        // Set ErrorLogging server expectations.  SensorMonitoring should log an
+        // error.
+        MockErrorLogging& errorLogging = services.getMockErrorLogging();
+        EXPECT_CALL(errorLogging, logI2CError).Times(1);
+
+        // Execute SensorMonitoring
+        monitoring->execute(services, *system, *chassis, *device, *rail);
+    }
+}
+
 TEST(SensorMonitoringTests, Execute)
 {
     // Test where works
     {
-        // Create mock services.  No logging should occur.
-        MockServices services{};
-        MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-        EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
-
         // Create PMBusReadSensorAction
         SensorType type{SensorType::iout};
-        uint8_t command = 0x8C;
-        pmbus_utils::SensorDataFormat format{
-            pmbus_utils::SensorDataFormat::linear_11};
+        uint8_t command{0x8C};
+        SensorDataFormat format{SensorDataFormat::linear_11};
         std::optional<int8_t> exponent{};
         std::unique_ptr<PMBusReadSensorAction> action =
             std::make_unique<PMBusReadSensorAction>(type, command, format,
                                                     exponent);
 
-        // Create mock I2CInterface.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
-            .Times(1);
-
         // Create SensorMonitoring
         std::vector<std::unique_ptr<Action>> actions{};
         actions.emplace_back(std::move(action));
-        std::unique_ptr<SensorMonitoring> sensorMonitoring =
-            std::make_unique<SensorMonitoring>(std::move(actions));
-        SensorMonitoring* sensorMonitoringPtr = sensorMonitoring.get();
+        SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions));
 
-        // Create Rail that contains sensorMonitoring
-        std::unique_ptr<Configuration> configuration{};
-        std::unique_ptr<Rail> rail = std::make_unique<Rail>(
-            "vio2", std::move(configuration), std::move(sensorMonitoring));
-        Rail* railPtr = rail.get();
+        // Create parent objects that contain SensorMonitoring
+        auto [system, chassis, device, i2cInterface, rail] =
+            createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring});
 
-        // Create Device that contains Rail
-        std::unique_ptr<PresenceDetection> presenceDetection{};
-        std::unique_ptr<Configuration> deviceConfiguration{};
-        std::vector<std::unique_ptr<Rail>> rails{};
-        rails.emplace_back(std::move(rail));
-        std::unique_ptr<Device> device = std::make_unique<Device>(
-            "reg1", true,
-            "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
-            std::move(i2cInterface), std::move(presenceDetection),
-            std::move(deviceConfiguration), std::move(rails));
-        Device* devicePtr = device.get();
+        // Set I2CInterface expectations
+        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
+        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
+            .Times(1)
+            .WillOnce(SetArgReferee<1>(0xD2E0));
 
-        // 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, chassisInvPath, std::move(devices));
-        Chassis* chassisPtr = chassis.get();
+        // Create mock services.  Set Sensors service expectations.
+        MockServices services{};
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors,
+                    startRail("vdd",
+                              "/xyz/openbmc_project/inventory/system/chassis/"
+                              "motherboard/reg2",
+                              "/xyz/openbmc_project/inventory/system/chassis"))
+            .Times(1);
+        EXPECT_CALL(sensors, setValue(SensorType::iout, 11.5)).Times(1);
+        EXPECT_CALL(sensors, endRail(false)).Times(1);
 
-        // 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)};
-
-        // Execute sensorMonitoring
-        sensorMonitoringPtr->execute(services, system, *chassisPtr, *devicePtr,
-                                     *railPtr);
+        // Execute SensorMonitoring
+        monitoring->execute(services, *system, *chassis, *device, *rail);
     }
 
     // Test where fails
     {
-        // Create mock services.  Expect logError() and logI2CError() to be
-        // called.
+        // Create PMBusReadSensorAction
+        SensorType type{SensorType::iout};
+        uint8_t command{0x8C};
+        SensorDataFormat format{SensorDataFormat::linear_11};
+        std::optional<int8_t> exponent{};
+        std::unique_ptr<PMBusReadSensorAction> action =
+            std::make_unique<PMBusReadSensorAction>(type, command, format,
+                                                    exponent);
+
+        // Create SensorMonitoring
+        std::vector<std::unique_ptr<Action>> actions{};
+        actions.emplace_back(std::move(action));
+        SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions));
+
+        // Create parent objects that contain SensorMonitoring
+        auto [system, chassis, device, i2cInterface, rail] =
+            createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring});
+
+        // Set I2CInterface expectations.  Should read register 0x8C 4 times.
+        EXPECT_CALL(*i2cInterface, isOpen)
+            .Times(4)
+            .WillRepeatedly(Return(true));
+        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
+            .Times(4)
+            .WillRepeatedly(Throw(i2c::I2CException{"Failed to read word data",
+                                                    "/dev/i2c-1", 0x70}));
+
+        // Create mock services
         MockServices services{};
-        MockErrorLogging& errorLogging = services.getMockErrorLogging();
+
+        // Set Sensors service expectations.  SensorMonitoring will be executed
+        // 4 times, and all should fail.
+        MockSensors& sensors = services.getMockSensors();
+        EXPECT_CALL(sensors,
+                    startRail("vdd",
+                              "/xyz/openbmc_project/inventory/system/chassis/"
+                              "motherboard/reg2",
+                              "/xyz/openbmc_project/inventory/system/chassis"))
+            .Times(4);
+        EXPECT_CALL(sensors, setValue).Times(0);
+        EXPECT_CALL(sensors, endRail(true)).Times(4);
+
+        // Set Journal service expectations.  SensorMonitoring should log error
+        // messages 3 times and then stop.
         MockJournal& journal = services.getMockJournal();
-        EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
         std::vector<std::string> expectedErrMessagesException{
-            "I2CException: Failed to write byte: bus /dev/i2c-1, addr 0x70",
+            "I2CException: Failed to read word data: bus /dev/i2c-1, addr 0x70",
             "ActionError: pmbus_read_sensor: { type: iout, command: 0x8C, "
             "format: linear_11 }"};
-        EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(1);
-        EXPECT_CALL(journal,
-                    logError("Unable to monitor sensors for rail vio2"))
-            .Times(1);
+        EXPECT_CALL(journal, logError(expectedErrMessagesException)).Times(3);
+        EXPECT_CALL(journal, logError("Unable to monitor sensors for rail vdd"))
+            .Times(3);
+
+        // Set ErrorLogging service expectations.  SensorMonitoring should log
+        // an error only once.
+        MockErrorLogging& errorLogging = services.getMockErrorLogging();
         EXPECT_CALL(errorLogging,
                     logI2CError(Entry::Level::Warning, Ref(journal),
                                 "/dev/i2c-1", 0x70, 0))
             .Times(1);
 
-        // Create PMBusReadSensorAction
-        SensorType type{SensorType::iout};
-        uint8_t command = 0x8C;
-        pmbus_utils::SensorDataFormat format{
-            pmbus_utils::SensorDataFormat::linear_11};
-        std::optional<int8_t> exponent{};
-        std::unique_ptr<PMBusReadSensorAction> action =
-            std::make_unique<PMBusReadSensorAction>(type, command, format,
-                                                    exponent);
-
-        // Create mock I2CInterface.
-        std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-            std::make_unique<i2c::MockedI2CInterface>();
-        EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-        EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
-            .Times(1)
-            .WillOnce(Throw(
-                i2c::I2CException{"Failed to write byte", "/dev/i2c-1", 0x70}));
-
-        // Create SensorMonitoring
-        std::vector<std::unique_ptr<Action>> actions{};
-        actions.emplace_back(std::move(action));
-        std::unique_ptr<SensorMonitoring> sensorMonitoring =
-            std::make_unique<SensorMonitoring>(std::move(actions));
-        SensorMonitoring* sensorMonitoringPtr = sensorMonitoring.get();
-
-        // Create Rail that contains sensorMonitoring
-        std::unique_ptr<Configuration> configuration{};
-        std::unique_ptr<Rail> rail = std::make_unique<Rail>(
-            "vio2", std::move(configuration), std::move(sensorMonitoring));
-        Rail* railPtr = rail.get();
-
-        // Create Device that contains Rail
-        std::unique_ptr<PresenceDetection> presenceDetection{};
-        std::unique_ptr<Configuration> deviceConfiguration{};
-        std::vector<std::unique_ptr<Rail>> rails{};
-        rails.emplace_back(std::move(rail));
-        std::unique_ptr<Device> device = std::make_unique<Device>(
-            "reg1", true,
-            "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
-            std::move(i2cInterface), std::move(presenceDetection),
-            std::move(deviceConfiguration), 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, chassisInvPath, 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)};
-
-        // Execute sensorMonitoring
-        sensorMonitoringPtr->execute(services, system, *chassisPtr, *devicePtr,
-                                     *railPtr);
+        // Execute SensorMonitoring 4 times
+        for (int i = 1; i <= 4; ++i)
+        {
+            monitoring->execute(services, *system, *chassis, *device, *rail);
+        }
     }
 }
 
diff --git a/phosphor-regulators/test/system_tests.cpp b/phosphor-regulators/test/system_tests.cpp
index 2bd94e0..31df5d6 100644
--- a/phosphor-regulators/test/system_tests.cpp
+++ b/phosphor-regulators/test/system_tests.cpp
@@ -13,15 +13,21 @@
  * See the License for the specific language governing permissions and
  * 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 "mock_action.hpp"
 #include "mock_journal.hpp"
+#include "mock_sensors.hpp"
 #include "mock_services.hpp"
 #include "mocked_i2c_interface.hpp"
-#include "pmbus_read_sensor_action.hpp"
+#include "presence_detection.hpp"
 #include "rail.hpp"
 #include "rule.hpp"
+#include "sensor_monitoring.hpp"
 #include "sensors.hpp"
 #include "services.hpp"
 #include "system.hpp"
@@ -250,61 +256,98 @@
 
 TEST(SystemTests, MonitorSensors)
 {
-    // Create mock services.  No logging should occur.
+    // Create mock services.  Set Sensors service expectations.
     MockServices services{};
-    MockJournal& journal = services.getMockJournal();
-    EXPECT_CALL(journal, logDebug(A<const std::string&>())).Times(0);
-    EXPECT_CALL(journal, logError(A<const std::string&>())).Times(0);
-
-    // Create PMBusReadSensorAction
-    SensorType type{SensorType::iout};
-    uint8_t command = 0x8C;
-    pmbus_utils::SensorDataFormat format{
-        pmbus_utils::SensorDataFormat::linear_11};
-    std::optional<int8_t> exponent{};
-    std::unique_ptr<PMBusReadSensorAction> action =
-        std::make_unique<PMBusReadSensorAction>(type, command, format,
-                                                exponent);
-
-    // Create mock I2CInterface.  A two-byte read should occur.
-    std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
-        std::make_unique<i2c::MockedI2CInterface>();
-    EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
-    EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
+    MockSensors& sensors = services.getMockSensors();
+    EXPECT_CALL(sensors, startRail("c1_vdd0",
+                                   "/xyz/openbmc_project/inventory/system/"
+                                   "chassis1/motherboard/vdd0_reg",
+                                   chassisInvPath + '1'))
         .Times(1);
+    EXPECT_CALL(sensors, startRail("c2_vdd0",
+                                   "/xyz/openbmc_project/inventory/system/"
+                                   "chassis2/motherboard/vdd0_reg",
+                                   chassisInvPath + '2'))
+        .Times(1);
+    EXPECT_CALL(sensors, setValue).Times(0);
+    EXPECT_CALL(sensors, endRail(false)).Times(2);
 
-    // Create SensorMonitoring
-    std::vector<std::unique_ptr<Action>> actions{};
-    actions.emplace_back(std::move(action));
-    std::unique_ptr<SensorMonitoring> sensorMonitoring =
-        std::make_unique<SensorMonitoring>(std::move(actions));
+    std::vector<std::unique_ptr<Chassis>> chassisVec{};
 
-    // Create Rail
-    std::vector<std::unique_ptr<Rail>> rails{};
-    std::unique_ptr<Configuration> configuration{};
-    std::unique_ptr<Rail> rail = std::make_unique<Rail>(
-        "vdd0", std::move(configuration), std::move(sensorMonitoring));
-    rails.emplace_back(std::move(rail));
+    // Create Chassis 1
+    {
+        // Create SensorMonitoring for Rail
+        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<SensorMonitoring> sensorMonitoring =
+            std::make_unique<SensorMonitoring>(std::move(actions));
 
-    // Create Device
-    std::unique_ptr<PresenceDetection> presenceDetection{};
-    std::unique_ptr<Configuration> deviceConfiguration{};
-    std::unique_ptr<Device> device = std::make_unique<Device>(
-        "reg1", true,
-        "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
-        std::move(i2cInterface), std::move(presenceDetection),
-        std::move(deviceConfiguration), std::move(rails));
+        // Create Rail
+        std::unique_ptr<Configuration> configuration{};
+        std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+            "c1_vdd0", std::move(configuration), std::move(sensorMonitoring));
 
-    // Create Chassis
-    std::vector<std::unique_ptr<Device>> devices{};
-    devices.emplace_back(std::move(device));
-    std::unique_ptr<Chassis> chassis =
-        std::make_unique<Chassis>(1, chassisInvPath, std::move(devices));
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<PresenceDetection> presenceDetection{};
+        std::unique_ptr<Configuration> deviceConfiguration{};
+        std::vector<std::unique_ptr<Rail>> rails{};
+        rails.emplace_back(std::move(rail));
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "c1_vdd0_reg", true,
+            "/xyz/openbmc_project/inventory/system/chassis1/motherboard/"
+            "vdd0_reg",
+            std::move(i2cInterface), std::move(presenceDetection),
+            std::move(deviceConfiguration), std::move(rails));
+
+        // Create Chassis
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.emplace_back(std::move(device));
+        std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
+            1, chassisInvPath + '1', std::move(devices));
+        chassisVec.emplace_back(std::move(chassis));
+    }
+
+    // Create Chassis 2
+    {
+        // Create SensorMonitoring for Rail
+        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<SensorMonitoring> sensorMonitoring =
+            std::make_unique<SensorMonitoring>(std::move(actions));
+
+        // Create Rail
+        std::unique_ptr<Configuration> configuration{};
+        std::unique_ptr<Rail> rail = std::make_unique<Rail>(
+            "c2_vdd0", std::move(configuration), std::move(sensorMonitoring));
+
+        // Create Device
+        std::unique_ptr<i2c::I2CInterface> i2cInterface = createI2CInterface();
+        std::unique_ptr<PresenceDetection> presenceDetection{};
+        std::unique_ptr<Configuration> deviceConfiguration{};
+        std::vector<std::unique_ptr<Rail>> rails{};
+        rails.emplace_back(std::move(rail));
+        std::unique_ptr<Device> device = std::make_unique<Device>(
+            "c2_vdd0_reg", true,
+            "/xyz/openbmc_project/inventory/system/chassis2/motherboard/"
+            "vdd0_reg",
+            std::move(i2cInterface), std::move(presenceDetection),
+            std::move(deviceConfiguration), std::move(rails));
+
+        // Create Chassis
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.emplace_back(std::move(device));
+        std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>(
+            2, chassisInvPath + '2', std::move(devices));
+        chassisVec.emplace_back(std::move(chassis));
+    }
 
     // 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 monitorSensors()