blob: a2472ed3146a4ca6bbb8a496d5e26e8ba9b9688f [file] [log] [blame]
Patrick Williamsa8c11e32023-05-10 07:50:56 -05001#include <systemd/sd-event.h>
2
William A. Kennington III81282e12018-09-19 18:28:37 -07003#include <sdeventplus/clock.hpp>
4#include <sdeventplus/event.hpp>
5#include <sdeventplus/test/sdevent.hpp>
6#include <sdeventplus/utility/timer.hpp>
Patrick Williamsa8c11e32023-05-10 07:50:56 -05007
8#include <chrono>
9#include <memory>
10#include <optional>
William A. Kennington III81282e12018-09-19 18:28:37 -070011#include <stdexcept>
Patrick Williamsa8c11e32023-05-10 07:50:56 -050012
13#include <gmock/gmock.h>
14#include <gtest/gtest.h>
William A. Kennington III81282e12018-09-19 18:28:37 -070015
16namespace sdeventplus
17{
18namespace utility
19{
20namespace
21{
22
23constexpr ClockId testClock = ClockId::Monotonic;
24
25using std::chrono::microseconds;
26using std::chrono::milliseconds;
27using testing::DoAll;
28using testing::Return;
William A. Kennington III5320b1f2019-03-29 20:00:37 -070029using testing::ReturnPointee;
William A. Kennington III81282e12018-09-19 18:28:37 -070030using testing::SaveArg;
31using testing::SetArgPointee;
32using TestTimer = Timer<testClock>;
33
34ssize_t event_ref_times = 0;
35
36ACTION(EventRef)
37{
38 event_ref_times++;
39}
40
41ACTION(EventUnref)
42{
43 ASSERT_LT(0, event_ref_times);
44 event_ref_times--;
45}
46
47class 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);
William A. Kennington IIIa5f85962018-10-29 20:15:45 -070054 sd_event_source* const expected_source2 =
55 reinterpret_cast<sd_event_source*>(3456);
William A. Kennington III81282e12018-09-19 18:28:37 -070056 const milliseconds interval{134};
57 const milliseconds starting_time{10};
William A. Kennington IIIa5f85962018-10-29 20:15:45 -070058 const milliseconds starting_time2{30};
William A. Kennington III81282e12018-09-19 18:28:37 -070059 sd_event_time_handler_t handler = nullptr;
60 void* handler_userdata;
William A. Kennington III5320b1f2019-03-29 20:00:37 -070061 sd_event_destroy_t handler_destroy;
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -070062 std::unique_ptr<Event> event;
William A. Kennington III81282e12018-09-19 18:28:37 -070063 std::unique_ptr<TestTimer> timer;
64 std::function<void()> callback;
65
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
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
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
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
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -070096 void resetTimer()
97 {
98 if (timer)
99 {
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700100 timer.reset();
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700101 handler_destroy(handler_userdata);
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700102 }
103 }
104
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
William A. Kennington III81282e12018-09-19 18:28:37 -0700115 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)));
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700121 event = std::make_unique<Event>(expected_event, &mock);
122 EXPECT_CALL(mock, sd_event_source_unref(expected_source))
123 .WillRepeatedly(Return(nullptr));
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700124 EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source,
125 testing::_))
126 .WillRepeatedly(DoAll(SaveArg<1>(&handler_destroy), Return(0)));
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700127 EXPECT_CALL(mock,
128 sd_event_source_set_userdata(expected_source, testing::_))
129 .WillRepeatedly(
130 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700131 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
132 .WillRepeatedly(ReturnPointee(&handler_userdata));
William A. Kennington III81282e12018-09-19 18:28:37 -0700133
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700134 // Having a callback proxy allows us to update the test callback
135 // dynamically, without changing it inside the timer
William A. Kennington III47558182018-09-25 15:18:14 -0700136 auto runCallback = [&](TestTimer&) {
William A. Kennington III81282e12018-09-19 18:28:37 -0700137 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)));
William A. Kennington III81282e12018-09-19 18:28:37 -0700150 expectSetEnabled(source::Enabled::On);
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700151 timer = std::make_unique<TestTimer>(*event, runCallback, interval);
William A. Kennington III27b73012018-10-18 01:12:09 -0700152 EXPECT_EQ(expected_event, timer->get_event().get());
William A. Kennington III81282e12018-09-19 18:28:37 -0700153 }
154
155 void TearDown()
156 {
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700157 resetTimer();
158 event.reset();
William A. Kennington III81282e12018-09-19 18:28:37 -0700159 EXPECT_EQ(0, event_ref_times);
160 }
161};
162
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700163TEST_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
182TEST_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
William A. Kennington III81282e12018-09-19 18:28:37 -0700199TEST_F(TimerTest, NewTimer)
200{
201 EXPECT_FALSE(timer->hasExpired());
202 EXPECT_EQ(interval, timer->getInterval());
203}
204
205TEST_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
213TEST_F(TimerTest, GetRemainingDisabled)
214{
215 expectGetEnabled(source::Enabled::Off);
216 EXPECT_THROW(timer->getRemaining(), std::runtime_error);
217}
218
219TEST_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
230TEST_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
241TEST_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
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700260TEST_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
274TEST_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
William A. Kennington III81282e12018-09-19 18:28:37 -0700286TEST_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
296TEST_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
306TEST_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
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700314TEST_F(TimerTest, SetIntervalEmpty)
315{
316 timer->setInterval(std::nullopt);
317 EXPECT_EQ(std::nullopt, timer->getInterval());
318 EXPECT_FALSE(timer->hasExpired());
319}
320
321TEST_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
340TEST_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
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700356TEST_F(TimerTest, CallbackMove)
357{
358 size_t called = 0;
359 callback = [&]() { ++called; };
360
361 expectNow(starting_time2);
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700362 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;
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700367 EXPECT_CALL(mock,
368 sd_event_source_set_userdata(expected_source2, testing::_))
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700369 .WillOnce(DoAll(SaveArg<1>(&local_userdata), Return(nullptr)));
370 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source2))
371 .WillRepeatedly(ReturnPointee(&local_userdata));
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700372 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)))
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700382 .WillOnce(Return(0));
383 TestTimer local_timer(*event, nullptr);
384
385 // Move assign
386 local_timer = std::move(*timer);
William A. Kennington III5320b1f2019-03-29 20:00:37 -0700387 local_destroy(local_userdata);
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700388 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);
William A. Kennington III08ebb392018-10-30 13:21:13 -0700399
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);
William A. Kennington IIIa5f85962018-10-29 20:15:45 -0700406}
407
William A. Kennington III81282e12018-09-19 18:28:37 -0700408TEST_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
436TEST_F(TimerTest, Restart)
437{
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700438 expireTimer();
William A. Kennington III81282e12018-09-19 18:28:37 -0700439
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());
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700447 expectSetEnabled(source::Enabled::On);
448 timer->setEnabled(true);
449}
450
451TEST_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
462TEST_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);
William A. Kennington III81282e12018-09-19 18:28:37 -0700475}
476
477} // namespace
478} // namespace utility
479} // namespace sdeventplus