xref: /openbmc/telemetry/tests/src/test_metric.cpp (revision 94f71c5190b64bb47aa34cdce4eb4cca71d36faa)
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 defaultSingleParams()
177 {
178     return MetricParams()
179         .operationType(OperationType::single)
180         .readings(TestMetricCalculationFunctions::defaultReadings())
181         .expectedReading(systemTimestamp + 16ms, 7.0);
182 }
183 
184 INSTANTIATE_TEST_SUITE_P(
185     OperationSingleReturnsLastReading, TestMetricCalculationFunctions,
186     Values(
187         defaultSingleParams().collectionTimeScope(CollectionTimeScope::point),
188         defaultSingleParams()
189             .collectionTimeScope(CollectionTimeScope::interval)
190             .collectionDuration(CollectionDuration(100ms)),
191         defaultSingleParams().collectionTimeScope(
192             CollectionTimeScope::startup)));
193 
194 MetricParams defaultPointParams()
195 {
196     return defaultSingleParams().collectionTimeScope(
197         CollectionTimeScope::point);
198 }
199 
200 INSTANTIATE_TEST_SUITE_P(
201     TimeScopePointReturnsLastReading, TestMetricCalculationFunctions,
202     Values(defaultPointParams().operationType(OperationType::single),
203            defaultPointParams().operationType(OperationType::min),
204            defaultPointParams().operationType(OperationType::max),
205            defaultPointParams().operationType(OperationType::sum),
206            defaultPointParams().operationType(OperationType::avg)));
207 
208 MetricParams defaultMinParams()
209 {
210     return defaultSingleParams().operationType(OperationType::min);
211 }
212 
213 INSTANTIATE_TEST_SUITE_P(
214     ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions,
215     Values(defaultMinParams()
216                .collectionTimeScope(CollectionTimeScope::interval)
217                .collectionDuration(CollectionDuration(100ms))
218                .expectedReading(systemTimestamp + 16ms, 3.0),
219            defaultMinParams()
220                .collectionTimeScope(CollectionTimeScope::interval)
221                .collectionDuration(CollectionDuration(3ms))
222                .expectedReading(systemTimestamp + 16ms, 7.0),
223            defaultMinParams()
224                .collectionTimeScope(CollectionTimeScope::startup)
225                .expectedReading(systemTimestamp + 16ms, 3.0)));
226 
227 MetricParams defaultMaxParams()
228 {
229     return defaultSingleParams().operationType(OperationType::max);
230 }
231 
232 INSTANTIATE_TEST_SUITE_P(
233     ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions,
234     Values(defaultMaxParams()
235                .collectionTimeScope(CollectionTimeScope::interval)
236                .collectionDuration(CollectionDuration(100ms))
237                .expectedReading(systemTimestamp + 16ms, 14.0),
238            defaultMaxParams()
239                .collectionTimeScope(CollectionTimeScope::interval)
240                .collectionDuration(CollectionDuration(6ms))
241                .expectedReading(systemTimestamp + 16ms, 14.0),
242            defaultMaxParams()
243                .collectionTimeScope(CollectionTimeScope::interval)
244                .collectionDuration(CollectionDuration(5ms))
245                .expectedReading(systemTimestamp + 16ms, 7.0),
246            defaultMaxParams()
247                .collectionTimeScope(CollectionTimeScope::startup)
248                .expectedReading(systemTimestamp + 16ms, 14.0)));
249 
250 MetricParams defaultSumParams()
251 {
252     return defaultSingleParams().operationType(OperationType::sum);
253 }
254 
255 INSTANTIATE_TEST_SUITE_P(
256     ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions,
257     Values(defaultSumParams()
258                .collectionTimeScope(CollectionTimeScope::interval)
259                .collectionDuration(CollectionDuration(100ms))
260                .expectedReading(systemTimestamp + 16ms,
261                                 14. * 0.01 + 3. * 0.001 + 7. * 0.005),
262            defaultSumParams()
263                .collectionTimeScope(CollectionTimeScope::interval)
264                .collectionDuration(CollectionDuration(8ms))
265                .expectedReading(systemTimestamp + 16ms,
266                                 14. * 0.002 + 3. * 0.001 + 7 * 0.005),
267            defaultSumParams()
268                .collectionTimeScope(CollectionTimeScope::interval)
269                .collectionDuration(CollectionDuration(6ms))
270                .expectedReading(systemTimestamp + 16ms, 3. * 0.001 + 7 * 0.005),
271            defaultSumParams()
272                .collectionTimeScope(CollectionTimeScope::startup)
273                .expectedReading(systemTimestamp + 16ms,
274                                 14. * 0.01 + 3. * 0.001 + 7 * 0.005)));
275 
276 MetricParams defaultAvgParams()
277 {
278     return defaultSingleParams().operationType(OperationType::avg);
279 }
280 
281 INSTANTIATE_TEST_SUITE_P(
282     ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions,
283     Values(defaultAvgParams()
284                .collectionTimeScope(CollectionTimeScope::interval)
285                .collectionDuration(CollectionDuration(100ms))
286                .expectedReading(systemTimestamp + 16ms,
287                                 (14. * 10 + 3. * 1 + 7 * 5) / 16.),
288            defaultAvgParams()
289                .collectionTimeScope(CollectionTimeScope::interval)
290                .collectionDuration(CollectionDuration(8ms))
291                .expectedReading(systemTimestamp + 16ms,
292                                 (14. * 2 + 3. * 1 + 7 * 5) / 8.),
293            defaultAvgParams()
294                .collectionTimeScope(CollectionTimeScope::interval)
295                .collectionDuration(CollectionDuration(6ms))
296                .expectedReading(systemTimestamp + 16ms, (3. * 1 + 7 * 5) / 6.),
297            defaultAvgParams()
298                .collectionTimeScope(CollectionTimeScope::startup)
299                .expectedReading(systemTimestamp + 16ms,
300                                 (14. * 10 + 3. * 1 + 7 * 5) / 16.)));
301 
302 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue)
303 {
304     for (auto [timestamp, reading] : GetParam().readings())
305     {
306         sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(),
307                            reading);
308         clockFake.advance(timestamp);
309     }
310 
311     const auto [expectedTimestamp, expectedReading] =
312         GetParam().expectedReading();
313     const auto readings = sut->getReadings();
314 
315     EXPECT_THAT(readings,
316                 ElementsAre(MetricValue{"id", "metadata", expectedReading,
317                                         expectedTimestamp.count()}));
318 }
319 
320 TEST_P(TestMetricCalculationFunctions,
321        calculatedReadingValueWithIntermediateCalculations)
322 {
323     for (auto [timestamp, reading] : GetParam().readings())
324     {
325         sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(),
326                            reading);
327         clockFake.advance(timestamp);
328         sut->getReadings();
329     }
330 
331     const auto [expectedTimestamp, expectedReading] =
332         GetParam().expectedReading();
333     const auto readings = sut->getReadings();
334 
335     EXPECT_THAT(readings,
336                 ElementsAre(MetricValue{"id", "metadata", expectedReading,
337                                         expectedTimestamp.count()}));
338 }
339