1 #include "dbus_environment.hpp"
2 #include "discrete_threshold.hpp"
3 #include "helpers.hpp"
4 #include "mocks/sensor_mock.hpp"
5 #include "mocks/trigger_action_mock.hpp"
6 #include "types/duration_types.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 TestDiscreteThreshold : 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<DiscreteThreshold> sut;
25 
26     std::shared_ptr<DiscreteThreshold>
27         makeThreshold(Milliseconds dwellTime, double thresholdValue,
28                       discrete::Severity severity = discrete::Severity::ok)
29     {
30         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
31         actions.push_back(std::move(actionMockPtr));
32 
33         return std::make_shared<DiscreteThreshold>(
34             DbusEnvironment::getIoc(),
35             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
36                 sensorMocks),
37             std::move(actions), dwellTime, thresholdValue, "treshold name",
38             severity);
39     }
40 
41     void SetUp() override
42     {
43         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
44         {
45             ON_CALL(*sensorMocks.at(idx), getName())
46                 .WillByDefault(Return(sensorNames[idx]));
47         }
48 
49         sut = makeThreshold(0ms, 90.0, discrete::Severity::critical);
50     }
51 };
52 
53 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered)
54 {
55     for (auto& sensor : sensorMocks)
56     {
57         EXPECT_CALL(*sensor,
58                     registerForUpdates(Truly([sut = sut.get()](const auto& x) {
59                         return x.lock().get() == sut;
60                     })));
61     }
62 
63     sut->initialize();
64 }
65 
66 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit)
67 {
68     EXPECT_CALL(actionMock, commit(_, _, _)).Times(0);
69 }
70 
71 TEST_F(TestDiscreteThreshold, getLabeledParamsReturnsCorrectly)
72 {
73     LabeledThresholdParam expected = discrete::LabeledThresholdParam(
74         "treshold name", discrete::Severity::critical, 0, std::to_string(90.0));
75     EXPECT_EQ(sut->getThresholdParam(), expected);
76 }
77 
78 struct DiscreteParams
79 {
80     struct UpdateParams
81     {
82         size_t sensor;
83         Milliseconds timestamp;
84         double value;
85         Milliseconds sleepAfter;
86 
87         UpdateParams(size_t sensor, Milliseconds timestamp, double value,
88                      Milliseconds sleepAfter = 0ms) :
89             sensor(sensor),
90             timestamp(timestamp), value(value), sleepAfter(sleepAfter)
91         {}
92     };
93 
94     struct ExpectedParams
95     {
96         size_t sensor;
97         Milliseconds timestamp;
98         double value;
99         Milliseconds waitMin;
100 
101         ExpectedParams(size_t sensor, Milliseconds timestamp, double value,
102                        Milliseconds waitMin = 0ms) :
103             sensor(sensor),
104             timestamp(timestamp), value(value), waitMin(waitMin)
105         {}
106     };
107 
108     DiscreteParams& Updates(std::vector<UpdateParams> val)
109     {
110         updates = std::move(val);
111         return *this;
112     }
113 
114     DiscreteParams& Expected(std::vector<ExpectedParams> val)
115     {
116         expected = std::move(val);
117         return *this;
118     }
119 
120     DiscreteParams& ThresholdValue(double val)
121     {
122         thresholdValue = val;
123         return *this;
124     }
125 
126     DiscreteParams& DwellTime(Milliseconds val)
127     {
128         dwellTime = std::move(val);
129         return *this;
130     }
131 
132     friend void PrintTo(const DiscreteParams& o, std::ostream* os)
133     {
134         *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
135         *os << ", ThresholdValue: " << o.thresholdValue;
136         *os << ", Updates: ";
137         for (const auto& [index, timestamp, value, sleepAfter] : o.updates)
138         {
139             *os << "{ SensorIndex: " << index
140                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
141                 << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
142         }
143         *os << "Expected: ";
144         for (const auto& [index, timestamp, value, waitMin] : o.expected)
145         {
146             *os << "{ SensorIndex: " << index
147                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
148                 << ", waitMin: " << waitMin.count() << "ms }, ";
149         }
150         *os << " }";
151     }
152 
153     std::vector<UpdateParams> updates;
154     std::vector<ExpectedParams> expected;
155     double thresholdValue = 0.0;
156     Milliseconds dwellTime = 0ms;
157 };
158 
159 class TestDiscreteThresholdCommon :
160     public TestDiscreteThreshold,
161     public WithParamInterface<DiscreteParams>
162 {
163   public:
164     void sleep(Milliseconds duration)
165     {
166         if (duration != 0ms)
167         {
168             DbusEnvironment::sleepFor(duration);
169         }
170     }
171 
172     void testBodySensorIsUpdatedMultipleTimes()
173     {
174         std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
175             timestamps(sensorMocks.size());
176 
177         sut->initialize();
178 
179         InSequence seq;
180 
181         for (const auto& [index, timestamp, value, waitMin] :
182              GetParam().expected)
183         {
184             EXPECT_CALL(actionMock,
185                         commit(sensorNames[index], timestamp, value))
186                 .WillOnce(DoAll(
187                     InvokeWithoutArgs([idx = index, &timestamps] {
188                         timestamps[idx] =
189                             std::chrono::high_resolution_clock::now();
190                     }),
191                     InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
192         }
193 
194         auto start = std::chrono::high_resolution_clock::now();
195 
196         for (const auto& [index, timestamp, value, sleepAfter] :
197              GetParam().updates)
198         {
199             sut->sensorUpdated(*sensorMocks[index], timestamp, value);
200             sleep(sleepAfter);
201         }
202 
203         EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
204         for (const auto& [index, timestamp, value, waitMin] :
205              GetParam().expected)
206         {
207             EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
208         }
209     }
210 };
211 
212 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon
213 {
214   public:
215     void SetUp() override
216     {
217 
218         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
219         {
220             ON_CALL(*sensorMocks.at(idx), getName())
221                 .WillByDefault(Return(sensorNames[idx]));
222         }
223 
224         sut = makeThreshold(0ms, GetParam().thresholdValue);
225     }
226 };
227 
228 INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdNoDwellTime,
229                          Values(DiscreteParams()
230                                     .ThresholdValue(90.0)
231                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
232                                     .Expected({}),
233                                 DiscreteParams()
234                                     .ThresholdValue(90.0)
235                                     .Updates({{0, 1ms, 80.0},
236                                               {0, 2ms, 90.0},
237                                               {0, 3ms, 80.0},
238                                               {0, 4ms, 90.0}})
239                                     .Expected({{0, 2ms, 90.0}, {0, 4ms, 90.0}}),
240                                 DiscreteParams()
241                                     .ThresholdValue(90.0)
242                                     .Updates({{0, 1ms, 90.0},
243                                               {0, 2ms, 99.0},
244                                               {1, 3ms, 100.0},
245                                               {1, 4ms, 90.0}})
246                                     .Expected({{0, 1ms, 90.0},
247                                                {1, 4ms, 90.0}})));
248 
249 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
250 {
251     testBodySensorIsUpdatedMultipleTimes();
252 }
253 
254 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon
255 {
256   public:
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(GetParam().dwellTime, GetParam().thresholdValue);
266     }
267 };
268 
269 INSTANTIATE_TEST_SUITE_P(
270     _, TestDiscreteThresholdWithDwellTime,
271     Values(DiscreteParams()
272                .DwellTime(200ms)
273                .ThresholdValue(90.0)
274                .Updates({{0, 1ms, 90.0, 100ms}, {0, 2ms, 91.0}, {0, 3ms, 90.0}})
275                .Expected({{0, 3ms, 90.0, 300ms}}),
276            DiscreteParams()
277                .DwellTime(100ms)
278                .ThresholdValue(90.0)
279                .Updates({{0, 1ms, 90.0, 100ms}})
280                .Expected({{0, 1ms, 90.0, 100ms}}),
281            DiscreteParams()
282                .DwellTime(1000ms)
283                .ThresholdValue(90.0)
284                .Updates({{0, 1ms, 90.0, 700ms},
285                          {0, 1ms, 91.0, 100ms},
286                          {0, 1ms, 90.0, 300ms},
287                          {0, 1ms, 91.0, 100ms}})
288                .Expected({}),
289            DiscreteParams()
290                .DwellTime(200ms)
291                .ThresholdValue(90.0)
292                .Updates({{0, 1ms, 90.0},
293                          {1, 2ms, 89.0, 100ms},
294                          {1, 3ms, 90.0, 100ms},
295                          {1, 4ms, 89.0, 100ms},
296                          {1, 5ms, 90.0, 300ms},
297                          {1, 6ms, 89.0, 100ms}})
298                .Expected({{0, 1ms, 90, 200ms}, {1, 5ms, 90, 500ms}})));
299 
300 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
301 {
302     testBodySensorIsUpdatedMultipleTimes();
303 }
304