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, ×tamps] {
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