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