Update Trigger Actions implementation

Dbus trigger action names were modified to reflect separation of
Telemetry Service from Redfish:
- LogToLogService is renamed to LogToJournal,
- RedfishEvent was renamed to LogToRedfishEventLog

Both of those logging actions, now also include trigger id and threshold
name. Threshold naming logic:
- For discrete triggers, it can be specified by user, if left empty it
  will be changed to "{Severity} condition".
- Numeric triggers have no way of naming threshold, instead its type
  will be converted to string, example "UpperWarning"
- Discrete OnChange threshold will always be named "OnChange"

Additionally, defect was found with timestamp attached to Trigger Logs:
it was a steady_clock timestamp instead of system_clock. The function
which was supposed to format it was also working incorrectly, and was
improved to work with milliseconds. This change required major refactor
of unit tests, especially for numeric threshold.

Testing done:
- LogToJournal action is working properly,
- LogToRedfishEventLog action is working properly,
- UTs are passing.

Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
Change-Id: Iae2490682f0e9e2a610b45fd8af5cc5e21e66f35
diff --git a/tests/src/test_numeric_threshold.cpp b/tests/src/test_numeric_threshold.cpp
index 65c976c..69ad0de 100644
--- a/tests/src/test_numeric_threshold.cpp
+++ b/tests/src/test_numeric_threshold.cpp
@@ -1,5 +1,6 @@
 #include "dbus_environment.hpp"
 #include "helpers.hpp"
+#include "mocks/clock_mock.hpp"
 #include "mocks/sensor_mock.hpp"
 #include "mocks/trigger_action_mock.hpp"
 #include "numeric_threshold.hpp"
@@ -21,6 +22,9 @@
         std::make_unique<StrictMock<TriggerActionMock>>();
     TriggerActionMock& actionMock = *actionMockPtr;
     std::shared_ptr<NumericThreshold> sut;
+    std::string triggerId = "MyTrigger";
+    std::unique_ptr<NiceMock<ClockMock>> clockMockPtr =
+        std::make_unique<NiceMock<ClockMock>>();
 
     void makeThreshold(Milliseconds dwellTime, numeric::Direction direction,
                        double thresholdValue,
@@ -30,10 +34,11 @@
         actions.push_back(std::move(actionMockPtr));
 
         sut = std::make_shared<NumericThreshold>(
-            DbusEnvironment::getIoc(),
+            DbusEnvironment::getIoc(), triggerId,
             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
                 sensorMocks),
-            std::move(actions), dwellTime, direction, thresholdValue, type);
+            std::move(actions), dwellTime, direction, thresholdValue, type,
+            std::move(clockMockPtr));
     }
 
     void SetUp() override
@@ -64,7 +69,7 @@
 
 TEST_F(TestNumericThreshold, thresholdIsNotInitializeExpectNoActionCommit)
 {
-    EXPECT_CALL(actionMock, commit(_, _, _)).Times(0);
+    EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0);
 }
 
 TEST_F(TestNumericThreshold, getLabeledParamsReturnsCorrectly)
@@ -76,56 +81,167 @@
 
 struct NumericParams
 {
+    struct UpdateParams
+    {
+        size_t sensor;
+        double value;
+        Milliseconds sleepAfter;
+
+        UpdateParams(size_t sensor, double value,
+                     Milliseconds sleepAfter = 0ms) :
+            sensor(sensor),
+            value(value), sleepAfter(sleepAfter)
+        {}
+    };
+
+    struct ExpectedParams
+    {
+        size_t sensor;
+        double value;
+        Milliseconds waitMin;
+
+        ExpectedParams(size_t sensor, double value,
+                       Milliseconds waitMin = 0ms) :
+            sensor(sensor),
+            value(value), waitMin(waitMin)
+        {}
+    };
+
     NumericParams& Direction(numeric::Direction val)
     {
         direction = val;
         return *this;
     }
 
-    NumericParams&
-        Updates(std::vector<std::tuple<size_t, Milliseconds, double>> val)
+    NumericParams& Updates(std::vector<UpdateParams> val)
     {
         updates = std::move(val);
         return *this;
     }
 
-    NumericParams&
-        Expected(std::vector<std::tuple<size_t, Milliseconds, double>> val)
+    NumericParams& Expected(std::vector<ExpectedParams> val)
     {
         expected = std::move(val);
         return *this;
     }
 
+    NumericParams& ThresholdValue(double val)
+    {
+        thresholdValue = val;
+        return *this;
+    }
+
+    NumericParams& DwellTime(Milliseconds val)
+    {
+        dwellTime = std::move(val);
+        return *this;
+    }
+
+    NumericParams& InitialValues(std::vector<double> val)
+    {
+        initialValues = std::move(val);
+        return *this;
+    }
+
     friend void PrintTo(const NumericParams& o, std::ostream* os)
     {
-        *os << "{ Direction: " << static_cast<int>(o.direction)
-            << ", Updates: ";
-        for (const auto& [index, timestamp, value] : o.updates)
+        *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
+        *os << ", ThresholdValue: " << o.thresholdValue;
+        *os << ", Direction: " << static_cast<int>(o.direction);
+        *os << ", InitialValues: [ ";
+        size_t idx = 0;
+        for (const double value : o.initialValues)
         {
-            *os << "{ SensorIndex: " << index
-                << ", Timestamp: " << timestamp.count() << ", Value: " << value
-                << " }, ";
+            *os << "{ SensorIndex: " << idx << ", Value: " << value << " }, ";
+            idx++;
         }
-        *os << "Expected: ";
-        for (const auto& [index, timestamp, value] : o.expected)
+        *os << " ], Updates: [ ";
+        for (const auto& [index, value, sleepAfter] : o.updates)
         {
-            *os << "{ SensorIndex: " << index
-                << ", Timestamp: " << timestamp.count() << ", Value: " << value
-                << " }, ";
+            *os << "{ SensorIndex: " << index << ", Value: " << value
+                << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
         }
-        *os << " }";
+        *os << " ], Expected: [ ";
+        for (const auto& [index, value, waitMin] : o.expected)
+        {
+            *os << "{ SensorIndex: " << index << ", Value: " << value
+                << ", waitMin: " << waitMin.count() << "ms }, ";
+        }
+        *os << " ] }";
     }
 
     numeric::Direction direction;
-    std::vector<std::tuple<size_t, Milliseconds, double>> updates;
-    std::vector<std::tuple<size_t, Milliseconds, double>> expected;
+    double thresholdValue = 0.0;
+    Milliseconds dwellTime = 0ms;
+    std::vector<UpdateParams> updates;
+    std::vector<ExpectedParams> expected;
+    std::vector<double> initialValues;
 };
 
-class TestNumericThresholdNoDwellTime :
+class TestNumericThresholdCommon :
     public TestNumericThreshold,
     public WithParamInterface<NumericParams>
 {
   public:
+    void sleep(Milliseconds duration)
+    {
+        if (duration != 0ms)
+        {
+            DbusEnvironment::sleepFor(duration);
+        }
+    }
+
+    void testBodySensorIsUpdatedMultipleTimes()
+    {
+        std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
+            timestamps(sensorMocks.size());
+
+        sut->initialize();
+
+        InSequence seq;
+
+        for (const auto& [index, value, waitMin] : GetParam().expected)
+        {
+            EXPECT_CALL(actionMock,
+                        commit(triggerId, Eq(std::nullopt), sensorNames[index],
+                               _, TriggerValue(value)))
+                .WillOnce(DoAll(
+                    InvokeWithoutArgs([idx = index, &timestamps] {
+                        timestamps[idx] =
+                            std::chrono::high_resolution_clock::now();
+                    }),
+                    InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
+        }
+
+        auto start = std::chrono::high_resolution_clock::now();
+
+        size_t idx = 0;
+        for (const double value : GetParam().initialValues)
+        {
+            sut->sensorUpdated(*sensorMocks[idx], 0ms, value);
+            idx++;
+        }
+
+        for (const auto& [index, value, sleepAfter] : GetParam().updates)
+        {
+            ASSERT_LT(index, GetParam().initialValues.size())
+                << "Initial value was not specified for sensor with index: "
+                << index;
+            sut->sensorUpdated(*sensorMocks[index], 42ms, value);
+            sleep(sleepAfter);
+        }
+
+        EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
+        for (const auto& [index, value, waitMin] : GetParam().expected)
+        {
+            EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
+        }
+    }
+};
+
+class TestNumericThresholdNoDwellTime : public TestNumericThresholdCommon
+{
+  public:
     void SetUp() override
     {
         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
@@ -134,93 +250,84 @@
                 .WillByDefault(Return(sensorNames[idx]));
         }
 
-        makeThreshold(0ms, GetParam().direction, 90.0);
+        makeThreshold(0ms, GetParam().direction, GetParam().thresholdValue);
     }
 };
 
 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdNoDwellTime,
                          Values(NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
+                                    .InitialValues({80.0})
+                                    .Updates({{0, 89.0}})
                                     .Expected({}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
-                                    .Expected({{0, 2ms, 91.0}}),
+                                    .InitialValues({80.0})
+                                    .Updates({{0, 91.0}})
+                                    .Expected({{0, 91.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {0, 2ms, 99.0},
-                                              {0, 3ms, 80.0},
-                                              {0, 4ms, 98.0}})
-                                    .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}),
+                                    .InitialValues({80.0})
+                                    .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0}})
+                                    .Expected({{0, 99.0}, {0, 98.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {0, 2ms, 99.0},
-                                              {1, 3ms, 100.0},
-                                              {1, 4ms, 98.0}})
-                                    .Expected({{0, 2ms, 99.0}}),
+                                    .InitialValues({80.0, 100.0})
+                                    .Updates({{0, 99.0}, {1, 98.0}})
+                                    .Expected({{0, 99.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
+                                    .InitialValues({100.0})
+                                    .Updates({{0, 91.0}})
                                     .Expected({}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
-                                    .Expected({{0, 2ms, 80.0}}),
+                                    .InitialValues({100.0})
+                                    .Updates({{0, 80.0}})
+                                    .Expected({{0, 80.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 99.0},
-                                              {0, 4ms, 85.0}})
-                                    .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}),
+                                    .InitialValues({100.0})
+                                    .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0}})
+                                    .Expected({{0, 80.0}, {0, 85.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {1, 3ms, 99.0},
-                                              {1, 4ms, 88.0}})
-                                    .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
+                                    .InitialValues({100.0, 99.0})
+                                    .Updates({{0, 80.0}, {1, 88.0}})
+                                    .Expected({{0, 80.0}, {1, 88.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
+                                    .InitialValues({98.0})
+                                    .Updates({{0, 91.0}})
                                     .Expected({}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {0, 4ms, 91.0}})
-                                    .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}),
+                                    .InitialValues({100.0})
+                                    .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0}})
+                                    .Expected({{0, 80.0}, {0, 91.0}}),
                                 NumericParams()
+                                    .ThresholdValue(90.0)
                                     .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {1, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {1, 4ms, 91.0}})
-                                    .Expected({{0, 3ms, 85.0},
-                                               {1, 4ms, 91.0}})));
+                                    .InitialValues({100.0, 80.0})
+                                    .Updates({{0, 85.0}, {1, 91.0}})
+                                    .Expected({{0, 85.0}, {1, 91.0}})));
 
 TEST_P(TestNumericThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
 {
-    InSequence seq;
-    for (const auto& [index, timestamp, value] : GetParam().expected)
-    {
-        EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
-    }
-
-    sut->initialize();
-    for (const auto& [index, timestamp, value] : GetParam().updates)
-    {
-        sut->sensorUpdated(*sensorMocks[index], timestamp, value);
-    }
+    testBodySensorIsUpdatedMultipleTimes();
 }
 
-class TestNumericThresholdWithDwellTime :
-    public TestNumericThreshold,
-    public WithParamInterface<NumericParams>
+class TestNumericThresholdWithDwellTime : public TestNumericThresholdCommon
 {
   public:
     void SetUp() override
@@ -231,197 +338,172 @@
                 .WillByDefault(Return(sensorNames[idx]));
         }
 
-        makeThreshold(2ms, GetParam().direction, 90.0);
-    }
-
-    void sleep()
-    {
-        DbusEnvironment::sleepFor(4ms);
+        makeThreshold(GetParam().dwellTime, GetParam().direction,
+                      GetParam().thresholdValue);
     }
 };
 
-INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime,
-                         Values(NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
-                                    .Expected({{0, 2ms, 91.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {0, 2ms, 99.0},
-                                              {0, 3ms, 80.0},
-                                              {0, 4ms, 98.0}})
-                                    .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {1, 2ms, 99.0},
-                                              {0, 3ms, 100.0},
-                                              {1, 4ms, 86.0}})
-                                    .Expected({{0, 3ms, 100.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
-                                    .Expected({{0, 2ms, 80.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 99.0},
-                                              {0, 4ms, 85.0}})
-                                    .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {1, 3ms, 99.0},
-                                              {1, 4ms, 88.0}})
-                                    .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {0, 4ms, 91.0}})
-                                    .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {1, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {1, 4ms, 91.0}})
-                                    .Expected({{0, 3ms, 85.0},
-                                               {1, 4ms, 91.0}})));
+INSTANTIATE_TEST_SUITE_P(
+    SleepAfterEveryUpdate, TestNumericThresholdWithDwellTime,
+    Values(NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 89.0, 200ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 91.0, 200ms}})
+               .Expected({{0, 91.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 99.0, 200ms}, {0, 80.0, 100ms}, {0, 98.0, 200ms}})
+               .Expected({{0, 99.0, 200ms}, {0, 98.0, 500ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0, 99.0})
+               .Updates({{0, 100.0, 100ms}, {1, 86.0, 100ms}})
+               .Expected({{0, 100.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 91.0, 200ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0, 200ms}})
+               .Expected({{0, 80.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0, 200ms}, {0, 99.0, 100ms}, {0, 85.0, 200ms}})
+               .Expected({{0, 80.0, 200ms}, {0, 85.0, 500ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0, 99.0})
+               .Updates({{0, 80.0, 200ms}, {1, 88.0, 200ms}})
+               .Expected({{0, 80.0, 200ms}, {1, 88.0, 400ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({98.0})
+               .Updates({{0, 91.0, 200ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0, 100ms}, {0, 85.0, 100ms}, {0, 91.0, 200ms}})
+               .Expected({{0, 80.0, 200ms}, {0, 91.0, 400ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({100.0, 80.0})
+               .Updates({{0, 85.0, 100ms}, {1, 91.0, 200ms}})
+               .Expected({{0, 85.0, 200ms}, {1, 91.0, 300ms}})));
 
-TEST_P(TestNumericThresholdWithDwellTime,
-       senorsIsUpdatedMultipleTimesSleepAfterEveryUpdate)
+INSTANTIATE_TEST_SUITE_P(
+    SleepAfterLastUpdate, TestNumericThresholdWithDwellTime,
+    Values(NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 89.0, 300ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 91.0, 300ms}})
+               .Expected({{0, 91.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0})
+               .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0, 300ms}})
+               .Expected({{0, 98.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::increasing)
+               .InitialValues({80.0, 99.0})
+               .Updates({{0, 100.0}, {1, 98.0, 300ms}})
+               .Expected({{0, 100.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 91.0, 300ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0, 300ms}})
+               .Expected({{0, 80.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0, 300ms}})
+               .Expected({{0, 85.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::decreasing)
+               .InitialValues({100.0, 99.0})
+               .Updates({{0, 80.0}, {1, 88.0, 300ms}})
+               .Expected({{0, 80.0, 200ms}, {1, 88.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({98.0})
+               .Updates({{0, 91.0, 300ms}})
+               .Expected({}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({100.0})
+               .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0, 300ms}})
+               .Expected({{0, 91.0, 200ms}}),
+           NumericParams()
+               .DwellTime(200ms)
+               .ThresholdValue(90.0)
+               .Direction(numeric::Direction::either)
+               .InitialValues({100.0, 80.0})
+               .Updates({{0, 85.0}, {1, 91.0, 300ms}})
+               .Expected({{0, 85.0, 200ms}, {1, 91.0, 200ms}})));
+
+TEST_P(TestNumericThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
 {
-    InSequence seq;
-    for (const auto& [index, timestamp, value] : GetParam().expected)
-    {
-        EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
-    }
-
-    sut->initialize();
-    for (const auto& [index, timestamp, value] : GetParam().updates)
-    {
-        sut->sensorUpdated(*sensorMocks[index], timestamp, value);
-        sleep();
-    }
-}
-
-class TestNumericThresholdWithDwellTime2 :
-    public TestNumericThreshold,
-    public WithParamInterface<NumericParams>
-{
-  public:
-    void SetUp() override
-    {
-        for (size_t idx = 0; idx < sensorMocks.size(); idx++)
-        {
-            ON_CALL(*sensorMocks.at(idx), getName())
-                .WillByDefault(Return(sensorNames[idx]));
-        }
-
-        makeThreshold(2ms, GetParam().direction, 90.0);
-    }
-
-    void sleep()
-    {
-        DbusEnvironment::sleepFor(4ms);
-    }
-};
-
-INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime2,
-                         Values(NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
-                                    .Expected({{0, 2ms, 91.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {0, 2ms, 99.0},
-                                              {0, 3ms, 80.0},
-                                              {0, 4ms, 98.0}})
-                                    .Expected({{0, 4ms, 98.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::increasing)
-                                    .Updates({{0, 1ms, 80.0},
-                                              {1, 2ms, 99.0},
-                                              {0, 3ms, 100.0},
-                                              {1, 4ms, 98.0}})
-                                    .Expected({{0, 3ms, 100.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
-                                    .Expected({{0, 2ms, 80.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 99.0},
-                                              {0, 4ms, 85.0}})
-                                    .Expected({{0, 4ms, 85.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::decreasing)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {1, 3ms, 99.0},
-                                              {1, 4ms, 88.0}})
-                                    .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
-                                    .Expected({}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {0, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {0, 4ms, 91.0}})
-                                    .Expected({{0, 4ms, 91.0}}),
-                                NumericParams()
-                                    .Direction(numeric::Direction::either)
-                                    .Updates({{0, 1ms, 100.0},
-                                              {1, 2ms, 80.0},
-                                              {0, 3ms, 85.0},
-                                              {1, 4ms, 91.0}})
-                                    .Expected({{0, 3ms, 85.0},
-                                               {1, 4ms, 91.0}})));
-
-TEST_P(TestNumericThresholdWithDwellTime2,
-       senorsIsUpdatedMultipleTimesSleepAfterLastUpdate)
-{
-    InSequence seq;
-    for (const auto& [index, timestamp, value] : GetParam().expected)
-    {
-        EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
-    }
-
-    sut->initialize();
-    for (const auto& [index, timestamp, value] : GetParam().updates)
-    {
-        sut->sensorUpdated(*sensorMocks[index], timestamp, value);
-    }
-    sleep();
+    testBodySensorIsUpdatedMultipleTimes();
 }