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