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