1 #include <systemd/sd-event.h>
2
3 #include <sdeventplus/clock.hpp>
4 #include <sdeventplus/event.hpp>
5 #include <sdeventplus/test/sdevent.hpp>
6 #include <sdeventplus/utility/timer.hpp>
7
8 #include <chrono>
9 #include <memory>
10 #include <optional>
11 #include <stdexcept>
12
13 #include <gmock/gmock.h>
14 #include <gtest/gtest.h>
15
16 namespace sdeventplus
17 {
18 namespace utility
19 {
20 namespace
21 {
22
23 constexpr ClockId testClock = ClockId::Monotonic;
24
25 using std::chrono::microseconds;
26 using std::chrono::milliseconds;
27 using testing::DoAll;
28 using testing::Return;
29 using testing::ReturnPointee;
30 using testing::SaveArg;
31 using testing::SetArgPointee;
32 using TestTimer = Timer<testClock>;
33
34 ssize_t event_ref_times = 0;
35
ACTION(EventRef)36 ACTION(EventRef)
37 {
38 event_ref_times++;
39 }
40
ACTION(EventUnref)41 ACTION(EventUnref)
42 {
43 ASSERT_LT(0, event_ref_times);
44 event_ref_times--;
45 }
46
47 class TimerTest : public testing::Test
48 {
49 protected:
50 testing::StrictMock<test::SdEventMock> mock;
51 sd_event* const expected_event = reinterpret_cast<sd_event*>(1234);
52 sd_event_source* const expected_source =
53 reinterpret_cast<sd_event_source*>(2345);
54 sd_event_source* const expected_source2 =
55 reinterpret_cast<sd_event_source*>(3456);
56 const milliseconds interval{134};
57 const milliseconds starting_time{10};
58 const milliseconds starting_time2{30};
59 sd_event_time_handler_t handler = nullptr;
60 void* handler_userdata;
61 sd_event_destroy_t handler_destroy;
62 std::unique_ptr<Event> event;
63 std::unique_ptr<TestTimer> timer;
64 std::function<void()> callback;
65
expectNow(microseconds ret)66 void expectNow(microseconds ret)
67 {
68 EXPECT_CALL(mock,
69 sd_event_now(expected_event,
70 static_cast<clockid_t>(testClock), testing::_))
71 .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0)));
72 }
73
expectSetTime(microseconds time)74 void expectSetTime(microseconds time)
75 {
76 EXPECT_CALL(mock,
77 sd_event_source_set_time(expected_source, time.count()))
78 .WillOnce(Return(0));
79 }
80
expectSetEnabled(source::Enabled enabled)81 void expectSetEnabled(source::Enabled enabled)
82 {
83 EXPECT_CALL(mock, sd_event_source_set_enabled(
84 expected_source, static_cast<int>(enabled)))
85 .WillOnce(Return(0));
86 }
87
expectGetEnabled(source::Enabled enabled)88 void expectGetEnabled(source::Enabled enabled)
89 {
90 EXPECT_CALL(mock,
91 sd_event_source_get_enabled(expected_source, testing::_))
92 .WillOnce(
93 DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0)));
94 }
95
resetTimer()96 void resetTimer()
97 {
98 if (timer)
99 {
100 timer.reset();
101 handler_destroy(handler_userdata);
102 }
103 }
104
expireTimer()105 void expireTimer()
106 {
107 const milliseconds new_time(90);
108 expectNow(new_time);
109 expectSetTime(new_time + interval);
110 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
111 EXPECT_TRUE(timer->hasExpired());
112 EXPECT_EQ(interval, timer->getInterval());
113 }
114
SetUp()115 void SetUp()
116 {
117 EXPECT_CALL(mock, sd_event_ref(expected_event))
118 .WillRepeatedly(DoAll(EventRef(), Return(expected_event)));
119 EXPECT_CALL(mock, sd_event_unref(expected_event))
120 .WillRepeatedly(DoAll(EventUnref(), Return(nullptr)));
121 event = std::make_unique<Event>(expected_event, &mock);
122 EXPECT_CALL(mock, sd_event_source_unref(expected_source))
123 .WillRepeatedly(Return(nullptr));
124 EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source,
125 testing::_))
126 .WillRepeatedly(DoAll(SaveArg<1>(&handler_destroy), Return(0)));
127 EXPECT_CALL(mock,
128 sd_event_source_set_userdata(expected_source, testing::_))
129 .WillRepeatedly(
130 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
131 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
132 .WillRepeatedly(ReturnPointee(&handler_userdata));
133
134 // Having a callback proxy allows us to update the test callback
135 // dynamically, without changing it inside the timer
136 auto runCallback = [&](TestTimer&) {
137 if (callback)
138 {
139 callback();
140 }
141 };
142 expectNow(starting_time);
143 EXPECT_CALL(mock, sd_event_add_time(
144 expected_event, testing::_,
145 static_cast<clockid_t>(testClock),
146 microseconds(starting_time + interval).count(),
147 1000, testing::_, nullptr))
148 .WillOnce(DoAll(SetArgPointee<1>(expected_source),
149 SaveArg<5>(&handler), Return(0)));
150 expectSetEnabled(source::Enabled::On);
151 timer = std::make_unique<TestTimer>(*event, runCallback, interval);
152 EXPECT_EQ(expected_event, timer->get_event().get());
153 }
154
TearDown()155 void TearDown()
156 {
157 resetTimer();
158 event.reset();
159 EXPECT_EQ(0, event_ref_times);
160 }
161 };
162
TEST_F(TimerTest,NoCallback)163 TEST_F(TimerTest, NoCallback)
164 {
165 resetTimer();
166 expectNow(starting_time);
167 EXPECT_CALL(
168 mock, sd_event_add_time(expected_event, testing::_,
169 static_cast<clockid_t>(testClock),
170 microseconds(starting_time + interval).count(),
171 1000, testing::_, nullptr))
172 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
173 Return(0)));
174 expectSetEnabled(source::Enabled::On);
175 timer = std::make_unique<TestTimer>(*event, nullptr, interval);
176
177 expectNow(starting_time);
178 expectSetTime(starting_time + interval);
179 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
180 }
181
TEST_F(TimerTest,NoInterval)182 TEST_F(TimerTest, NoInterval)
183 {
184 resetTimer();
185 expectNow(starting_time);
186 EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
187 static_cast<clockid_t>(testClock),
188 microseconds(starting_time).count(),
189 1000, testing::_, nullptr))
190 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
191 Return(0)));
192 expectSetEnabled(source::Enabled::Off);
193 timer = std::make_unique<TestTimer>(*event, nullptr);
194
195 EXPECT_EQ(std::nullopt, timer->getInterval());
196 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
197 }
198
TEST_F(TimerTest,NewTimer)199 TEST_F(TimerTest, NewTimer)
200 {
201 EXPECT_FALSE(timer->hasExpired());
202 EXPECT_EQ(interval, timer->getInterval());
203 }
204
TEST_F(TimerTest,IsEnabled)205 TEST_F(TimerTest, IsEnabled)
206 {
207 expectGetEnabled(source::Enabled::On);
208 EXPECT_TRUE(timer->isEnabled());
209 expectGetEnabled(source::Enabled::Off);
210 EXPECT_FALSE(timer->isEnabled());
211 }
212
TEST_F(TimerTest,GetRemainingDisabled)213 TEST_F(TimerTest, GetRemainingDisabled)
214 {
215 expectGetEnabled(source::Enabled::Off);
216 EXPECT_THROW(timer->getRemaining(), std::runtime_error);
217 }
218
TEST_F(TimerTest,GetRemainingNegative)219 TEST_F(TimerTest, GetRemainingNegative)
220 {
221 milliseconds now(675), end(453);
222 expectGetEnabled(source::Enabled::On);
223 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
224 .WillOnce(
225 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
226 expectNow(now);
227 EXPECT_EQ(milliseconds(0), timer->getRemaining());
228 }
229
TEST_F(TimerTest,GetRemainingPositive)230 TEST_F(TimerTest, GetRemainingPositive)
231 {
232 milliseconds now(453), end(675);
233 expectGetEnabled(source::Enabled::On);
234 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
235 .WillOnce(
236 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
237 expectNow(now);
238 EXPECT_EQ(end - now, timer->getRemaining());
239 }
240
TEST_F(TimerTest,SetEnabled)241 TEST_F(TimerTest, SetEnabled)
242 {
243 expectSetEnabled(source::Enabled::On);
244 timer->setEnabled(true);
245 EXPECT_FALSE(timer->hasExpired());
246 // Value should always be passed through regardless of current state
247 expectSetEnabled(source::Enabled::On);
248 timer->setEnabled(true);
249 EXPECT_FALSE(timer->hasExpired());
250
251 expectSetEnabled(source::Enabled::Off);
252 timer->setEnabled(false);
253 EXPECT_FALSE(timer->hasExpired());
254 // Value should always be passed through regardless of current state
255 expectSetEnabled(source::Enabled::Off);
256 timer->setEnabled(false);
257 EXPECT_FALSE(timer->hasExpired());
258 }
259
TEST_F(TimerTest,SetEnabledUnsetTimer)260 TEST_F(TimerTest, SetEnabledUnsetTimer)
261 {
262 // Force the timer to become unset
263 expectSetEnabled(source::Enabled::Off);
264 timer->restart(std::nullopt);
265
266 // Setting an interval should not update the timer directly
267 timer->setInterval(milliseconds(90));
268
269 expectSetEnabled(source::Enabled::Off);
270 timer->setEnabled(false);
271 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
272 }
273
TEST_F(TimerTest,SetEnabledOneshot)274 TEST_F(TimerTest, SetEnabledOneshot)
275 {
276 // Timer effectively becomes oneshot if it gets initialized but has
277 // the interval removed
278 timer->setInterval(std::nullopt);
279
280 expectSetEnabled(source::Enabled::Off);
281 timer->setEnabled(false);
282 expectSetEnabled(source::Enabled::On);
283 timer->setEnabled(true);
284 }
285
TEST_F(TimerTest,SetRemaining)286 TEST_F(TimerTest, SetRemaining)
287 {
288 const milliseconds now(90), remaining(30);
289 expectNow(now);
290 expectSetTime(now + remaining);
291 timer->setRemaining(remaining);
292 EXPECT_EQ(interval, timer->getInterval());
293 EXPECT_FALSE(timer->hasExpired());
294 }
295
TEST_F(TimerTest,ResetRemaining)296 TEST_F(TimerTest, ResetRemaining)
297 {
298 const milliseconds now(90);
299 expectNow(now);
300 expectSetTime(now + interval);
301 timer->resetRemaining();
302 EXPECT_EQ(interval, timer->getInterval());
303 EXPECT_FALSE(timer->hasExpired());
304 }
305
TEST_F(TimerTest,SetInterval)306 TEST_F(TimerTest, SetInterval)
307 {
308 const milliseconds new_interval(40);
309 timer->setInterval(new_interval);
310 EXPECT_EQ(new_interval, timer->getInterval());
311 EXPECT_FALSE(timer->hasExpired());
312 }
313
TEST_F(TimerTest,SetIntervalEmpty)314 TEST_F(TimerTest, SetIntervalEmpty)
315 {
316 timer->setInterval(std::nullopt);
317 EXPECT_EQ(std::nullopt, timer->getInterval());
318 EXPECT_FALSE(timer->hasExpired());
319 }
320
TEST_F(TimerTest,CallbackHappensLast)321 TEST_F(TimerTest, CallbackHappensLast)
322 {
323 const milliseconds new_time(90);
324 expectNow(new_time);
325 expectSetTime(new_time + interval);
326 callback = [&]() {
327 EXPECT_TRUE(timer->hasExpired());
328 expectSetEnabled(source::Enabled::On);
329 timer->setEnabled(true);
330 timer->clearExpired();
331 timer->setInterval(std::nullopt);
332 };
333 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
334 EXPECT_FALSE(timer->hasExpired());
335 EXPECT_EQ(std::nullopt, timer->getInterval());
336 expectSetEnabled(source::Enabled::On);
337 timer->setEnabled(true);
338 }
339
TEST_F(TimerTest,CallbackOneshot)340 TEST_F(TimerTest, CallbackOneshot)
341 {
342 // Make sure we try a one shot so we can test the callback
343 // correctly
344 timer->setInterval(std::nullopt);
345
346 expectSetEnabled(source::Enabled::Off);
347 callback = [&]() {
348 EXPECT_TRUE(timer->hasExpired());
349 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
350 timer->setInterval(interval);
351 };
352 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
353 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
354 }
355
TEST_F(TimerTest,CallbackMove)356 TEST_F(TimerTest, CallbackMove)
357 {
358 size_t called = 0;
359 callback = [&]() { ++called; };
360
361 expectNow(starting_time2);
362 sd_event_destroy_t local_destroy;
363 EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source2,
364 testing::_))
365 .WillOnce(DoAll(SaveArg<1>(&local_destroy), Return(0)));
366 void* local_userdata;
367 EXPECT_CALL(mock,
368 sd_event_source_set_userdata(expected_source2, testing::_))
369 .WillOnce(DoAll(SaveArg<1>(&local_userdata), Return(nullptr)));
370 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source2))
371 .WillRepeatedly(ReturnPointee(&local_userdata));
372 EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
373 static_cast<clockid_t>(testClock),
374 microseconds(starting_time2).count(),
375 1000, testing::_, nullptr))
376 .WillOnce(DoAll(SetArgPointee<1>(expected_source2), Return(0)));
377 EXPECT_CALL(mock, sd_event_source_unref(expected_source2))
378 .WillOnce(Return(nullptr));
379 EXPECT_CALL(mock,
380 sd_event_source_set_enabled(
381 expected_source2, static_cast<int>(source::Enabled::Off)))
382 .WillOnce(Return(0));
383 TestTimer local_timer(*event, nullptr);
384
385 // Move assign
386 local_timer = std::move(*timer);
387 local_destroy(local_userdata);
388 timer.reset();
389
390 // Move construct
391 timer = std::make_unique<TestTimer>(std::move(local_timer));
392
393 // handler_userdata should have been updated and the callback should work
394 const milliseconds new_time(90);
395 expectNow(new_time);
396 expectSetTime(new_time + interval);
397 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
398 EXPECT_EQ(1, called);
399
400 // update the callback and make sure it still works
401 timer->set_callback(std::bind([]() {}));
402 expectNow(new_time);
403 expectSetTime(new_time + interval);
404 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
405 EXPECT_EQ(1, called);
406 }
407
TEST_F(TimerTest,SetValuesExpiredTimer)408 TEST_F(TimerTest, SetValuesExpiredTimer)
409 {
410 const milliseconds new_time(90);
411 expectNow(new_time);
412 expectSetTime(new_time + interval);
413 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
414 EXPECT_TRUE(timer->hasExpired());
415 EXPECT_EQ(interval, timer->getInterval());
416
417 // Timer should remain expired unless clearExpired() or reset()
418 expectSetEnabled(source::Enabled::On);
419 timer->setEnabled(true);
420 EXPECT_TRUE(timer->hasExpired());
421 expectNow(milliseconds(20));
422 expectSetTime(milliseconds(50));
423 timer->setRemaining(milliseconds(30));
424 EXPECT_TRUE(timer->hasExpired());
425 timer->setInterval(milliseconds(10));
426 EXPECT_TRUE(timer->hasExpired());
427 expectNow(milliseconds(20));
428 expectSetTime(milliseconds(30));
429 timer->resetRemaining();
430 EXPECT_TRUE(timer->hasExpired());
431
432 timer->clearExpired();
433 EXPECT_FALSE(timer->hasExpired());
434 }
435
TEST_F(TimerTest,Restart)436 TEST_F(TimerTest, Restart)
437 {
438 expireTimer();
439
440 const milliseconds new_interval(471);
441 expectNow(starting_time);
442 expectSetTime(starting_time + new_interval);
443 expectSetEnabled(source::Enabled::On);
444 timer->restart(new_interval);
445 EXPECT_FALSE(timer->hasExpired());
446 EXPECT_EQ(new_interval, timer->getInterval());
447 expectSetEnabled(source::Enabled::On);
448 timer->setEnabled(true);
449 }
450
TEST_F(TimerTest,RestartEmpty)451 TEST_F(TimerTest, RestartEmpty)
452 {
453 expireTimer();
454
455 expectSetEnabled(source::Enabled::Off);
456 timer->restart(std::nullopt);
457 EXPECT_FALSE(timer->hasExpired());
458 EXPECT_EQ(std::nullopt, timer->getInterval());
459 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
460 }
461
TEST_F(TimerTest,RestartOnce)462 TEST_F(TimerTest, RestartOnce)
463 {
464 expireTimer();
465
466 const milliseconds remaining(471);
467 expectNow(starting_time);
468 expectSetTime(starting_time + remaining);
469 expectSetEnabled(source::Enabled::On);
470 timer->restartOnce(remaining);
471 EXPECT_FALSE(timer->hasExpired());
472 EXPECT_EQ(std::nullopt, timer->getInterval());
473 expectSetEnabled(source::Enabled::On);
474 timer->setEnabled(true);
475 }
476
477 } // namespace
478 } // namespace utility
479 } // namespace sdeventplus
480