xref: /openbmc/telemetry/tests/src/test_numeric_threshold.cpp (revision 3c8aa74d87f885c6a3bbf71c00d93699b5e91ab1)
1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "mocks/clock_mock.hpp"
4 #include "mocks/sensor_mock.hpp"
5 #include "mocks/trigger_action_mock.hpp"
6 #include "numeric_threshold.hpp"
7 #include "utils/conv_container.hpp"
8 
9 #include <gmock/gmock.h>
10 
11 using namespace testing;
12 using namespace std::chrono_literals;
13 
14 class TestNumericThreshold : public Test
15 {
16   public:
17     std::vector<std::shared_ptr<SensorMock>> sensorMocks = {
18         std::make_shared<NiceMock<SensorMock>>(),
19         std::make_shared<NiceMock<SensorMock>>()};
20     std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"};
21     std::unique_ptr<TriggerActionMock> actionMockPtr =
22         std::make_unique<StrictMock<TriggerActionMock>>();
23     TriggerActionMock& actionMock = *actionMockPtr;
24     std::shared_ptr<NumericThreshold> sut;
25     std::string triggerId = "MyTrigger";
26     std::unique_ptr<NiceMock<ClockMock>> clockMockPtr =
27         std::make_unique<NiceMock<ClockMock>>();
28 
makeThreshold(Milliseconds dwellTime,numeric::Direction direction,double thresholdValue,numeric::Type type=numeric::Type::lowerWarning)29     void makeThreshold(Milliseconds dwellTime, numeric::Direction direction,
30                        double thresholdValue,
31                        numeric::Type type = numeric::Type::lowerWarning)
32     {
33         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
34         actions.push_back(std::move(actionMockPtr));
35 
36         sut = std::make_shared<NumericThreshold>(
37             DbusEnvironment::getIoc(), triggerId,
38             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
39                 sensorMocks),
40             std::move(actions), dwellTime, direction, thresholdValue, type,
41             std::move(clockMockPtr));
42     }
43 
SetUp()44     void SetUp() override
45     {
46         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
47         {
48             ON_CALL(*sensorMocks.at(idx), getName())
49                 .WillByDefault(Return(sensorNames[idx]));
50         }
51 
52         makeThreshold(0ms, numeric::Direction::increasing, 90.0,
53                       numeric::Type::upperCritical);
54     }
55 };
56 
TEST_F(TestNumericThreshold,initializeThresholdExpectAllSensorsAreRegistered)57 TEST_F(TestNumericThreshold, initializeThresholdExpectAllSensorsAreRegistered)
58 {
59     for (auto& sensor : sensorMocks)
60     {
61         EXPECT_CALL(*sensor,
62                     registerForUpdates(Truly([sut = sut.get()](const auto& x) {
63                         return x.lock().get() == sut;
64                     })));
65     }
66 
67     sut->initialize();
68 }
69 
TEST_F(TestNumericThreshold,thresholdIsNotInitializeExpectNoActionCommit)70 TEST_F(TestNumericThreshold, thresholdIsNotInitializeExpectNoActionCommit)
71 {
72     EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0);
73 }
74 
TEST_F(TestNumericThreshold,getLabeledParamsReturnsCorrectly)75 TEST_F(TestNumericThreshold, getLabeledParamsReturnsCorrectly)
76 {
77     LabeledThresholdParam expected = numeric::LabeledThresholdParam(
78         numeric::Type::upperCritical, 0, numeric::Direction::increasing, 90.0);
79     EXPECT_EQ(sut->getThresholdParam(), expected);
80 }
81 
82 struct NumericParams
83 {
84     struct UpdateParams
85     {
86         size_t sensor;
87         double value;
88         Milliseconds sleepAfter;
89 
UpdateParamsNumericParams::UpdateParams90         UpdateParams(size_t sensor, double value,
91                      Milliseconds sleepAfter = 0ms) :
92             sensor(sensor), value(value), sleepAfter(sleepAfter)
93         {}
94     };
95 
96     struct ExpectedParams
97     {
98         size_t sensor;
99         double value;
100         Milliseconds waitMin;
101 
ExpectedParamsNumericParams::ExpectedParams102         ExpectedParams(size_t sensor, double value,
103                        Milliseconds waitMin = 0ms) :
104             sensor(sensor), value(value), waitMin(waitMin)
105         {}
106     };
107 
DirectionNumericParams108     NumericParams& Direction(numeric::Direction val)
109     {
110         direction = val;
111         return *this;
112     }
113 
UpdatesNumericParams114     NumericParams& Updates(std::vector<UpdateParams> val)
115     {
116         updates = std::move(val);
117         return *this;
118     }
119 
ExpectedNumericParams120     NumericParams& Expected(std::vector<ExpectedParams> val)
121     {
122         expected = std::move(val);
123         return *this;
124     }
125 
ThresholdValueNumericParams126     NumericParams& ThresholdValue(double val)
127     {
128         thresholdValue = val;
129         return *this;
130     }
131 
DwellTimeNumericParams132     NumericParams& DwellTime(Milliseconds val)
133     {
134         dwellTime = std::move(val);
135         return *this;
136     }
137 
InitialValuesNumericParams138     NumericParams& InitialValues(std::vector<double> val)
139     {
140         initialValues = std::move(val);
141         return *this;
142     }
143 
PrintTo(const NumericParams & o,std::ostream * os)144     friend void PrintTo(const NumericParams& o, std::ostream* os)
145     {
146         *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
147         *os << ", ThresholdValue: " << o.thresholdValue;
148         *os << ", Direction: " << static_cast<int>(o.direction);
149         *os << ", InitialValues: [ ";
150         size_t idx = 0;
151         for (const double value : o.initialValues)
152         {
153             *os << "{ SensorIndex: " << idx << ", Value: " << value << " }, ";
154             idx++;
155         }
156         *os << " ], Updates: [ ";
157         for (const auto& [index, value, sleepAfter] : o.updates)
158         {
159             *os << "{ SensorIndex: " << index << ", Value: " << value
160                 << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
161         }
162         *os << " ], Expected: [ ";
163         for (const auto& [index, value, waitMin] : o.expected)
164         {
165             *os << "{ SensorIndex: " << index << ", Value: " << value
166                 << ", waitMin: " << waitMin.count() << "ms }, ";
167         }
168         *os << " ] }";
169     }
170 
171     numeric::Direction direction;
172     double thresholdValue = 0.0;
173     Milliseconds dwellTime = 0ms;
174     std::vector<UpdateParams> updates;
175     std::vector<ExpectedParams> expected;
176     std::vector<double> initialValues;
177 };
178 
179 class TestNumericThresholdCommon :
180     public TestNumericThreshold,
181     public WithParamInterface<NumericParams>
182 {
183   public:
sleep(Milliseconds duration)184     void sleep(Milliseconds duration)
185     {
186         if (duration != 0ms)
187         {
188             DbusEnvironment::sleepFor(duration);
189         }
190     }
191 
testBodySensorIsUpdatedMultipleTimes()192     void testBodySensorIsUpdatedMultipleTimes()
193     {
194         std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
195             timestamps(sensorMocks.size());
196 
197         sut->initialize();
198 
199         InSequence seq;
200 
201         for (const auto& [index, value, waitMin] : GetParam().expected)
202         {
203             EXPECT_CALL(actionMock,
204                         commit(triggerId, Eq(std::nullopt), sensorNames[index],
205                                _, TriggerValue(value)))
206                 .WillOnce(DoAll(
207                     InvokeWithoutArgs([idx = index, &timestamps] {
208                         timestamps[idx] =
209                             std::chrono::high_resolution_clock::now();
210                     }),
211                     InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
212         }
213 
214         auto start = std::chrono::high_resolution_clock::now();
215 
216         size_t idx = 0;
217         for (const double value : GetParam().initialValues)
218         {
219             sut->sensorUpdated(*sensorMocks[idx], 0ms, value);
220             idx++;
221         }
222 
223         for (const auto& [index, value, sleepAfter] : GetParam().updates)
224         {
225             ASSERT_LT(index, GetParam().initialValues.size())
226                 << "Initial value was not specified for sensor with index: "
227                 << index;
228             sut->sensorUpdated(*sensorMocks[index], 42ms, value);
229             sleep(sleepAfter);
230         }
231 
232         EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
233         for (const auto& [index, value, waitMin] : GetParam().expected)
234         {
235             EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
236         }
237     }
238 };
239 
240 class TestNumericThresholdNoDwellTime : public TestNumericThresholdCommon
241 {
242   public:
SetUp()243     void SetUp() override
244     {
245         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
246         {
247             ON_CALL(*sensorMocks.at(idx), getName())
248                 .WillByDefault(Return(sensorNames[idx]));
249         }
250 
251         makeThreshold(0ms, GetParam().direction, GetParam().thresholdValue);
252     }
253 };
254 
255 INSTANTIATE_TEST_SUITE_P(
256     _, TestNumericThresholdNoDwellTime,
257     Values(
258         NumericParams()
259             .ThresholdValue(90.0)
260             .Direction(numeric::Direction::increasing)
261             .InitialValues({80.0})
262             .Updates({{0, 89.0}})
263             .Expected({}),
264         NumericParams()
265             .ThresholdValue(90.0)
266             .Direction(numeric::Direction::increasing)
267             .InitialValues({80.0})
268             .Updates({{0, 91.0}})
269             .Expected({{0, 91.0}}),
270         NumericParams()
271             .ThresholdValue(90.0)
272             .Direction(numeric::Direction::increasing)
273             .InitialValues({80.0})
274             .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0}})
275             .Expected({{0, 99.0}, {0, 98.0}}),
276         NumericParams()
277             .ThresholdValue(90.0)
278             .Direction(numeric::Direction::increasing)
279             .InitialValues({80.0, 100.0})
280             .Updates({{0, 99.0}, {1, 98.0}})
281             .Expected({{0, 99.0}}),
282         NumericParams()
283             .ThresholdValue(90.0)
284             .Direction(numeric::Direction::decreasing)
285             .InitialValues({100.0})
286             .Updates({{0, 91.0}})
287             .Expected({}),
288         NumericParams()
289             .ThresholdValue(90.0)
290             .Direction(numeric::Direction::decreasing)
291             .InitialValues({100.0})
292             .Updates({{0, 80.0}})
293             .Expected({{0, 80.0}}),
294         NumericParams()
295             .ThresholdValue(90.0)
296             .Direction(numeric::Direction::decreasing)
297             .InitialValues({100.0})
298             .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0}})
299             .Expected({{0, 80.0}, {0, 85.0}}),
300         NumericParams()
301             .ThresholdValue(90.0)
302             .Direction(numeric::Direction::decreasing)
303             .InitialValues({100.0, 99.0})
304             .Updates({{0, 80.0}, {1, 88.0}})
305             .Expected({{0, 80.0}, {1, 88.0}}),
306         NumericParams()
307             .ThresholdValue(90.0)
308             .Direction(numeric::Direction::either)
309             .InitialValues({98.0})
310             .Updates({{0, 91.0}})
311             .Expected({}),
312         NumericParams()
313             .ThresholdValue(90.0)
314             .Direction(numeric::Direction::either)
315             .InitialValues({100.0})
316             .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0}})
317             .Expected({{0, 80.0}, {0, 91.0}}),
318         NumericParams()
319             .ThresholdValue(90.0)
320             .Direction(numeric::Direction::either)
321             .InitialValues({100.0, 80.0})
322             .Updates({{0, 85.0}, {1, 91.0}})
323             .Expected({{0, 85.0}, {1, 91.0}}),
324         NumericParams()
325             .ThresholdValue(30.0)
326             .Direction(numeric::Direction::decreasing)
327             .InitialValues({40.0})
328             .Updates({{0, 30.0}, {0, 20.0}})
329             .Expected({{0, 20.0}}),
330         NumericParams()
331             .ThresholdValue(30.0)
332             .Direction(numeric::Direction::decreasing)
333             .InitialValues({20.0})
334             .Updates({{0, 30.0}, {0, 20.0}})
335             .Expected({}),
336         NumericParams()
337             .ThresholdValue(30.0)
338             .Direction(numeric::Direction::either)
339             .InitialValues({20.0})
340             .Updates({{0, 30.0}, {0, 20.0}})
341             .Expected({}),
342         NumericParams()
343             .ThresholdValue(30.0)
344             .Direction(numeric::Direction::increasing)
345             .InitialValues({20.0})
346             .Updates({{0, 30.0}, {0, 40.0}})
347             .Expected({{0, 40.0}}),
348         NumericParams()
349             .ThresholdValue(30.0)
350             .Direction(numeric::Direction::increasing)
351             .InitialValues({40.0})
352             .Updates({{0, 30.0}, {0, 40.0}})
353             .Expected({}),
354         NumericParams()
355             .ThresholdValue(30.0)
356             .Direction(numeric::Direction::either)
357             .InitialValues({40.0})
358             .Updates({{0, 30.0}, {0, 40.0}})
359             .Expected({})));
360 
TEST_P(TestNumericThresholdNoDwellTime,senorsIsUpdatedMultipleTimes)361 TEST_P(TestNumericThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
362 {
363     testBodySensorIsUpdatedMultipleTimes();
364 }
365 
366 class TestNumericThresholdWithDwellTime : public TestNumericThresholdCommon
367 {
368   public:
SetUp()369     void SetUp() override
370     {
371         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
372         {
373             ON_CALL(*sensorMocks.at(idx), getName())
374                 .WillByDefault(Return(sensorNames[idx]));
375         }
376 
377         makeThreshold(GetParam().dwellTime, GetParam().direction,
378                       GetParam().thresholdValue);
379     }
380 };
381 
382 INSTANTIATE_TEST_SUITE_P(
383     SleepAfterEveryUpdate, TestNumericThresholdWithDwellTime,
384     Values(
385         NumericParams()
386             .DwellTime(200ms)
387             .ThresholdValue(90.0)
388             .Direction(numeric::Direction::increasing)
389             .InitialValues({80.0})
390             .Updates({{0, 89.0, 200ms}})
391             .Expected({}),
392         NumericParams()
393             .DwellTime(200ms)
394             .ThresholdValue(90.0)
395             .Direction(numeric::Direction::increasing)
396             .InitialValues({80.0})
397             .Updates({{0, 91.0, 200ms}})
398             .Expected({{0, 91.0, 200ms}}),
399         NumericParams()
400             .DwellTime(200ms)
401             .ThresholdValue(90.0)
402             .Direction(numeric::Direction::increasing)
403             .InitialValues({80.0})
404             .Updates({{0, 99.0, 210ms}, {0, 80.0, 100ms}, {0, 98.0, 200ms}})
405             .Expected({{0, 99.0, 210ms}, {0, 98.0, 510ms}}),
406         NumericParams()
407             .DwellTime(200ms)
408             .ThresholdValue(90.0)
409             .Direction(numeric::Direction::increasing)
410             .InitialValues({80.0, 99.0})
411             .Updates({{0, 100.0, 100ms}, {1, 86.0, 100ms}})
412             .Expected({{0, 100.0, 200ms}}),
413         NumericParams()
414             .DwellTime(200ms)
415             .ThresholdValue(90.0)
416             .Direction(numeric::Direction::decreasing)
417             .InitialValues({100.0})
418             .Updates({{0, 91.0, 200ms}})
419             .Expected({}),
420         NumericParams()
421             .DwellTime(200ms)
422             .ThresholdValue(90.0)
423             .Direction(numeric::Direction::decreasing)
424             .InitialValues({100.0})
425             .Updates({{0, 80.0, 200ms}})
426             .Expected({{0, 80.0, 200ms}}),
427         NumericParams()
428             .DwellTime(200ms)
429             .ThresholdValue(90.0)
430             .Direction(numeric::Direction::decreasing)
431             .InitialValues({100.0})
432             .Updates({{0, 80.0, 210ms}, {0, 99.0, 100ms}, {0, 85.0, 200ms}})
433             .Expected({{0, 80.0, 210ms}, {0, 85.0, 510ms}}),
434         NumericParams()
435             .DwellTime(200ms)
436             .ThresholdValue(90.0)
437             .Direction(numeric::Direction::decreasing)
438             .InitialValues({100.0, 99.0})
439             .Updates({{0, 80.0, 200ms}, {1, 88.0, 200ms}})
440             .Expected({{0, 80.0, 200ms}, {1, 88.0, 400ms}}),
441         NumericParams()
442             .DwellTime(200ms)
443             .ThresholdValue(90.0)
444             .Direction(numeric::Direction::either)
445             .InitialValues({98.0})
446             .Updates({{0, 91.0, 200ms}})
447             .Expected({}),
448         NumericParams()
449             .DwellTime(200ms)
450             .ThresholdValue(90.0)
451             .Direction(numeric::Direction::either)
452             .InitialValues({100.0})
453             .Updates({{0, 80.0, 100ms}, {0, 85.0, 110ms}, {0, 91.0, 200ms}})
454             .Expected({{0, 80.0, 200ms}, {0, 91.0, 410ms}}),
455         NumericParams()
456             .DwellTime(200ms)
457             .ThresholdValue(90.0)
458             .Direction(numeric::Direction::either)
459             .InitialValues({100.0, 80.0})
460             .Updates({{0, 85.0, 100ms}, {1, 91.0, 200ms}})
461             .Expected({{0, 85.0, 200ms}, {1, 91.0, 300ms}})));
462 
463 INSTANTIATE_TEST_SUITE_P(
464     SleepAfterLastUpdate, TestNumericThresholdWithDwellTime,
465     Values(
466         NumericParams()
467             .DwellTime(200ms)
468             .ThresholdValue(90.0)
469             .Direction(numeric::Direction::increasing)
470             .InitialValues({80.0})
471             .Updates({{0, 89.0, 300ms}})
472             .Expected({}),
473         NumericParams()
474             .DwellTime(200ms)
475             .ThresholdValue(90.0)
476             .Direction(numeric::Direction::increasing)
477             .InitialValues({80.0})
478             .Updates({{0, 91.0, 300ms}})
479             .Expected({{0, 91.0, 200ms}}),
480         NumericParams()
481             .DwellTime(200ms)
482             .ThresholdValue(90.0)
483             .Direction(numeric::Direction::increasing)
484             .InitialValues({80.0})
485             .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0, 300ms}})
486             .Expected({{0, 98.0, 200ms}}),
487         NumericParams()
488             .DwellTime(200ms)
489             .ThresholdValue(90.0)
490             .Direction(numeric::Direction::increasing)
491             .InitialValues({80.0, 99.0})
492             .Updates({{0, 100.0}, {1, 98.0, 300ms}})
493             .Expected({{0, 100.0, 200ms}}),
494         NumericParams()
495             .DwellTime(200ms)
496             .ThresholdValue(90.0)
497             .Direction(numeric::Direction::decreasing)
498             .InitialValues({100.0})
499             .Updates({{0, 91.0, 300ms}})
500             .Expected({}),
501         NumericParams()
502             .DwellTime(200ms)
503             .ThresholdValue(90.0)
504             .Direction(numeric::Direction::decreasing)
505             .InitialValues({100.0})
506             .Updates({{0, 80.0, 300ms}})
507             .Expected({{0, 80.0, 200ms}}),
508         NumericParams()
509             .DwellTime(200ms)
510             .ThresholdValue(90.0)
511             .Direction(numeric::Direction::decreasing)
512             .InitialValues({100.0})
513             .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0, 300ms}})
514             .Expected({{0, 85.0, 200ms}}),
515         NumericParams()
516             .DwellTime(200ms)
517             .ThresholdValue(90.0)
518             .Direction(numeric::Direction::decreasing)
519             .InitialValues({100.0, 99.0})
520             .Updates({{0, 80.0}, {1, 88.0, 300ms}})
521             .Expected({{0, 80.0, 200ms}, {1, 88.0, 200ms}}),
522         NumericParams()
523             .DwellTime(200ms)
524             .ThresholdValue(90.0)
525             .Direction(numeric::Direction::either)
526             .InitialValues({98.0})
527             .Updates({{0, 91.0, 300ms}})
528             .Expected({}),
529         NumericParams()
530             .DwellTime(200ms)
531             .ThresholdValue(90.0)
532             .Direction(numeric::Direction::either)
533             .InitialValues({100.0})
534             .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0, 300ms}})
535             .Expected({{0, 91.0, 200ms}}),
536         NumericParams()
537             .DwellTime(200ms)
538             .ThresholdValue(90.0)
539             .Direction(numeric::Direction::either)
540             .InitialValues({100.0, 80.0})
541             .Updates({{0, 85.0}, {1, 91.0, 300ms}})
542             .Expected({{0, 85.0, 200ms}, {1, 91.0, 200ms}})));
543 
TEST_P(TestNumericThresholdWithDwellTime,senorsIsUpdatedMultipleTimes)544 TEST_P(TestNumericThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
545 {
546     testBodySensorIsUpdatedMultipleTimes();
547 }
548