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