1 #include "dbus_environment.hpp"
2 #include "discrete_threshold.hpp"
3 #include "helpers.hpp"
4 #include "mocks/clock_mock.hpp"
5 #include "mocks/sensor_mock.hpp"
6 #include "mocks/trigger_action_mock.hpp"
7 #include "types/duration_types.hpp"
8 #include "utils/conv_container.hpp"
9
10 #include <gmock/gmock.h>
11
12 using namespace testing;
13 using namespace std::chrono_literals;
14
15 class TestDiscreteThreshold : public Test
16 {
17 public:
18 std::vector<std::shared_ptr<SensorMock>> sensorMocks = {
19 std::make_shared<NiceMock<SensorMock>>(),
20 std::make_shared<NiceMock<SensorMock>>()};
21 std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"};
22 std::unique_ptr<TriggerActionMock> actionMockPtr =
23 std::make_unique<StrictMock<TriggerActionMock>>();
24 TriggerActionMock& actionMock = *actionMockPtr;
25 std::shared_ptr<DiscreteThreshold> sut;
26 std::string triggerId = "MyTrigger";
27 std::unique_ptr<NiceMock<ClockMock>> clockMockPtr =
28 std::make_unique<NiceMock<ClockMock>>();
29
30 std::shared_ptr<DiscreteThreshold>
makeThreshold(Milliseconds dwellTime,std::string thresholdValue,discrete::Severity severity=discrete::Severity::ok,std::string thresholdName="treshold name")31 makeThreshold(Milliseconds dwellTime, std::string thresholdValue,
32 discrete::Severity severity = discrete::Severity::ok,
33 std::string thresholdName = "treshold name")
34 {
35 std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
36 actions.push_back(std::move(actionMockPtr));
37
38 return std::make_shared<DiscreteThreshold>(
39 DbusEnvironment::getIoc(), triggerId,
40 utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
41 sensorMocks),
42 std::move(actions), dwellTime, thresholdValue, thresholdName,
43 severity, std::move(clockMockPtr));
44 }
45
SetUp()46 void SetUp() override
47 {
48 for (size_t idx = 0; idx < sensorMocks.size(); idx++)
49 {
50 ON_CALL(*sensorMocks.at(idx), getName())
51 .WillByDefault(Return(sensorNames[idx]));
52 }
53
54 sut = makeThreshold(0ms, "90.0", discrete::Severity::critical);
55 }
56 };
57
TEST_F(TestDiscreteThreshold,initializeThresholdExpectAllSensorsAreRegistered)58 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered)
59 {
60 for (auto& sensor : sensorMocks)
61 {
62 EXPECT_CALL(*sensor,
63 registerForUpdates(Truly([sut = sut.get()](const auto& x) {
64 return x.lock().get() == sut;
65 })));
66 }
67
68 sut->initialize();
69 }
70
TEST_F(TestDiscreteThreshold,thresholdIsNotInitializeExpectNoActionCommit)71 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit)
72 {
73 EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0);
74 }
75
76 class TestDiscreteThresholdValues :
77 public TestDiscreteThreshold,
78 public WithParamInterface<std::string>
79 {};
80
81 INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdValues,
82 Values("90", ".90", "90.123", "0.0"));
83
TEST_P(TestDiscreteThresholdValues,thresholdValueIsNumericAndStoredCorrectly)84 TEST_P(TestDiscreteThresholdValues, thresholdValueIsNumericAndStoredCorrectly)
85 {
86 sut = makeThreshold(0ms, GetParam(), discrete::Severity::critical);
87 LabeledThresholdParam expected = discrete::LabeledThresholdParam(
88 "treshold name", discrete::Severity::critical, 0, GetParam());
89 EXPECT_EQ(sut->getThresholdParam(), expected);
90 }
91
92 class TestBadDiscreteThresholdValues :
93 public TestDiscreteThreshold,
94 public WithParamInterface<std::string>
95 {};
96
97 INSTANTIATE_TEST_SUITE_P(_, TestBadDiscreteThresholdValues,
98 Values("90ad", "ab.90", "x90", "On", "Off", ""));
99
TEST_P(TestBadDiscreteThresholdValues,throwsWhenNotNumericValues)100 TEST_P(TestBadDiscreteThresholdValues, throwsWhenNotNumericValues)
101 {
102 EXPECT_THROW(makeThreshold(0ms, GetParam()), std::invalid_argument);
103 }
104
105 class TestDiscreteThresholdInit : public TestDiscreteThreshold
106 {
SetUp()107 void SetUp() override {}
108 };
109
TEST_F(TestDiscreteThresholdInit,nonEmptyNameIsNotChanged)110 TEST_F(TestDiscreteThresholdInit, nonEmptyNameIsNotChanged)
111 {
112 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "non-empty");
113 EXPECT_THAT(
114 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam())
115 .at_label<utils::tstring::UserId>(),
116 Eq("non-empty"));
117 }
118
TEST_F(TestDiscreteThresholdInit,emptyNameIsChanged)119 TEST_F(TestDiscreteThresholdInit, emptyNameIsChanged)
120 {
121 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "");
122 EXPECT_THAT(
123 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam())
124 .at_label<utils::tstring::UserId>(),
125 Not(Eq("")));
126 }
127
128 struct DiscreteParams
129 {
130 struct UpdateParams
131 {
132 size_t sensor;
133 double value;
134 Milliseconds sleepAfter;
135
UpdateParamsDiscreteParams::UpdateParams136 UpdateParams(size_t sensor, double value,
137 Milliseconds sleepAfter = 0ms) :
138 sensor(sensor), value(value), sleepAfter(sleepAfter)
139 {}
140 };
141
142 struct ExpectedParams
143 {
144 size_t sensor;
145 double value;
146 Milliseconds waitMin;
147
ExpectedParamsDiscreteParams::ExpectedParams148 ExpectedParams(size_t sensor, double value,
149 Milliseconds waitMin = 0ms) :
150 sensor(sensor), value(value), waitMin(waitMin)
151 {}
152 };
153
UpdatesDiscreteParams154 DiscreteParams& Updates(std::vector<UpdateParams> val)
155 {
156 updates = std::move(val);
157 return *this;
158 }
159
ExpectedDiscreteParams160 DiscreteParams& Expected(std::vector<ExpectedParams> val)
161 {
162 expected = std::move(val);
163 return *this;
164 }
165
ThresholdValueDiscreteParams166 DiscreteParams& ThresholdValue(std::string val)
167 {
168 thresholdValue = std::move(val);
169 return *this;
170 }
171
DwellTimeDiscreteParams172 DiscreteParams& DwellTime(Milliseconds val)
173 {
174 dwellTime = std::move(val);
175 return *this;
176 }
177
PrintTo(const DiscreteParams & o,std::ostream * os)178 friend void PrintTo(const DiscreteParams& o, std::ostream* os)
179 {
180 *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
181 *os << ", ThresholdValue: " << o.thresholdValue;
182 *os << ", Updates: [ ";
183 for (const auto& [index, value, sleepAfter] : o.updates)
184 {
185 *os << "{ SensorIndex: " << index << ", Value: " << value
186 << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
187 }
188 *os << " ] Expected: [ ";
189 for (const auto& [index, value, waitMin] : o.expected)
190 {
191 *os << "{ SensorIndex: " << index << ", Value: " << value
192 << ", waitMin: " << waitMin.count() << "ms }, ";
193 }
194 *os << " ] }";
195 }
196
197 std::vector<UpdateParams> updates;
198 std::vector<ExpectedParams> expected;
199 std::string thresholdValue = "0.0";
200 Milliseconds dwellTime = 0ms;
201 };
202
203 class TestDiscreteThresholdCommon :
204 public TestDiscreteThreshold,
205 public WithParamInterface<DiscreteParams>
206 {
207 public:
sleep(Milliseconds duration)208 void sleep(Milliseconds duration)
209 {
210 if (duration != 0ms)
211 {
212 DbusEnvironment::sleepFor(duration);
213 }
214 }
215
testBodySensorIsUpdatedMultipleTimes()216 void testBodySensorIsUpdatedMultipleTimes()
217 {
218 std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
219 timestamps(sensorMocks.size());
220
221 sut->initialize();
222
223 InSequence seq;
224
225 for (const auto& [index, value, waitMin] : GetParam().expected)
226 {
227 EXPECT_CALL(actionMock,
228 commit(triggerId, Optional(StrEq("treshold name")),
229 sensorNames[index], _,
230 TriggerValue(GetParam().thresholdValue)))
231 .WillOnce(DoAll(
232 InvokeWithoutArgs([idx = index, ×tamps] {
233 timestamps[idx] = std::chrono::high_resolution_clock::now();
234 }),
235 InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
236 }
237
238 auto start = std::chrono::high_resolution_clock::now();
239
240 for (const auto& [index, value, sleepAfter] : GetParam().updates)
241 {
242 sut->sensorUpdated(*sensorMocks[index], 42ms, value);
243 sleep(sleepAfter);
244 }
245
246 EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
247 for (const auto& [index, value, waitMin] : GetParam().expected)
248 {
249 EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
250 }
251 }
252 };
253
254 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon
255 {
256 public:
SetUp()257 void SetUp() override
258 {
259 for (size_t idx = 0; idx < sensorMocks.size(); idx++)
260 {
261 ON_CALL(*sensorMocks.at(idx), getName())
262 .WillByDefault(Return(sensorNames[idx]));
263 }
264
265 sut = makeThreshold(0ms, GetParam().thresholdValue);
266 }
267 };
268
269 INSTANTIATE_TEST_SUITE_P(
270 _, TestDiscreteThresholdNoDwellTime,
271 Values(DiscreteParams()
272 .ThresholdValue("90.0")
273 .Updates({{0, 80.0}, {0, 89.0}})
274 .Expected({}),
275 DiscreteParams()
276 .ThresholdValue("90.0")
277 .Updates({{0, 80.0}, {0, 90.0}, {0, 80.0}, {0, 90.0}})
278 .Expected({{0, 90.0}, {0, 90.0}}),
279 DiscreteParams()
280 .ThresholdValue("90.0")
281 .Updates({{0, 90.0}, {0, 99.0}, {1, 100.0}, {1, 90.0}})
282 .Expected({{0, 90.0}, {1, 90.0}})));
283
TEST_P(TestDiscreteThresholdNoDwellTime,senorsIsUpdatedMultipleTimes)284 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
285 {
286 testBodySensorIsUpdatedMultipleTimes();
287 }
288
289 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon
290 {
291 public:
SetUp()292 void SetUp() override
293 {
294 for (size_t idx = 0; idx < sensorMocks.size(); idx++)
295 {
296 ON_CALL(*sensorMocks.at(idx), getName())
297 .WillByDefault(Return(sensorNames[idx]));
298 }
299
300 sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue);
301 }
302 };
303
304 INSTANTIATE_TEST_SUITE_P(
305 _, TestDiscreteThresholdWithDwellTime,
306 Values(DiscreteParams()
307 .DwellTime(200ms)
308 .ThresholdValue("90.0")
309 .Updates({{0, 90.0, 100ms}, {0, 91.0}, {0, 90.0}})
310 .Expected({{0, 90.0, 300ms}}),
311 DiscreteParams()
312 .DwellTime(100ms)
313 .ThresholdValue("90.0")
314 .Updates({{0, 90.0, 100ms}})
315 .Expected({{0, 90.0, 100ms}}),
316 DiscreteParams()
317 .DwellTime(1000ms)
318 .ThresholdValue("90.0")
319 .Updates({{0, 90.0, 700ms},
320 {0, 91.0, 100ms},
321 {0, 90.0, 300ms},
322 {0, 91.0, 100ms}})
323 .Expected({}),
324 DiscreteParams()
325 .DwellTime(200ms)
326 .ThresholdValue("90.0")
327 .Updates({{0, 90.0},
328 {1, 89.0, 100ms},
329 {1, 90.0, 100ms},
330 {1, 89.0, 100ms},
331 {1, 90.0, 300ms},
332 {1, 89.0, 100ms}})
333 .Expected({{0, 90, 200ms}, {1, 90, 500ms}})));
334
TEST_P(TestDiscreteThresholdWithDwellTime,senorsIsUpdatedMultipleTimes)335 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
336 {
337 testBodySensorIsUpdatedMultipleTimes();
338 }
339