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