added support for Collection Functions

new supported operations: min,max,sum,avg
new supported time scopes: interval,startup

added unit test to verify that each collection function returns correct
timestamp and value

Tested:
- POST/GET on telemetry features in bmcweb, no regression detected
- Using dbus API metric with collection function works as expected

Change-Id: Ib364c433915e07fd7a102f00109525362c40ab8a
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/tests/src/test_metric.cpp b/tests/src/test_metric.cpp
index 6c68513..87e32f9 100644
--- a/tests/src/test_metric.cpp
+++ b/tests/src/test_metric.cpp
@@ -1,3 +1,4 @@
+#include "fakes/clock_fake.hpp"
 #include "helpers.hpp"
 #include "metric.hpp"
 #include "mocks/sensor_mock.hpp"
@@ -35,17 +36,18 @@
             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
                 sensorMocks),
             p.operationType(), p.id(), p.metadata(), p.collectionTimeScope(),
-            p.collectionDuration());
+            p.collectionDuration(), std::move(clockFakePtr));
     }
 
-    MetricParams params =
-        MetricParams()
-            .id("id")
-            .metadata("metadata")
-            .operationType(OperationType::avg)
-            .collectionTimeScope(CollectionTimeScope::interval)
-            .collectionDuration(CollectionDuration(42ms));
+    MetricParams params = MetricParams()
+                              .id("id")
+                              .metadata("metadata")
+                              .operationType(OperationType::avg)
+                              .collectionTimeScope(CollectionTimeScope::point)
+                              .collectionDuration(CollectionDuration(0ms));
     std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u);
+    std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>();
+    ClockFake& clockFake = *clockFakePtr;
     std::shared_ptr<Metric> sut;
 };
 
@@ -157,14 +159,196 @@
     const auto conf = sut->dumpConfiguration();
 
     LabeledMetricParameters expected = {};
-    expected.at_label<ts::Id>() = "id";
-    expected.at_label<ts::MetricMetadata>() = "metadata";
-    expected.at_label<ts::OperationType>() = OperationType::avg;
-    expected.at_label<ts::CollectionTimeScope>() =
-        CollectionTimeScope::interval;
-    expected.at_label<ts::CollectionDuration>() = CollectionDuration(42ms);
+    expected.at_label<ts::Id>() = params.id();
+    expected.at_label<ts::MetricMetadata>() = params.metadata();
+    expected.at_label<ts::OperationType>() = params.operationType();
+    expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope();
+    expected.at_label<ts::CollectionDuration>() = params.collectionDuration();
     expected.at_label<ts::SensorPath>() = {
         LabeledSensorParameters("service1", "path1")};
 
     EXPECT_THAT(conf, Eq(expected));
 }
+
+class TestMetricCalculationFunctions :
+    public TestMetric,
+    public WithParamInterface<MetricParams>
+{
+  public:
+    void SetUp() override
+    {
+        clockFakePtr->set(0ms);
+
+        sut = makeSut(params.operationType(GetParam().operationType())
+                          .collectionTimeScope(GetParam().collectionTimeScope())
+                          .collectionDuration(GetParam().collectionDuration()));
+    }
+
+    static std::vector<std::pair<Milliseconds, double>> defaultReadings()
+    {
+        std::vector<std::pair<Milliseconds, double>> ret;
+        ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN());
+        ret.emplace_back(10ms, 14.);
+        ret.emplace_back(1ms, 3.);
+        ret.emplace_back(5ms, 7.);
+        return ret;
+    }
+};
+
+MetricParams defaultSingleParams()
+{
+    return MetricParams()
+        .operationType(OperationType::single)
+        .readings(TestMetricCalculationFunctions::defaultReadings())
+        .expectedReading(11ms, 7.0);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    OperationSingleReturnsLastReading, TestMetricCalculationFunctions,
+    Values(
+        defaultSingleParams().collectionTimeScope(CollectionTimeScope::point),
+        defaultSingleParams()
+            .collectionTimeScope(CollectionTimeScope::interval)
+            .collectionDuration(CollectionDuration(100ms)),
+        defaultSingleParams().collectionTimeScope(
+            CollectionTimeScope::startup)));
+
+MetricParams defaultPointParams()
+{
+    return defaultSingleParams().collectionTimeScope(
+        CollectionTimeScope::point);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TimeScopePointReturnsLastReading, TestMetricCalculationFunctions,
+    Values(defaultPointParams().operationType(OperationType::single),
+           defaultPointParams().operationType(OperationType::min),
+           defaultPointParams().operationType(OperationType::max),
+           defaultPointParams().operationType(OperationType::sum),
+           defaultPointParams().operationType(OperationType::avg)));
+
+MetricParams defaultMinParams()
+{
+    return defaultSingleParams().operationType(OperationType::min);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions,
+    Values(defaultMinParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(100ms))
+               .expectedReading(10ms, 3.0),
+           defaultMinParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(3ms))
+               .expectedReading(13ms, 7.0),
+           defaultMinParams()
+               .collectionTimeScope(CollectionTimeScope::startup)
+               .expectedReading(10ms, 3.0)));
+
+MetricParams defaultMaxParams()
+{
+    return defaultSingleParams().operationType(OperationType::max);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions,
+    Values(defaultMaxParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(100ms))
+               .expectedReading(0ms, 14.0),
+           defaultMaxParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(6ms))
+               .expectedReading(10ms, 14.0),
+           defaultMaxParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(5ms))
+               .expectedReading(11ms, 7.0),
+           defaultMaxParams()
+               .collectionTimeScope(CollectionTimeScope::startup)
+               .expectedReading(0ms, 14.0)));
+
+MetricParams defaultSumParams()
+{
+    return defaultSingleParams().operationType(OperationType::sum);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions,
+    Values(defaultSumParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(100ms))
+               .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5),
+           defaultSumParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(8ms))
+               .expectedReading(16ms, 14. * 2 + 3. * 1 + 7 * 5),
+           defaultSumParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(6ms))
+               .expectedReading(16ms, 3. * 1 + 7 * 5),
+           defaultSumParams()
+               .collectionTimeScope(CollectionTimeScope::startup)
+               .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5)));
+
+MetricParams defaultAvgParams()
+{
+    return defaultSingleParams().operationType(OperationType::avg);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions,
+    Values(defaultAvgParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(100ms))
+               .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.),
+           defaultAvgParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(8ms))
+               .expectedReading(16ms, (14. * 2 + 3. * 1 + 7 * 5) / 8.),
+           defaultAvgParams()
+               .collectionTimeScope(CollectionTimeScope::interval)
+               .collectionDuration(CollectionDuration(6ms))
+               .expectedReading(16ms, (3. * 1 + 7 * 5) / 6.),
+           defaultAvgParams()
+               .collectionTimeScope(CollectionTimeScope::startup)
+               .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.)));
+
+TEST_P(TestMetricCalculationFunctions, calculatesReadingValue)
+{
+    for (auto [timestamp, reading] : GetParam().readings())
+    {
+        sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
+                           reading);
+        clockFake.advance(timestamp);
+    }
+
+    const auto [expectedTimestamp, expectedReading] =
+        GetParam().expectedReading();
+    const auto readings = sut->getReadings();
+
+    EXPECT_THAT(readings, ElementsAre(MetricValue{
+                              "id", "metadata", expectedReading,
+                              ClockFake::toTimestamp(expectedTimestamp)}));
+}
+
+TEST_P(TestMetricCalculationFunctions,
+       calculatedReadingValueWithIntermediateCalculations)
+{
+    for (auto [timestamp, reading] : GetParam().readings())
+    {
+        sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
+                           reading);
+        clockFake.advance(timestamp);
+        sut->getReadings();
+    }
+
+    const auto [expectedTimestamp, expectedReading] =
+        GetParam().expectedReading();
+    const auto readings = sut->getReadings();
+
+    EXPECT_THAT(readings, ElementsAre(MetricValue{
+                              "id", "metadata", expectedReading,
+                              ClockFake::toTimestamp(expectedTimestamp)}));
+}