1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "mocks/sensor_mock.hpp"
4 #include "mocks/trigger_action_mock.hpp"
5 #include "numeric_threshold.hpp"
6 #include "utils/conv_container.hpp"
7 
8 #include <gmock/gmock.h>
9 
10 using namespace testing;
11 using namespace std::chrono_literals;
12 
13 class TestNumericThreshold : public Test
14 {
15   public:
16     std::vector<std::shared_ptr<SensorMock>> sensorMocks = {
17         std::make_shared<NiceMock<SensorMock>>(),
18         std::make_shared<NiceMock<SensorMock>>()};
19     std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"};
20     std::unique_ptr<TriggerActionMock> actionMockPtr =
21         std::make_unique<StrictMock<TriggerActionMock>>();
22     TriggerActionMock& actionMock = *actionMockPtr;
23     std::shared_ptr<NumericThreshold> sut;
24 
25     void makeThreshold(Milliseconds dwellTime, numeric::Direction direction,
26                        double thresholdValue,
27                        numeric::Type type = numeric::Type::lowerWarning)
28     {
29         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
30         actions.push_back(std::move(actionMockPtr));
31 
32         sut = std::make_shared<NumericThreshold>(
33             DbusEnvironment::getIoc(),
34             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
35                 sensorMocks),
36             std::move(actions), dwellTime, direction, thresholdValue, type);
37     }
38 
39     void SetUp() override
40     {
41         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
42         {
43             ON_CALL(*sensorMocks.at(idx), getName())
44                 .WillByDefault(Return(sensorNames[idx]));
45         }
46 
47         makeThreshold(0ms, numeric::Direction::increasing, 90.0,
48                       numeric::Type::upperCritical);
49     }
50 };
51 
52 TEST_F(TestNumericThreshold, initializeThresholdExpectAllSensorsAreRegistered)
53 {
54     for (auto& sensor : sensorMocks)
55     {
56         EXPECT_CALL(*sensor,
57                     registerForUpdates(Truly([sut = sut.get()](const auto& x) {
58                         return x.lock().get() == sut;
59                     })));
60     }
61 
62     sut->initialize();
63 }
64 
65 TEST_F(TestNumericThreshold, thresholdIsNotInitializeExpectNoActionCommit)
66 {
67     EXPECT_CALL(actionMock, commit(_, _, _)).Times(0);
68 }
69 
70 TEST_F(TestNumericThreshold, getLabeledParamsReturnsCorrectly)
71 {
72     LabeledThresholdParam expected = numeric::LabeledThresholdParam(
73         numeric::Type::upperCritical, 0, numeric::Direction::increasing, 90.0);
74     EXPECT_EQ(sut->getThresholdParam(), expected);
75 }
76 
77 struct NumericParams
78 {
79     NumericParams& Direction(numeric::Direction val)
80     {
81         direction = val;
82         return *this;
83     }
84 
85     NumericParams&
86         Updates(std::vector<std::tuple<size_t, Milliseconds, double>> val)
87     {
88         updates = std::move(val);
89         return *this;
90     }
91 
92     NumericParams&
93         Expected(std::vector<std::tuple<size_t, Milliseconds, double>> val)
94     {
95         expected = std::move(val);
96         return *this;
97     }
98 
99     friend void PrintTo(const NumericParams& o, std::ostream* os)
100     {
101         *os << "{ Direction: " << static_cast<int>(o.direction)
102             << ", Updates: ";
103         for (const auto& [index, timestamp, value] : o.updates)
104         {
105             *os << "{ SensorIndex: " << index
106                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
107                 << " }, ";
108         }
109         *os << "Expected: ";
110         for (const auto& [index, timestamp, value] : o.expected)
111         {
112             *os << "{ SensorIndex: " << index
113                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
114                 << " }, ";
115         }
116         *os << " }";
117     }
118 
119     numeric::Direction direction;
120     std::vector<std::tuple<size_t, Milliseconds, double>> updates;
121     std::vector<std::tuple<size_t, Milliseconds, double>> expected;
122 };
123 
124 class TestNumericThresholdNoDwellTime :
125     public TestNumericThreshold,
126     public WithParamInterface<NumericParams>
127 {
128   public:
129     void SetUp() override
130     {
131         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
132         {
133             ON_CALL(*sensorMocks.at(idx), getName())
134                 .WillByDefault(Return(sensorNames[idx]));
135         }
136 
137         makeThreshold(0ms, GetParam().direction, 90.0);
138     }
139 };
140 
141 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdNoDwellTime,
142                          Values(NumericParams()
143                                     .Direction(numeric::Direction::increasing)
144                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
145                                     .Expected({}),
146                                 NumericParams()
147                                     .Direction(numeric::Direction::increasing)
148                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
149                                     .Expected({{0, 2ms, 91.0}}),
150                                 NumericParams()
151                                     .Direction(numeric::Direction::increasing)
152                                     .Updates({{0, 1ms, 80.0},
153                                               {0, 2ms, 99.0},
154                                               {0, 3ms, 80.0},
155                                               {0, 4ms, 98.0}})
156                                     .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}),
157                                 NumericParams()
158                                     .Direction(numeric::Direction::increasing)
159                                     .Updates({{0, 1ms, 80.0},
160                                               {0, 2ms, 99.0},
161                                               {1, 3ms, 100.0},
162                                               {1, 4ms, 98.0}})
163                                     .Expected({{0, 2ms, 99.0}}),
164                                 NumericParams()
165                                     .Direction(numeric::Direction::decreasing)
166                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
167                                     .Expected({}),
168                                 NumericParams()
169                                     .Direction(numeric::Direction::decreasing)
170                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
171                                     .Expected({{0, 2ms, 80.0}}),
172                                 NumericParams()
173                                     .Direction(numeric::Direction::decreasing)
174                                     .Updates({{0, 1ms, 100.0},
175                                               {0, 2ms, 80.0},
176                                               {0, 3ms, 99.0},
177                                               {0, 4ms, 85.0}})
178                                     .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}),
179                                 NumericParams()
180                                     .Direction(numeric::Direction::decreasing)
181                                     .Updates({{0, 1ms, 100.0},
182                                               {0, 2ms, 80.0},
183                                               {1, 3ms, 99.0},
184                                               {1, 4ms, 88.0}})
185                                     .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
186                                 NumericParams()
187                                     .Direction(numeric::Direction::either)
188                                     .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
189                                     .Expected({}),
190                                 NumericParams()
191                                     .Direction(numeric::Direction::either)
192                                     .Updates({{0, 1ms, 100.0},
193                                               {0, 2ms, 80.0},
194                                               {0, 3ms, 85.0},
195                                               {0, 4ms, 91.0}})
196                                     .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}),
197                                 NumericParams()
198                                     .Direction(numeric::Direction::either)
199                                     .Updates({{0, 1ms, 100.0},
200                                               {1, 2ms, 80.0},
201                                               {0, 3ms, 85.0},
202                                               {1, 4ms, 91.0}})
203                                     .Expected({{0, 3ms, 85.0},
204                                                {1, 4ms, 91.0}})));
205 
206 TEST_P(TestNumericThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
207 {
208     InSequence seq;
209     for (const auto& [index, timestamp, value] : GetParam().expected)
210     {
211         EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
212     }
213 
214     sut->initialize();
215     for (const auto& [index, timestamp, value] : GetParam().updates)
216     {
217         sut->sensorUpdated(*sensorMocks[index], timestamp, value);
218     }
219 }
220 
221 class TestNumericThresholdWithDwellTime :
222     public TestNumericThreshold,
223     public WithParamInterface<NumericParams>
224 {
225   public:
226     void SetUp() override
227     {
228         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
229         {
230             ON_CALL(*sensorMocks.at(idx), getName())
231                 .WillByDefault(Return(sensorNames[idx]));
232         }
233 
234         makeThreshold(2ms, GetParam().direction, 90.0);
235     }
236 
237     void sleep()
238     {
239         DbusEnvironment::sleepFor(4ms);
240     }
241 };
242 
243 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime,
244                          Values(NumericParams()
245                                     .Direction(numeric::Direction::increasing)
246                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
247                                     .Expected({}),
248                                 NumericParams()
249                                     .Direction(numeric::Direction::increasing)
250                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
251                                     .Expected({{0, 2ms, 91.0}}),
252                                 NumericParams()
253                                     .Direction(numeric::Direction::increasing)
254                                     .Updates({{0, 1ms, 80.0},
255                                               {0, 2ms, 99.0},
256                                               {0, 3ms, 80.0},
257                                               {0, 4ms, 98.0}})
258                                     .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}),
259                                 NumericParams()
260                                     .Direction(numeric::Direction::increasing)
261                                     .Updates({{0, 1ms, 80.0},
262                                               {1, 2ms, 99.0},
263                                               {0, 3ms, 100.0},
264                                               {1, 4ms, 86.0}})
265                                     .Expected({{0, 3ms, 100.0}}),
266                                 NumericParams()
267                                     .Direction(numeric::Direction::decreasing)
268                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
269                                     .Expected({}),
270                                 NumericParams()
271                                     .Direction(numeric::Direction::decreasing)
272                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
273                                     .Expected({{0, 2ms, 80.0}}),
274                                 NumericParams()
275                                     .Direction(numeric::Direction::decreasing)
276                                     .Updates({{0, 1ms, 100.0},
277                                               {0, 2ms, 80.0},
278                                               {0, 3ms, 99.0},
279                                               {0, 4ms, 85.0}})
280                                     .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}),
281                                 NumericParams()
282                                     .Direction(numeric::Direction::decreasing)
283                                     .Updates({{0, 1ms, 100.0},
284                                               {0, 2ms, 80.0},
285                                               {1, 3ms, 99.0},
286                                               {1, 4ms, 88.0}})
287                                     .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
288                                 NumericParams()
289                                     .Direction(numeric::Direction::either)
290                                     .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
291                                     .Expected({}),
292                                 NumericParams()
293                                     .Direction(numeric::Direction::either)
294                                     .Updates({{0, 1ms, 100.0},
295                                               {0, 2ms, 80.0},
296                                               {0, 3ms, 85.0},
297                                               {0, 4ms, 91.0}})
298                                     .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}),
299                                 NumericParams()
300                                     .Direction(numeric::Direction::either)
301                                     .Updates({{0, 1ms, 100.0},
302                                               {1, 2ms, 80.0},
303                                               {0, 3ms, 85.0},
304                                               {1, 4ms, 91.0}})
305                                     .Expected({{0, 3ms, 85.0},
306                                                {1, 4ms, 91.0}})));
307 
308 TEST_P(TestNumericThresholdWithDwellTime,
309        senorsIsUpdatedMultipleTimesSleepAfterEveryUpdate)
310 {
311     InSequence seq;
312     for (const auto& [index, timestamp, value] : GetParam().expected)
313     {
314         EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
315     }
316 
317     sut->initialize();
318     for (const auto& [index, timestamp, value] : GetParam().updates)
319     {
320         sut->sensorUpdated(*sensorMocks[index], timestamp, value);
321         sleep();
322     }
323 }
324 
325 class TestNumericThresholdWithDwellTime2 :
326     public TestNumericThreshold,
327     public WithParamInterface<NumericParams>
328 {
329   public:
330     void SetUp() override
331     {
332         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
333         {
334             ON_CALL(*sensorMocks.at(idx), getName())
335                 .WillByDefault(Return(sensorNames[idx]));
336         }
337 
338         makeThreshold(2ms, GetParam().direction, 90.0);
339     }
340 
341     void sleep()
342     {
343         DbusEnvironment::sleepFor(4ms);
344     }
345 };
346 
347 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime2,
348                          Values(NumericParams()
349                                     .Direction(numeric::Direction::increasing)
350                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
351                                     .Expected({}),
352                                 NumericParams()
353                                     .Direction(numeric::Direction::increasing)
354                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}})
355                                     .Expected({{0, 2ms, 91.0}}),
356                                 NumericParams()
357                                     .Direction(numeric::Direction::increasing)
358                                     .Updates({{0, 1ms, 80.0},
359                                               {0, 2ms, 99.0},
360                                               {0, 3ms, 80.0},
361                                               {0, 4ms, 98.0}})
362                                     .Expected({{0, 4ms, 98.0}}),
363                                 NumericParams()
364                                     .Direction(numeric::Direction::increasing)
365                                     .Updates({{0, 1ms, 80.0},
366                                               {1, 2ms, 99.0},
367                                               {0, 3ms, 100.0},
368                                               {1, 4ms, 98.0}})
369                                     .Expected({{0, 3ms, 100.0}}),
370                                 NumericParams()
371                                     .Direction(numeric::Direction::decreasing)
372                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}})
373                                     .Expected({}),
374                                 NumericParams()
375                                     .Direction(numeric::Direction::decreasing)
376                                     .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}})
377                                     .Expected({{0, 2ms, 80.0}}),
378                                 NumericParams()
379                                     .Direction(numeric::Direction::decreasing)
380                                     .Updates({{0, 1ms, 100.0},
381                                               {0, 2ms, 80.0},
382                                               {0, 3ms, 99.0},
383                                               {0, 4ms, 85.0}})
384                                     .Expected({{0, 4ms, 85.0}}),
385                                 NumericParams()
386                                     .Direction(numeric::Direction::decreasing)
387                                     .Updates({{0, 1ms, 100.0},
388                                               {0, 2ms, 80.0},
389                                               {1, 3ms, 99.0},
390                                               {1, 4ms, 88.0}})
391                                     .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}),
392                                 NumericParams()
393                                     .Direction(numeric::Direction::either)
394                                     .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}})
395                                     .Expected({}),
396                                 NumericParams()
397                                     .Direction(numeric::Direction::either)
398                                     .Updates({{0, 1ms, 100.0},
399                                               {0, 2ms, 80.0},
400                                               {0, 3ms, 85.0},
401                                               {0, 4ms, 91.0}})
402                                     .Expected({{0, 4ms, 91.0}}),
403                                 NumericParams()
404                                     .Direction(numeric::Direction::either)
405                                     .Updates({{0, 1ms, 100.0},
406                                               {1, 2ms, 80.0},
407                                               {0, 3ms, 85.0},
408                                               {1, 4ms, 91.0}})
409                                     .Expected({{0, 3ms, 85.0},
410                                                {1, 4ms, 91.0}})));
411 
412 TEST_P(TestNumericThresholdWithDwellTime2,
413        senorsIsUpdatedMultipleTimesSleepAfterLastUpdate)
414 {
415     InSequence seq;
416     for (const auto& [index, timestamp, value] : GetParam().expected)
417     {
418         EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value));
419     }
420 
421     sut->initialize();
422     for (const auto& [index, timestamp, value] : GetParam().updates)
423     {
424         sut->sensorUpdated(*sensorMocks[index], timestamp, value);
425     }
426     sleep();
427 }
428