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] = std::chrono::high_resolution_clock::now();
209             }),
210                     InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
211         }
212 
213         auto start = std::chrono::high_resolution_clock::now();
214 
215         size_t idx = 0;
216         for (const double value : GetParam().initialValues)
217         {
218             sut->sensorUpdated(*sensorMocks[idx], 0ms, value);
219             idx++;
220         }
221 
222         for (const auto& [index, value, sleepAfter] : GetParam().updates)
223         {
224             ASSERT_LT(index, GetParam().initialValues.size())
225                 << "Initial value was not specified for sensor with index: "
226                 << index;
227             sut->sensorUpdated(*sensorMocks[index], 42ms, value);
228             sleep(sleepAfter);
229         }
230 
231         EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
232         for (const auto& [index, value, waitMin] : GetParam().expected)
233         {
234             EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
235         }
236     }
237 };
238 
239 class TestNumericThresholdNoDwellTime : public TestNumericThresholdCommon
240 {
241   public:
SetUp()242     void SetUp() override
243     {
244         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
245         {
246             ON_CALL(*sensorMocks.at(idx), getName())
247                 .WillByDefault(Return(sensorNames[idx]));
248         }
249 
250         makeThreshold(0ms, GetParam().direction, GetParam().thresholdValue);
251     }
252 };
253 
254 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdNoDwellTime,
255                          Values(NumericParams()
256                                     .ThresholdValue(90.0)
257                                     .Direction(numeric::Direction::increasing)
258                                     .InitialValues({80.0})
259                                     .Updates({{0, 89.0}})
260                                     .Expected({}),
261                                 NumericParams()
262                                     .ThresholdValue(90.0)
263                                     .Direction(numeric::Direction::increasing)
264                                     .InitialValues({80.0})
265                                     .Updates({{0, 91.0}})
266                                     .Expected({{0, 91.0}}),
267                                 NumericParams()
268                                     .ThresholdValue(90.0)
269                                     .Direction(numeric::Direction::increasing)
270                                     .InitialValues({80.0})
271                                     .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0}})
272                                     .Expected({{0, 99.0}, {0, 98.0}}),
273                                 NumericParams()
274                                     .ThresholdValue(90.0)
275                                     .Direction(numeric::Direction::increasing)
276                                     .InitialValues({80.0, 100.0})
277                                     .Updates({{0, 99.0}, {1, 98.0}})
278                                     .Expected({{0, 99.0}}),
279                                 NumericParams()
280                                     .ThresholdValue(90.0)
281                                     .Direction(numeric::Direction::decreasing)
282                                     .InitialValues({100.0})
283                                     .Updates({{0, 91.0}})
284                                     .Expected({}),
285                                 NumericParams()
286                                     .ThresholdValue(90.0)
287                                     .Direction(numeric::Direction::decreasing)
288                                     .InitialValues({100.0})
289                                     .Updates({{0, 80.0}})
290                                     .Expected({{0, 80.0}}),
291                                 NumericParams()
292                                     .ThresholdValue(90.0)
293                                     .Direction(numeric::Direction::decreasing)
294                                     .InitialValues({100.0})
295                                     .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0}})
296                                     .Expected({{0, 80.0}, {0, 85.0}}),
297                                 NumericParams()
298                                     .ThresholdValue(90.0)
299                                     .Direction(numeric::Direction::decreasing)
300                                     .InitialValues({100.0, 99.0})
301                                     .Updates({{0, 80.0}, {1, 88.0}})
302                                     .Expected({{0, 80.0}, {1, 88.0}}),
303                                 NumericParams()
304                                     .ThresholdValue(90.0)
305                                     .Direction(numeric::Direction::either)
306                                     .InitialValues({98.0})
307                                     .Updates({{0, 91.0}})
308                                     .Expected({}),
309                                 NumericParams()
310                                     .ThresholdValue(90.0)
311                                     .Direction(numeric::Direction::either)
312                                     .InitialValues({100.0})
313                                     .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0}})
314                                     .Expected({{0, 80.0}, {0, 91.0}}),
315                                 NumericParams()
316                                     .ThresholdValue(90.0)
317                                     .Direction(numeric::Direction::either)
318                                     .InitialValues({100.0, 80.0})
319                                     .Updates({{0, 85.0}, {1, 91.0}})
320                                     .Expected({{0, 85.0}, {1, 91.0}}),
321                                 NumericParams()
322                                     .ThresholdValue(30.0)
323                                     .Direction(numeric::Direction::decreasing)
324                                     .InitialValues({40.0})
325                                     .Updates({{0, 30.0}, {0, 20.0}})
326                                     .Expected({{0, 20.0}}),
327                                 NumericParams()
328                                     .ThresholdValue(30.0)
329                                     .Direction(numeric::Direction::decreasing)
330                                     .InitialValues({20.0})
331                                     .Updates({{0, 30.0}, {0, 20.0}})
332                                     .Expected({}),
333                                 NumericParams()
334                                     .ThresholdValue(30.0)
335                                     .Direction(numeric::Direction::either)
336                                     .InitialValues({20.0})
337                                     .Updates({{0, 30.0}, {0, 20.0}})
338                                     .Expected({}),
339                                 NumericParams()
340                                     .ThresholdValue(30.0)
341                                     .Direction(numeric::Direction::increasing)
342                                     .InitialValues({20.0})
343                                     .Updates({{0, 30.0}, {0, 40.0}})
344                                     .Expected({{0, 40.0}}),
345                                 NumericParams()
346                                     .ThresholdValue(30.0)
347                                     .Direction(numeric::Direction::increasing)
348                                     .InitialValues({40.0})
349                                     .Updates({{0, 30.0}, {0, 40.0}})
350                                     .Expected({}),
351                                 NumericParams()
352                                     .ThresholdValue(30.0)
353                                     .Direction(numeric::Direction::either)
354                                     .InitialValues({40.0})
355                                     .Updates({{0, 30.0}, {0, 40.0}})
356                                     .Expected({})));
357 
TEST_P(TestNumericThresholdNoDwellTime,senorsIsUpdatedMultipleTimes)358 TEST_P(TestNumericThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
359 {
360     testBodySensorIsUpdatedMultipleTimes();
361 }
362 
363 class TestNumericThresholdWithDwellTime : public TestNumericThresholdCommon
364 {
365   public:
SetUp()366     void SetUp() override
367     {
368         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
369         {
370             ON_CALL(*sensorMocks.at(idx), getName())
371                 .WillByDefault(Return(sensorNames[idx]));
372         }
373 
374         makeThreshold(GetParam().dwellTime, GetParam().direction,
375                       GetParam().thresholdValue);
376     }
377 };
378 
379 INSTANTIATE_TEST_SUITE_P(
380     SleepAfterEveryUpdate, TestNumericThresholdWithDwellTime,
381     Values(NumericParams()
382                .DwellTime(200ms)
383                .ThresholdValue(90.0)
384                .Direction(numeric::Direction::increasing)
385                .InitialValues({80.0})
386                .Updates({{0, 89.0, 200ms}})
387                .Expected({}),
388            NumericParams()
389                .DwellTime(200ms)
390                .ThresholdValue(90.0)
391                .Direction(numeric::Direction::increasing)
392                .InitialValues({80.0})
393                .Updates({{0, 91.0, 200ms}})
394                .Expected({{0, 91.0, 200ms}}),
395            NumericParams()
396                .DwellTime(200ms)
397                .ThresholdValue(90.0)
398                .Direction(numeric::Direction::increasing)
399                .InitialValues({80.0})
400                .Updates({{0, 99.0, 200ms}, {0, 80.0, 100ms}, {0, 98.0, 200ms}})
401                .Expected({{0, 99.0, 200ms}, {0, 98.0, 500ms}}),
402            NumericParams()
403                .DwellTime(200ms)
404                .ThresholdValue(90.0)
405                .Direction(numeric::Direction::increasing)
406                .InitialValues({80.0, 99.0})
407                .Updates({{0, 100.0, 100ms}, {1, 86.0, 100ms}})
408                .Expected({{0, 100.0, 200ms}}),
409            NumericParams()
410                .DwellTime(200ms)
411                .ThresholdValue(90.0)
412                .Direction(numeric::Direction::decreasing)
413                .InitialValues({100.0})
414                .Updates({{0, 91.0, 200ms}})
415                .Expected({}),
416            NumericParams()
417                .DwellTime(200ms)
418                .ThresholdValue(90.0)
419                .Direction(numeric::Direction::decreasing)
420                .InitialValues({100.0})
421                .Updates({{0, 80.0, 200ms}})
422                .Expected({{0, 80.0, 200ms}}),
423            NumericParams()
424                .DwellTime(200ms)
425                .ThresholdValue(90.0)
426                .Direction(numeric::Direction::decreasing)
427                .InitialValues({100.0})
428                .Updates({{0, 80.0, 200ms}, {0, 99.0, 100ms}, {0, 85.0, 200ms}})
429                .Expected({{0, 80.0, 200ms}, {0, 85.0, 500ms}}),
430            NumericParams()
431                .DwellTime(200ms)
432                .ThresholdValue(90.0)
433                .Direction(numeric::Direction::decreasing)
434                .InitialValues({100.0, 99.0})
435                .Updates({{0, 80.0, 200ms}, {1, 88.0, 200ms}})
436                .Expected({{0, 80.0, 200ms}, {1, 88.0, 400ms}}),
437            NumericParams()
438                .DwellTime(200ms)
439                .ThresholdValue(90.0)
440                .Direction(numeric::Direction::either)
441                .InitialValues({98.0})
442                .Updates({{0, 91.0, 200ms}})
443                .Expected({}),
444            NumericParams()
445                .DwellTime(200ms)
446                .ThresholdValue(90.0)
447                .Direction(numeric::Direction::either)
448                .InitialValues({100.0})
449                .Updates({{0, 80.0, 100ms}, {0, 85.0, 100ms}, {0, 91.0, 200ms}})
450                .Expected({{0, 80.0, 200ms}, {0, 91.0, 400ms}}),
451            NumericParams()
452                .DwellTime(200ms)
453                .ThresholdValue(90.0)
454                .Direction(numeric::Direction::either)
455                .InitialValues({100.0, 80.0})
456                .Updates({{0, 85.0, 100ms}, {1, 91.0, 200ms}})
457                .Expected({{0, 85.0, 200ms}, {1, 91.0, 300ms}})));
458 
459 INSTANTIATE_TEST_SUITE_P(
460     SleepAfterLastUpdate, TestNumericThresholdWithDwellTime,
461     Values(NumericParams()
462                .DwellTime(200ms)
463                .ThresholdValue(90.0)
464                .Direction(numeric::Direction::increasing)
465                .InitialValues({80.0})
466                .Updates({{0, 89.0, 300ms}})
467                .Expected({}),
468            NumericParams()
469                .DwellTime(200ms)
470                .ThresholdValue(90.0)
471                .Direction(numeric::Direction::increasing)
472                .InitialValues({80.0})
473                .Updates({{0, 91.0, 300ms}})
474                .Expected({{0, 91.0, 200ms}}),
475            NumericParams()
476                .DwellTime(200ms)
477                .ThresholdValue(90.0)
478                .Direction(numeric::Direction::increasing)
479                .InitialValues({80.0})
480                .Updates({{0, 99.0}, {0, 80.0}, {0, 98.0, 300ms}})
481                .Expected({{0, 98.0, 200ms}}),
482            NumericParams()
483                .DwellTime(200ms)
484                .ThresholdValue(90.0)
485                .Direction(numeric::Direction::increasing)
486                .InitialValues({80.0, 99.0})
487                .Updates({{0, 100.0}, {1, 98.0, 300ms}})
488                .Expected({{0, 100.0, 200ms}}),
489            NumericParams()
490                .DwellTime(200ms)
491                .ThresholdValue(90.0)
492                .Direction(numeric::Direction::decreasing)
493                .InitialValues({100.0})
494                .Updates({{0, 91.0, 300ms}})
495                .Expected({}),
496            NumericParams()
497                .DwellTime(200ms)
498                .ThresholdValue(90.0)
499                .Direction(numeric::Direction::decreasing)
500                .InitialValues({100.0})
501                .Updates({{0, 80.0, 300ms}})
502                .Expected({{0, 80.0, 200ms}}),
503            NumericParams()
504                .DwellTime(200ms)
505                .ThresholdValue(90.0)
506                .Direction(numeric::Direction::decreasing)
507                .InitialValues({100.0})
508                .Updates({{0, 80.0}, {0, 99.0}, {0, 85.0, 300ms}})
509                .Expected({{0, 85.0, 200ms}}),
510            NumericParams()
511                .DwellTime(200ms)
512                .ThresholdValue(90.0)
513                .Direction(numeric::Direction::decreasing)
514                .InitialValues({100.0, 99.0})
515                .Updates({{0, 80.0}, {1, 88.0, 300ms}})
516                .Expected({{0, 80.0, 200ms}, {1, 88.0, 200ms}}),
517            NumericParams()
518                .DwellTime(200ms)
519                .ThresholdValue(90.0)
520                .Direction(numeric::Direction::either)
521                .InitialValues({98.0})
522                .Updates({{0, 91.0, 300ms}})
523                .Expected({}),
524            NumericParams()
525                .DwellTime(200ms)
526                .ThresholdValue(90.0)
527                .Direction(numeric::Direction::either)
528                .InitialValues({100.0})
529                .Updates({{0, 80.0}, {0, 85.0}, {0, 91.0, 300ms}})
530                .Expected({{0, 91.0, 200ms}}),
531            NumericParams()
532                .DwellTime(200ms)
533                .ThresholdValue(90.0)
534                .Direction(numeric::Direction::either)
535                .InitialValues({100.0, 80.0})
536                .Updates({{0, 85.0}, {1, 91.0, 300ms}})
537                .Expected({{0, 85.0, 200ms}, {1, 91.0, 200ms}})));
538 
TEST_P(TestNumericThresholdWithDwellTime,senorsIsUpdatedMultipleTimes)539 TEST_P(TestNumericThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
540 {
541     testBodySensorIsUpdatedMultipleTimes();
542 }
543