xref: /openbmc/telemetry/tests/src/test_metric.cpp (revision b8cc78ddf9cc87c83176c7bda575ceef2678d00f)
1 #include "fakes/clock_fake.hpp"
2 #include "helpers.hpp"
3 #include "metric.hpp"
4 #include "mocks/sensor_mock.hpp"
5 #include "params/metric_params.hpp"
6 #include "utils/conv_container.hpp"
7 #include "utils/conversion.hpp"
8 #include "utils/tstring.hpp"
9 
10 #include <gmock/gmock.h>
11 
12 using namespace testing;
13 using namespace std::chrono_literals;
14 
15 namespace tstring = utils::tstring;
16 
17 using Timestamp = uint64_t;
18 
19 class TestMetric : public Test
20 {
21   public:
22     static std::vector<std::shared_ptr<SensorMock>>
23         makeSensorMocks(size_t amount)
24     {
25         std::vector<std::shared_ptr<SensorMock>> result;
26         for (size_t i = 0; i < amount; ++i)
27         {
28             auto& metricMock =
29                 result.emplace_back(std::make_shared<NiceMock<SensorMock>>());
30             ON_CALL(*metricMock, metadata()).WillByDefault(Return("metadata"));
31         }
32         return result;
33     }
34 
35     std::shared_ptr<Metric> makeSut(const MetricParams& p)
36     {
37         return std::make_shared<Metric>(
38             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
39                 sensorMocks),
40             p.operationType(), p.id(), p.collectionTimeScope(),
41             p.collectionDuration(), std::move(clockFakePtr));
42     }
43 
44     MetricParams params = MetricParams()
45                               .id("id")
46                               .operationType(OperationType::avg)
47                               .collectionTimeScope(CollectionTimeScope::point)
48                               .collectionDuration(CollectionDuration(0ms));
49     std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u);
50     std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>();
51     ClockFake& clockFake = *clockFakePtr;
52     std::shared_ptr<Metric> sut;
53 };
54 
55 TEST_F(TestMetric, subscribesForSensorDuringInitialization)
56 {
57     sut = makeSut(params);
58 
59     EXPECT_CALL(*sensorMocks.front(),
60                 registerForUpdates(Truly([sut = sut.get()](const auto& a0) {
61                     return a0.lock().get() == sut;
62                 })));
63 
64     sut->initialize();
65 }
66 
67 TEST_F(TestMetric, unsubscribesForSensorDuringDeinitialization)
68 {
69     sut = makeSut(params);
70 
71     EXPECT_CALL(*sensorMocks.front(),
72                 unregisterFromUpdates(Truly([sut = sut.get()](const auto& a0) {
73                     return a0.lock().get() == sut;
74                 })));
75 
76     sut->deinitialize();
77 }
78 
79 TEST_F(TestMetric, containsEmptyReadingAfterCreated)
80 {
81     sut = makeSut(params);
82 
83     ASSERT_THAT(sut->getReadings(),
84                 ElementsAre(MetricValue({"id", "metadata", 0., 0u})));
85 }
86 
87 class TestMetricAfterInitialization : public TestMetric
88 {
89   public:
90     void SetUp() override
91     {
92         sut = makeSut(params);
93         sut->initialize();
94     }
95 };
96 
97 TEST_F(TestMetricAfterInitialization, containsEmptyReading)
98 {
99     ASSERT_THAT(sut->getReadings(),
100                 ElementsAre(MetricValue({"id", "metadata", 0., 0u})));
101 }
102 
103 TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate)
104 {
105     sut->sensorUpdated(*sensorMocks.front(), Timestamp{18}, 31.2);
106 
107     ASSERT_THAT(sut->getReadings(),
108                 ElementsAre(MetricValue{"id", "metadata", 31.2, 18u}));
109 }
110 
111 TEST_F(TestMetricAfterInitialization,
112        throwsWhenUpdateIsPerformedOnUnknownSensor)
113 {
114     auto sensor = std::make_shared<StrictMock<SensorMock>>();
115     EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}), std::out_of_range);
116     EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}, 20.0),
117                  std::out_of_range);
118 }
119 
120 TEST_F(TestMetricAfterInitialization, dumpsConfiguration)
121 {
122     namespace ts = utils::tstring;
123 
124     ON_CALL(*sensorMocks.front(), id())
125         .WillByDefault(Return(SensorMock::makeId("service1", "path1")));
126     ON_CALL(*sensorMocks.front(), metadata())
127         .WillByDefault(Return("metadata1"));
128 
129     const auto conf = sut->dumpConfiguration();
130 
131     LabeledMetricParameters expected = {};
132     expected.at_label<ts::Id>() = params.id();
133     expected.at_label<ts::OperationType>() = params.operationType();
134     expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope();
135     expected.at_label<ts::CollectionDuration>() = params.collectionDuration();
136     expected.at_label<ts::SensorPath>() = {
137         LabeledSensorParameters("service1", "path1", "metadata1")};
138 
139     EXPECT_THAT(conf, Eq(expected));
140 }
141 
142 class TestMetricCalculationFunctions :
143     public TestMetric,
144     public WithParamInterface<MetricParams>
145 {
146   public:
147     void SetUp() override
148     {
149         clockFake.reset();
150         sut = makeSut(params.operationType(GetParam().operationType())
151                           .collectionTimeScope(GetParam().collectionTimeScope())
152                           .collectionDuration(GetParam().collectionDuration()));
153     }
154 
155     static std::vector<std::pair<Milliseconds, double>> defaultReadings()
156     {
157         std::vector<std::pair<Milliseconds, double>> ret;
158         ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN());
159         ret.emplace_back(10ms, 14.);
160         ret.emplace_back(1ms, 3.);
161         ret.emplace_back(5ms, 7.);
162         return ret;
163     }
164 };
165 
166 MetricParams defaultSingleParams()
167 {
168     return MetricParams()
169         .operationType(OperationType::single)
170         .readings(TestMetricCalculationFunctions::defaultReadings())
171         .expectedReading(11ms, 7.0);
172 }
173 
174 INSTANTIATE_TEST_SUITE_P(
175     OperationSingleReturnsLastReading, TestMetricCalculationFunctions,
176     Values(
177         defaultSingleParams().collectionTimeScope(CollectionTimeScope::point),
178         defaultSingleParams()
179             .collectionTimeScope(CollectionTimeScope::interval)
180             .collectionDuration(CollectionDuration(100ms)),
181         defaultSingleParams().collectionTimeScope(
182             CollectionTimeScope::startup)));
183 
184 MetricParams defaultPointParams()
185 {
186     return defaultSingleParams().collectionTimeScope(
187         CollectionTimeScope::point);
188 }
189 
190 INSTANTIATE_TEST_SUITE_P(
191     TimeScopePointReturnsLastReading, TestMetricCalculationFunctions,
192     Values(defaultPointParams().operationType(OperationType::single),
193            defaultPointParams().operationType(OperationType::min),
194            defaultPointParams().operationType(OperationType::max),
195            defaultPointParams().operationType(OperationType::sum),
196            defaultPointParams().operationType(OperationType::avg)));
197 
198 MetricParams defaultMinParams()
199 {
200     return defaultSingleParams().operationType(OperationType::min);
201 }
202 
203 INSTANTIATE_TEST_SUITE_P(
204     ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions,
205     Values(defaultMinParams()
206                .collectionTimeScope(CollectionTimeScope::interval)
207                .collectionDuration(CollectionDuration(100ms))
208                .expectedReading(10ms, 3.0),
209            defaultMinParams()
210                .collectionTimeScope(CollectionTimeScope::interval)
211                .collectionDuration(CollectionDuration(3ms))
212                .expectedReading(13ms, 7.0),
213            defaultMinParams()
214                .collectionTimeScope(CollectionTimeScope::startup)
215                .expectedReading(10ms, 3.0)));
216 
217 MetricParams defaultMaxParams()
218 {
219     return defaultSingleParams().operationType(OperationType::max);
220 }
221 
222 INSTANTIATE_TEST_SUITE_P(
223     ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions,
224     Values(defaultMaxParams()
225                .collectionTimeScope(CollectionTimeScope::interval)
226                .collectionDuration(CollectionDuration(100ms))
227                .expectedReading(0ms, 14.0),
228            defaultMaxParams()
229                .collectionTimeScope(CollectionTimeScope::interval)
230                .collectionDuration(CollectionDuration(6ms))
231                .expectedReading(10ms, 14.0),
232            defaultMaxParams()
233                .collectionTimeScope(CollectionTimeScope::interval)
234                .collectionDuration(CollectionDuration(5ms))
235                .expectedReading(11ms, 7.0),
236            defaultMaxParams()
237                .collectionTimeScope(CollectionTimeScope::startup)
238                .expectedReading(0ms, 14.0)));
239 
240 MetricParams defaultSumParams()
241 {
242     return defaultSingleParams().operationType(OperationType::sum);
243 }
244 
245 INSTANTIATE_TEST_SUITE_P(
246     ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions,
247     Values(defaultSumParams()
248                .collectionTimeScope(CollectionTimeScope::interval)
249                .collectionDuration(CollectionDuration(100ms))
250                .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5),
251            defaultSumParams()
252                .collectionTimeScope(CollectionTimeScope::interval)
253                .collectionDuration(CollectionDuration(8ms))
254                .expectedReading(16ms, 14. * 2 + 3. * 1 + 7 * 5),
255            defaultSumParams()
256                .collectionTimeScope(CollectionTimeScope::interval)
257                .collectionDuration(CollectionDuration(6ms))
258                .expectedReading(16ms, 3. * 1 + 7 * 5),
259            defaultSumParams()
260                .collectionTimeScope(CollectionTimeScope::startup)
261                .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5)));
262 
263 MetricParams defaultAvgParams()
264 {
265     return defaultSingleParams().operationType(OperationType::avg);
266 }
267 
268 INSTANTIATE_TEST_SUITE_P(
269     ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions,
270     Values(defaultAvgParams()
271                .collectionTimeScope(CollectionTimeScope::interval)
272                .collectionDuration(CollectionDuration(100ms))
273                .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.),
274            defaultAvgParams()
275                .collectionTimeScope(CollectionTimeScope::interval)
276                .collectionDuration(CollectionDuration(8ms))
277                .expectedReading(16ms, (14. * 2 + 3. * 1 + 7 * 5) / 8.),
278            defaultAvgParams()
279                .collectionTimeScope(CollectionTimeScope::interval)
280                .collectionDuration(CollectionDuration(6ms))
281                .expectedReading(16ms, (3. * 1 + 7 * 5) / 6.),
282            defaultAvgParams()
283                .collectionTimeScope(CollectionTimeScope::startup)
284                .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.)));
285 
286 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue)
287 {
288     for (auto [timestamp, reading] : GetParam().readings())
289     {
290         sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
291                            reading);
292         clockFake.advance(timestamp);
293     }
294 
295     const auto [expectedTimestamp, expectedReading] =
296         GetParam().expectedReading();
297     const auto readings = sut->getReadings();
298 
299     EXPECT_THAT(readings, ElementsAre(MetricValue{
300                               "id", "metadata", expectedReading,
301                               ClockFake::toTimestamp(expectedTimestamp)}));
302 }
303 
304 TEST_P(TestMetricCalculationFunctions,
305        calculatedReadingValueWithIntermediateCalculations)
306 {
307     for (auto [timestamp, reading] : GetParam().readings())
308     {
309         sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
310                            reading);
311         clockFake.advance(timestamp);
312         sut->getReadings();
313     }
314 
315     const auto [expectedTimestamp, expectedReading] =
316         GetParam().expectedReading();
317     const auto readings = sut->getReadings();
318 
319     EXPECT_THAT(readings, ElementsAre(MetricValue{
320                               "id", "metadata", expectedReading,
321                               ClockFake::toTimestamp(expectedTimestamp)}));
322 }
323