regulators: Add clearErrorHistory to Rail class

Add a clearErrorHistory() method to the Rail class.  The method should
clear all data on previously logged errors.  If the error occurs again
in the future, it will be logged again.

This method is normally called when the system is being powered on.  For
code that runs repeatedly, errors are only logged once per boot.

When the system is powered off, hardware may be replaced that fixes the
problem.  Thus, errors are cleared during power on.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I41e56162165186acb7a7872a288f0582874aa87d
diff --git a/phosphor-regulators/src/rail.cpp b/phosphor-regulators/src/rail.cpp
index ee24427..560ec9f 100644
--- a/phosphor-regulators/src/rail.cpp
+++ b/phosphor-regulators/src/rail.cpp
@@ -23,6 +23,15 @@
 namespace phosphor::power::regulators
 {
 
+void Rail::clearErrorHistory()
+{
+    // If sensor monitoring is defined for this rail, clear its error history
+    if (sensorMonitoring)
+    {
+        sensorMonitoring->clearErrorHistory();
+    }
+}
+
 void Rail::configure(Services& services, System& system, Chassis& chassis,
                      Device& device)
 {
diff --git a/phosphor-regulators/src/rail.hpp b/phosphor-regulators/src/rail.hpp
index ac55060..c661183 100644
--- a/phosphor-regulators/src/rail.hpp
+++ b/phosphor-regulators/src/rail.hpp
@@ -68,6 +68,16 @@
     }
 
     /**
+     * Clears all error history.
+     *
+     * All data on previously logged errors will be deleted.  If errors occur
+     * again in the future they will be logged again.
+     *
+     * This method is normally called when the system is being powered on.
+     */
+    void clearErrorHistory();
+
+    /**
      * Configure this rail.
      *
      * Applies the configuration changes that are defined for this rail, if any.
diff --git a/phosphor-regulators/test/rail_tests.cpp b/phosphor-regulators/test/rail_tests.cpp
index d852a06..ab34d20 100644
--- a/phosphor-regulators/test/rail_tests.cpp
+++ b/phosphor-regulators/test/rail_tests.cpp
@@ -19,6 +19,7 @@
 #include "device.hpp"
 #include "i2c_interface.hpp"
 #include "mock_action.hpp"
+#include "mock_error_logging.hpp"
 #include "mock_journal.hpp"
 #include "mock_sensors.hpp"
 #include "mock_services.hpp"
@@ -29,6 +30,7 @@
 #include "sensor_monitoring.hpp"
 #include "sensors.hpp"
 #include "system.hpp"
+#include "test_sdbus_error.hpp"
 
 #include <memory>
 #include <optional>
@@ -42,6 +44,7 @@
 
 using ::testing::A;
 using ::testing::Return;
+using ::testing::Throw;
 using ::testing::TypedEq;
 
 static const std::string chassisInvPath{
@@ -86,6 +89,87 @@
     }
 }
 
+TEST(RailTests, ClearErrorHistory)
+{
+    // Create SensorMonitoring.  Will fail with a DBus exception.
+    std::unique_ptr<MockAction> action = std::make_unique<MockAction>();
+    EXPECT_CALL(*action, execute)
+        .WillRepeatedly(Throw(TestSDBusError{"Unable to set sensor value"}));
+    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>(
+        "vddr1", std::move(configuration), std::move(sensorMonitoring));
+    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{};
+    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)};
+
+    // Create mock services
+    MockServices services{};
+
+    // Expect Sensors service to be called 5+5=10 times
+    MockSensors& sensors = services.getMockSensors();
+    EXPECT_CALL(sensors, startRail).Times(10);
+    EXPECT_CALL(sensors, setValue).Times(0);
+    EXPECT_CALL(sensors, endRail).Times(10);
+
+    // Expect Journal service to be called 3+3=6 times to log error messages
+    MockJournal& journal = services.getMockJournal();
+    EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>()))
+        .Times(6);
+    EXPECT_CALL(journal, logError(A<const std::string&>())).Times(6);
+
+    // Expect ErrorLogging service to be called 1+1=2 times to log a DBus error
+    MockErrorLogging& errorLogging = services.getMockErrorLogging();
+    EXPECT_CALL(errorLogging, logDBusError).Times(2);
+
+    // Monitor sensors 5 times.  Should fail every time, write to journal 3
+    // times, and log one error.
+    for (int i = 1; i <= 5; ++i)
+    {
+        railPtr->monitorSensors(services, system, *chassisPtr, *devicePtr);
+    }
+
+    // Clear error history
+    railPtr->clearErrorHistory();
+
+    // Monitor sensors 5 times again.  Should fail every time, write to journal
+    // 3 times, and log one error.
+    for (int i = 1; i <= 5; ++i)
+    {
+        railPtr->monitorSensors(services, system, *chassisPtr, *devicePtr);
+    }
+}
+
 TEST(RailTests, Configure)
 {
     // Test where Configuration was not specified in constructor