xref: /openbmc/telemetry/tests/src/test_metric.cpp (revision c7935fa1)
1 #include "fakes/clock_fake.hpp"
2 #include "helpers.hpp"
3 #include "metric.hpp"
4 #include "mocks/metric_listener_mock.hpp"
5 #include "mocks/sensor_mock.hpp"
6 #include "params/metric_params.hpp"
7 #include "utils/conv_container.hpp"
8 #include "utils/conversion.hpp"
9 #include "utils/tstring.hpp"
10 
11 #include <gmock/gmock.h>
12 
13 using namespace testing;
14 using namespace std::chrono_literals;
15 
16 namespace tstring = utils::tstring;
17 
18 constexpr Milliseconds systemTimestamp = 42ms;
19 
20 class TestMetric : public Test
21 {
22   public:
TestMetric()23     TestMetric()
24     {
25         clockFake.steady.reset();
26         clockFake.system.set(systemTimestamp);
27     }
28 
29     static std::vector<std::shared_ptr<SensorMock>>
makeSensorMocks(size_t amount)30         makeSensorMocks(size_t amount)
31     {
32         std::vector<std::shared_ptr<SensorMock>> result;
33         for (size_t i = 0; i < amount; ++i)
34         {
35             auto& metricMock =
36                 result.emplace_back(std::make_shared<NiceMock<SensorMock>>());
37             ON_CALL(*metricMock, metadata())
38                 .WillByDefault(Return("metadata" + std::to_string(i)));
39         }
40         return result;
41     }
42 
makeSut(const MetricParams & p)43     std::shared_ptr<Metric> makeSut(const MetricParams& p)
44     {
45         return std::make_shared<Metric>(
46             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
47                 sensorMocks),
48             p.operationType(), p.collectionTimeScope(), p.collectionDuration(),
49             std::move(clockFakePtr));
50     }
51 
52     MetricParams params = MetricParams()
53                               .operationType(OperationType::avg)
54                               .collectionTimeScope(CollectionTimeScope::point)
55                               .collectionDuration(CollectionDuration(0ms));
56     std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u);
57     std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>();
58     ClockFake& clockFake = *clockFakePtr;
59     NiceMock<MetricListenerMock> listenerMock;
60     std::shared_ptr<Metric> sut;
61 };
62 
TEST_F(TestMetric,subscribesForSensorDuringInitialization)63 TEST_F(TestMetric, subscribesForSensorDuringInitialization)
64 {
65     sut = makeSut(params);
66 
67     EXPECT_CALL(*sensorMocks.front(),
68                 registerForUpdates(Truly([sut = sut.get()](const auto& a0) {
69         return a0.lock().get() == sut;
70     })));
71 
72     sut->initialize();
73 }
74 
TEST_F(TestMetric,unsubscribesForSensorDuringDeinitialization)75 TEST_F(TestMetric, unsubscribesForSensorDuringDeinitialization)
76 {
77     sut = makeSut(params);
78 
79     EXPECT_CALL(*sensorMocks.front(),
80                 unregisterFromUpdates(Truly([sut = sut.get()](const auto& a0) {
81         return a0.lock().get() == sut;
82     })));
83 
84     sut->deinitialize();
85 }
86 
TEST_F(TestMetric,containsEmptyReadingAfterCreated)87 TEST_F(TestMetric, containsEmptyReadingAfterCreated)
88 {
89     sut = makeSut(params);
90 
91     ASSERT_THAT(sut->getUpdatedReadings(), ElementsAre());
92 }
93 
TEST_F(TestMetric,notifiesRegisteredListenersOnManualUpdateWhenMetricValueChanges)94 TEST_F(TestMetric,
95        notifiesRegisteredListenersOnManualUpdateWhenMetricValueChanges)
96 {
97     sut = makeSut(params.collectionTimeScope(CollectionTimeScope::startup));
98     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
99     sut->registerForUpdates(listenerMock);
100 
101     EXPECT_CALL(listenerMock, metricUpdated()).Times(2);
102 
103     sut->updateReadings(Milliseconds{50u});
104     sut->updateReadings(Milliseconds{100u});
105 }
106 
TEST_F(TestMetric,doesntNotifyRegisteredListenersOnManualUpdateWhenMetricValueDoesntChange)107 TEST_F(TestMetric,
108        doesntNotifyRegisteredListenersOnManualUpdateWhenMetricValueDoesntChange)
109 {
110     sut = makeSut(params.collectionTimeScope(CollectionTimeScope::startup)
111                       .operationType(OperationType::max));
112     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
113     sut->registerForUpdates(listenerMock);
114 
115     EXPECT_CALL(listenerMock, metricUpdated()).Times(0);
116 
117     sut->updateReadings(Milliseconds{50u});
118     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{180}, 11.);
119     sut->updateReadings(Milliseconds{100u});
120 }
121 
122 class TestMetricAfterInitialization : public TestMetric
123 {
124   public:
SetUp()125     void SetUp() override
126     {
127         sut = makeSut(params);
128         sut->initialize();
129     }
130 };
131 
TEST_F(TestMetricAfterInitialization,containsEmptyReading)132 TEST_F(TestMetricAfterInitialization, containsEmptyReading)
133 {
134     ASSERT_THAT(sut->getUpdatedReadings(), ElementsAre());
135 }
136 
TEST_F(TestMetricAfterInitialization,updatesMetricValuesOnSensorUpdate)137 TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate)
138 {
139     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
140 
141     ASSERT_THAT(
142         sut->getUpdatedReadings(),
143         ElementsAre(MetricValue{"metadata0", 31.2,
144                                 std::chrono::duration_cast<Milliseconds>(
145                                     clockFake.system.timestamp())
146                                     .count()}));
147 }
148 
TEST_F(TestMetricAfterInitialization,throwsWhenUpdateIsPerformedOnUnknownSensor)149 TEST_F(TestMetricAfterInitialization,
150        throwsWhenUpdateIsPerformedOnUnknownSensor)
151 {
152     auto sensor = std::make_shared<StrictMock<SensorMock>>();
153     EXPECT_THROW(sut->sensorUpdated(*sensor, Milliseconds{10}, 20.0),
154                  std::out_of_range);
155 }
156 
TEST_F(TestMetricAfterInitialization,dumpsConfiguration)157 TEST_F(TestMetricAfterInitialization, dumpsConfiguration)
158 {
159     namespace ts = utils::tstring;
160 
161     ON_CALL(*sensorMocks.front(), id())
162         .WillByDefault(Return(SensorMock::makeId("service1", "path1")));
163     ON_CALL(*sensorMocks.front(), metadata())
164         .WillByDefault(Return("metadata1"));
165 
166     const auto conf = sut->dumpConfiguration();
167 
168     LabeledMetricParameters expected = {};
169     expected.at_label<ts::OperationType>() = params.operationType();
170     expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope();
171     expected.at_label<ts::CollectionDuration>() = params.collectionDuration();
172     expected.at_label<ts::SensorPath>() = {
173         LabeledSensorInfo("service1", "path1", "metadata1")};
174 
175     EXPECT_THAT(conf, Eq(expected));
176 }
177 
TEST_F(TestMetricAfterInitialization,notifiesRegisteredListeners)178 TEST_F(TestMetricAfterInitialization, notifiesRegisteredListeners)
179 {
180     EXPECT_CALL(listenerMock, metricUpdated());
181 
182     sut->registerForUpdates(listenerMock);
183     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
184 }
185 
TEST_F(TestMetricAfterInitialization,doesntNotifyRegisteredListenersWhenValueDoesntChange)186 TEST_F(TestMetricAfterInitialization,
187        doesntNotifyRegisteredListenersWhenValueDoesntChange)
188 {
189     EXPECT_CALL(listenerMock, metricUpdated());
190 
191     sut->registerForUpdates(listenerMock);
192     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
193     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{70}, 31.2);
194 }
195 
TEST_F(TestMetricAfterInitialization,doesntNotifyAfterUnRegisterListener)196 TEST_F(TestMetricAfterInitialization, doesntNotifyAfterUnRegisterListener)
197 {
198     EXPECT_CALL(listenerMock, metricUpdated()).Times(0);
199 
200     sut->registerForUpdates(listenerMock);
201     sut->unregisterFromUpdates(listenerMock);
202     sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2);
203 }
204 
205 class TestMetricCalculationFunctions :
206     public TestMetric,
207     public WithParamInterface<MetricParams>
208 {
209   public:
SetUp()210     void SetUp() override
211     {
212         sut = makeSut(params.operationType(GetParam().operationType())
213                           .collectionTimeScope(GetParam().collectionTimeScope())
214                           .collectionDuration(GetParam().collectionDuration()));
215     }
216 
defaultReadings()217     static std::vector<std::pair<Milliseconds, double>> defaultReadings()
218     {
219         std::vector<std::pair<Milliseconds, double>> ret;
220         ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN());
221         ret.emplace_back(10ms, 14.);
222         ret.emplace_back(1ms, 3.);
223         ret.emplace_back(5ms, 7.);
224         return ret;
225     }
226 };
227 
defaultCollectionFunctionParams()228 MetricParams defaultCollectionFunctionParams()
229 {
230     return MetricParams()
231         .readings(TestMetricCalculationFunctions::defaultReadings())
232         .expectedReading(systemTimestamp + 16ms, 7.0);
233 }
234 
defaultPointParams()235 MetricParams defaultPointParams()
236 {
237     return defaultCollectionFunctionParams()
238         .collectionTimeScope(CollectionTimeScope::point)
239         .expectedIsTimerRequired(false);
240 }
241 
242 INSTANTIATE_TEST_SUITE_P(
243     TimeScopePointReturnsLastReading, TestMetricCalculationFunctions,
244     Values(defaultPointParams().operationType(OperationType::min),
245            defaultPointParams().operationType(OperationType::max),
246            defaultPointParams().operationType(OperationType::sum),
247            defaultPointParams().operationType(OperationType::avg)));
248 
defaultMinParams()249 MetricParams defaultMinParams()
250 {
251     return defaultCollectionFunctionParams().operationType(OperationType::min);
252 }
253 
254 INSTANTIATE_TEST_SUITE_P(
255     ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions,
256     Values(defaultMinParams()
257                .collectionTimeScope(CollectionTimeScope::interval)
258                .collectionDuration(CollectionDuration(100ms))
259                .expectedReading(systemTimestamp + 16ms, 3.0),
260            defaultMinParams()
261                .collectionTimeScope(CollectionTimeScope::interval)
262                .collectionDuration(CollectionDuration(3ms))
263                .expectedReading(systemTimestamp + 16ms, 7.0),
264            defaultMinParams()
265                .collectionTimeScope(CollectionTimeScope::startup)
266                .expectedReading(systemTimestamp + 16ms, 3.0)
267                .expectedIsTimerRequired(false)));
268 
defaultMaxParams()269 MetricParams defaultMaxParams()
270 {
271     return defaultCollectionFunctionParams().operationType(OperationType::max);
272 }
273 
274 INSTANTIATE_TEST_SUITE_P(
275     ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions,
276     Values(defaultMaxParams()
277                .collectionTimeScope(CollectionTimeScope::interval)
278                .collectionDuration(CollectionDuration(100ms))
279                .expectedReading(systemTimestamp + 16ms, 14.0),
280            defaultMaxParams()
281                .collectionTimeScope(CollectionTimeScope::interval)
282                .collectionDuration(CollectionDuration(6ms))
283                .expectedReading(systemTimestamp + 16ms, 14.0),
284            defaultMaxParams()
285                .collectionTimeScope(CollectionTimeScope::interval)
286                .collectionDuration(CollectionDuration(5ms))
287                .expectedReading(systemTimestamp + 16ms, 7.0),
288            defaultMaxParams()
289                .collectionTimeScope(CollectionTimeScope::startup)
290                .expectedReading(systemTimestamp + 16ms, 14.0)
291                .expectedIsTimerRequired(false)));
292 
defaultSumParams()293 MetricParams defaultSumParams()
294 {
295     return defaultCollectionFunctionParams().operationType(OperationType::sum);
296 }
297 
298 INSTANTIATE_TEST_SUITE_P(
299     ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions,
300     Values(defaultSumParams()
301                .collectionTimeScope(CollectionTimeScope::interval)
302                .collectionDuration(CollectionDuration(100ms))
303                .expectedReading(systemTimestamp + 16ms,
304                                 14. * 0.01 + 3. * 0.001 + 7. * 0.005),
305            defaultSumParams()
306                .collectionTimeScope(CollectionTimeScope::interval)
307                .collectionDuration(CollectionDuration(8ms))
308                .expectedReading(systemTimestamp + 16ms,
309                                 14. * 0.002 + 3. * 0.001 + 7 * 0.005),
310            defaultSumParams()
311                .collectionTimeScope(CollectionTimeScope::interval)
312                .collectionDuration(CollectionDuration(6ms))
313                .expectedReading(systemTimestamp + 16ms, 3. * 0.001 + 7 * 0.005),
314            defaultSumParams()
315                .collectionTimeScope(CollectionTimeScope::startup)
316                .expectedReading(systemTimestamp + 16ms,
317                                 14. * 0.01 + 3. * 0.001 + 7 * 0.005)));
318 
defaultAvgParams()319 MetricParams defaultAvgParams()
320 {
321     return defaultCollectionFunctionParams().operationType(OperationType::avg);
322 }
323 
324 INSTANTIATE_TEST_SUITE_P(
325     ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions,
326     Values(defaultAvgParams()
327                .collectionTimeScope(CollectionTimeScope::interval)
328                .collectionDuration(CollectionDuration(100ms))
329                .expectedReading(systemTimestamp + 16ms,
330                                 (14. * 10 + 3. * 1 + 7 * 5) / 16.),
331            defaultAvgParams()
332                .collectionTimeScope(CollectionTimeScope::interval)
333                .collectionDuration(CollectionDuration(8ms))
334                .expectedReading(systemTimestamp + 16ms,
335                                 (14. * 2 + 3. * 1 + 7 * 5) / 8.),
336            defaultAvgParams()
337                .collectionTimeScope(CollectionTimeScope::interval)
338                .collectionDuration(CollectionDuration(6ms))
339                .expectedReading(systemTimestamp + 16ms, (3. * 1 + 7 * 5) / 6.),
340            defaultAvgParams()
341                .collectionTimeScope(CollectionTimeScope::startup)
342                .expectedReading(systemTimestamp + 16ms,
343                                 (14. * 10 + 3. * 1 + 7 * 5) / 16.)));
344 
TEST_P(TestMetricCalculationFunctions,calculatesReadingValue)345 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue)
346 {
347     for (auto [timestamp, reading] : GetParam().readings())
348     {
349         sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(),
350                            reading);
351         clockFake.advance(timestamp);
352     }
353 
354     const auto [expectedTimestamp,
355                 expectedReading] = GetParam().expectedReading();
356     const auto readings = sut->getUpdatedReadings();
357 
358     EXPECT_THAT(readings, ElementsAre(MetricValue{"metadata0", expectedReading,
359                                                   expectedTimestamp.count()}));
360 }
361 
TEST_P(TestMetricCalculationFunctions,calculatedReadingValueWithIntermediateCalculations)362 TEST_P(TestMetricCalculationFunctions,
363        calculatedReadingValueWithIntermediateCalculations)
364 {
365     for (auto [timestamp, reading] : GetParam().readings())
366     {
367         sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(),
368                            reading);
369         clockFake.advance(timestamp);
370         sut->getUpdatedReadings();
371     }
372 
373     const auto [expectedTimestamp,
374                 expectedReading] = GetParam().expectedReading();
375     const auto readings = sut->getUpdatedReadings();
376 
377     EXPECT_THAT(readings, ElementsAre(MetricValue{"metadata0", expectedReading,
378                                                   expectedTimestamp.count()}));
379 }
380 
TEST_P(TestMetricCalculationFunctions,returnsIsTimerRequired)381 TEST_P(TestMetricCalculationFunctions, returnsIsTimerRequired)
382 {
383     EXPECT_THAT(sut->isTimerRequired(),
384                 Eq(GetParam().expectedIsTimerRequired()));
385 }
386 
387 class TestMetricWithMultipleSensors : public TestMetric
388 {
389   public:
TestMetricWithMultipleSensors()390     TestMetricWithMultipleSensors()
391     {
392         sensorMocks = makeSensorMocks(7u);
393 
394         sut = makeSut(params);
395         sut->initialize();
396     }
397 };
398 
TEST_F(TestMetricWithMultipleSensors,readingsContainsAllReadingsInOrder)399 TEST_F(TestMetricWithMultipleSensors, readingsContainsAllReadingsInOrder)
400 {
401     for (size_t i = 0; i < sensorMocks.size(); ++i)
402     {
403         sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0);
404         sut->getUpdatedReadings();
405     }
406 
407     clockFake.system.set(Milliseconds{72});
408 
409     EXPECT_THAT(sut->getUpdatedReadings(),
410                 ElementsAre(MetricValue{"metadata0", 10.0, 72},
411                             MetricValue{"metadata1", 11.0, 72},
412                             MetricValue{"metadata2", 12.0, 72},
413                             MetricValue{"metadata3", 13.0, 72},
414                             MetricValue{"metadata4", 14.0, 72},
415                             MetricValue{"metadata5", 15.0, 72},
416                             MetricValue{"metadata6", 16.0, 72}));
417 }
418 
TEST_F(TestMetricWithMultipleSensors,readingsContainOnlyAvailableSensors)419 TEST_F(TestMetricWithMultipleSensors, readingsContainOnlyAvailableSensors)
420 {
421     for (auto i : {5u, 3u, 6u, 0u})
422     {
423         sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0);
424         sut->getUpdatedReadings();
425     }
426 
427     clockFake.system.set(Milliseconds{62});
428 
429     EXPECT_THAT(sut->getUpdatedReadings(),
430                 ElementsAre(MetricValue{"metadata5", 15.0, 62},
431                             MetricValue{"metadata3", 13.0, 62},
432                             MetricValue{"metadata6", 16.0, 62},
433                             MetricValue{"metadata0", 10.0, 62}));
434 }
435 
TEST_F(TestMetricWithMultipleSensors,readingsContainsAllReadingsOutOfOrder)436 TEST_F(TestMetricWithMultipleSensors, readingsContainsAllReadingsOutOfOrder)
437 {
438     for (auto i : {6u, 5u, 3u, 4u, 0u, 2u, 1u})
439     {
440         sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0);
441         sut->getUpdatedReadings();
442     }
443 
444     clockFake.system.set(Milliseconds{52});
445 
446     EXPECT_THAT(sut->getUpdatedReadings(),
447                 ElementsAre(MetricValue{"metadata6", 16.0, 52},
448                             MetricValue{"metadata5", 15.0, 52},
449                             MetricValue{"metadata3", 13.0, 52},
450                             MetricValue{"metadata4", 14.0, 52},
451                             MetricValue{"metadata0", 10.0, 52},
452                             MetricValue{"metadata2", 12.0, 52},
453                             MetricValue{"metadata1", 11.0, 52}));
454 }
455