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