blob: 6d5dff047268a6c082e317bf4f1f20ca7003b06c [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);
50 const milliseconds interval{134};
51 const milliseconds starting_time{10};
52 sd_event_time_handler_t handler = nullptr;
53 void* handler_userdata;
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -070054 std::unique_ptr<Event> event;
William A. Kennington III81282e12018-09-19 18:28:37 -070055 std::unique_ptr<TestTimer> timer;
56 std::function<void()> callback;
57
58 void expectNow(microseconds ret)
59 {
60 EXPECT_CALL(mock,
61 sd_event_now(expected_event,
62 static_cast<clockid_t>(testClock), testing::_))
63 .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0)));
64 }
65
66 void expectSetTime(microseconds time)
67 {
68 EXPECT_CALL(mock,
69 sd_event_source_set_time(expected_source, time.count()))
70 .WillOnce(Return(0));
71 }
72
73 void expectSetEnabled(source::Enabled enabled)
74 {
75 EXPECT_CALL(mock, sd_event_source_set_enabled(
76 expected_source, static_cast<int>(enabled)))
77 .WillOnce(Return(0));
78 }
79
80 void expectGetEnabled(source::Enabled enabled)
81 {
82 EXPECT_CALL(mock,
83 sd_event_source_get_enabled(expected_source, testing::_))
84 .WillOnce(
85 DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0)));
86 }
87
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -070088 void resetTimer()
89 {
90 if (timer)
91 {
92 expectSetEnabled(source::Enabled::Off);
93 timer.reset();
94 }
95 }
96
97 void expireTimer()
98 {
99 const milliseconds new_time(90);
100 expectNow(new_time);
101 expectSetTime(new_time + interval);
102 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
103 EXPECT_TRUE(timer->hasExpired());
104 EXPECT_EQ(interval, timer->getInterval());
105 }
106
William A. Kennington III81282e12018-09-19 18:28:37 -0700107 void SetUp()
108 {
109 EXPECT_CALL(mock, sd_event_ref(expected_event))
110 .WillRepeatedly(DoAll(EventRef(), Return(expected_event)));
111 EXPECT_CALL(mock, sd_event_unref(expected_event))
112 .WillRepeatedly(DoAll(EventUnref(), Return(nullptr)));
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700113 event = std::make_unique<Event>(expected_event, &mock);
114 EXPECT_CALL(mock, sd_event_source_unref(expected_source))
115 .WillRepeatedly(Return(nullptr));
116 EXPECT_CALL(mock,
117 sd_event_source_set_userdata(expected_source, testing::_))
118 .WillRepeatedly(
119 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
William A. Kennington III81282e12018-09-19 18:28:37 -0700120
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700121 // Having a callback proxy allows us to update the test callback
122 // dynamically, without changing it inside the timer
William A. Kennington III47558182018-09-25 15:18:14 -0700123 auto runCallback = [&](TestTimer&) {
William A. Kennington III81282e12018-09-19 18:28:37 -0700124 if (callback)
125 {
126 callback();
127 }
128 };
129 expectNow(starting_time);
130 EXPECT_CALL(mock, sd_event_add_time(
131 expected_event, testing::_,
132 static_cast<clockid_t>(testClock),
133 microseconds(starting_time + interval).count(),
134 1000, testing::_, nullptr))
135 .WillOnce(DoAll(SetArgPointee<1>(expected_source),
136 SaveArg<5>(&handler), Return(0)));
William A. Kennington III81282e12018-09-19 18:28:37 -0700137 expectSetEnabled(source::Enabled::On);
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700138 timer = std::make_unique<TestTimer>(*event, runCallback, interval);
William A. Kennington III27b73012018-10-18 01:12:09 -0700139 EXPECT_EQ(expected_event, timer->get_event().get());
William A. Kennington III81282e12018-09-19 18:28:37 -0700140 }
141
142 void TearDown()
143 {
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700144 resetTimer();
145 event.reset();
William A. Kennington III81282e12018-09-19 18:28:37 -0700146 EXPECT_EQ(0, event_ref_times);
147 }
148};
149
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700150TEST_F(TimerTest, NoCallback)
151{
152 resetTimer();
153 expectNow(starting_time);
154 EXPECT_CALL(
155 mock, sd_event_add_time(expected_event, testing::_,
156 static_cast<clockid_t>(testClock),
157 microseconds(starting_time + interval).count(),
158 1000, testing::_, nullptr))
159 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
160 Return(0)));
161 expectSetEnabled(source::Enabled::On);
162 timer = std::make_unique<TestTimer>(*event, nullptr, interval);
163
164 expectNow(starting_time);
165 expectSetTime(starting_time + interval);
166 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
167}
168
169TEST_F(TimerTest, NoInterval)
170{
171 resetTimer();
172 expectNow(starting_time);
173 EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
174 static_cast<clockid_t>(testClock),
175 microseconds(starting_time).count(),
176 1000, testing::_, nullptr))
177 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
178 Return(0)));
179 expectSetEnabled(source::Enabled::Off);
180 timer = std::make_unique<TestTimer>(*event, nullptr);
181
182 EXPECT_EQ(std::nullopt, timer->getInterval());
183 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
184}
185
William A. Kennington III81282e12018-09-19 18:28:37 -0700186TEST_F(TimerTest, NewTimer)
187{
188 EXPECT_FALSE(timer->hasExpired());
189 EXPECT_EQ(interval, timer->getInterval());
190}
191
192TEST_F(TimerTest, IsEnabled)
193{
194 expectGetEnabled(source::Enabled::On);
195 EXPECT_TRUE(timer->isEnabled());
196 expectGetEnabled(source::Enabled::Off);
197 EXPECT_FALSE(timer->isEnabled());
198}
199
200TEST_F(TimerTest, GetRemainingDisabled)
201{
202 expectGetEnabled(source::Enabled::Off);
203 EXPECT_THROW(timer->getRemaining(), std::runtime_error);
204}
205
206TEST_F(TimerTest, GetRemainingNegative)
207{
208 milliseconds now(675), end(453);
209 expectGetEnabled(source::Enabled::On);
210 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
211 .WillOnce(
212 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
213 expectNow(now);
214 EXPECT_EQ(milliseconds(0), timer->getRemaining());
215}
216
217TEST_F(TimerTest, GetRemainingPositive)
218{
219 milliseconds now(453), end(675);
220 expectGetEnabled(source::Enabled::On);
221 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
222 .WillOnce(
223 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
224 expectNow(now);
225 EXPECT_EQ(end - now, timer->getRemaining());
226}
227
228TEST_F(TimerTest, SetEnabled)
229{
230 expectSetEnabled(source::Enabled::On);
231 timer->setEnabled(true);
232 EXPECT_FALSE(timer->hasExpired());
233 // Value should always be passed through regardless of current state
234 expectSetEnabled(source::Enabled::On);
235 timer->setEnabled(true);
236 EXPECT_FALSE(timer->hasExpired());
237
238 expectSetEnabled(source::Enabled::Off);
239 timer->setEnabled(false);
240 EXPECT_FALSE(timer->hasExpired());
241 // Value should always be passed through regardless of current state
242 expectSetEnabled(source::Enabled::Off);
243 timer->setEnabled(false);
244 EXPECT_FALSE(timer->hasExpired());
245}
246
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700247TEST_F(TimerTest, SetEnabledUnsetTimer)
248{
249 // Force the timer to become unset
250 expectSetEnabled(source::Enabled::Off);
251 timer->restart(std::nullopt);
252
253 // Setting an interval should not update the timer directly
254 timer->setInterval(milliseconds(90));
255
256 expectSetEnabled(source::Enabled::Off);
257 timer->setEnabled(false);
258 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
259}
260
261TEST_F(TimerTest, SetEnabledOneshot)
262{
263 // Timer effectively becomes oneshot if it gets initialized but has
264 // the interval removed
265 timer->setInterval(std::nullopt);
266
267 expectSetEnabled(source::Enabled::Off);
268 timer->setEnabled(false);
269 expectSetEnabled(source::Enabled::On);
270 timer->setEnabled(true);
271}
272
William A. Kennington III81282e12018-09-19 18:28:37 -0700273TEST_F(TimerTest, SetRemaining)
274{
275 const milliseconds now(90), remaining(30);
276 expectNow(now);
277 expectSetTime(now + remaining);
278 timer->setRemaining(remaining);
279 EXPECT_EQ(interval, timer->getInterval());
280 EXPECT_FALSE(timer->hasExpired());
281}
282
283TEST_F(TimerTest, ResetRemaining)
284{
285 const milliseconds now(90);
286 expectNow(now);
287 expectSetTime(now + interval);
288 timer->resetRemaining();
289 EXPECT_EQ(interval, timer->getInterval());
290 EXPECT_FALSE(timer->hasExpired());
291}
292
293TEST_F(TimerTest, SetInterval)
294{
295 const milliseconds new_interval(40);
296 timer->setInterval(new_interval);
297 EXPECT_EQ(new_interval, timer->getInterval());
298 EXPECT_FALSE(timer->hasExpired());
299}
300
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700301TEST_F(TimerTest, SetIntervalEmpty)
302{
303 timer->setInterval(std::nullopt);
304 EXPECT_EQ(std::nullopt, timer->getInterval());
305 EXPECT_FALSE(timer->hasExpired());
306}
307
308TEST_F(TimerTest, CallbackHappensLast)
309{
310 const milliseconds new_time(90);
311 expectNow(new_time);
312 expectSetTime(new_time + interval);
313 callback = [&]() {
314 EXPECT_TRUE(timer->hasExpired());
315 expectSetEnabled(source::Enabled::On);
316 timer->setEnabled(true);
317 timer->clearExpired();
318 timer->setInterval(std::nullopt);
319 };
320 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
321 EXPECT_FALSE(timer->hasExpired());
322 EXPECT_EQ(std::nullopt, timer->getInterval());
323 expectSetEnabled(source::Enabled::On);
324 timer->setEnabled(true);
325}
326
327TEST_F(TimerTest, CallbackOneshot)
328{
329 // Make sure we try a one shot so we can test the callback
330 // correctly
331 timer->setInterval(std::nullopt);
332
333 expectSetEnabled(source::Enabled::Off);
334 callback = [&]() {
335 EXPECT_TRUE(timer->hasExpired());
336 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
337 timer->setInterval(interval);
338 };
339 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
340 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
341}
342
William A. Kennington III81282e12018-09-19 18:28:37 -0700343TEST_F(TimerTest, SetValuesExpiredTimer)
344{
345 const milliseconds new_time(90);
346 expectNow(new_time);
347 expectSetTime(new_time + interval);
348 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
349 EXPECT_TRUE(timer->hasExpired());
350 EXPECT_EQ(interval, timer->getInterval());
351
352 // Timer should remain expired unless clearExpired() or reset()
353 expectSetEnabled(source::Enabled::On);
354 timer->setEnabled(true);
355 EXPECT_TRUE(timer->hasExpired());
356 expectNow(milliseconds(20));
357 expectSetTime(milliseconds(50));
358 timer->setRemaining(milliseconds(30));
359 EXPECT_TRUE(timer->hasExpired());
360 timer->setInterval(milliseconds(10));
361 EXPECT_TRUE(timer->hasExpired());
362 expectNow(milliseconds(20));
363 expectSetTime(milliseconds(30));
364 timer->resetRemaining();
365 EXPECT_TRUE(timer->hasExpired());
366
367 timer->clearExpired();
368 EXPECT_FALSE(timer->hasExpired());
369}
370
371TEST_F(TimerTest, Restart)
372{
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700373 expireTimer();
William A. Kennington III81282e12018-09-19 18:28:37 -0700374
375 const milliseconds new_interval(471);
376 expectNow(starting_time);
377 expectSetTime(starting_time + new_interval);
378 expectSetEnabled(source::Enabled::On);
379 timer->restart(new_interval);
380 EXPECT_FALSE(timer->hasExpired());
381 EXPECT_EQ(new_interval, timer->getInterval());
William A. Kennington IIIba04ffb2018-09-20 11:35:11 -0700382 expectSetEnabled(source::Enabled::On);
383 timer->setEnabled(true);
384}
385
386TEST_F(TimerTest, RestartEmpty)
387{
388 expireTimer();
389
390 expectSetEnabled(source::Enabled::Off);
391 timer->restart(std::nullopt);
392 EXPECT_FALSE(timer->hasExpired());
393 EXPECT_EQ(std::nullopt, timer->getInterval());
394 EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
395}
396
397TEST_F(TimerTest, RestartOnce)
398{
399 expireTimer();
400
401 const milliseconds remaining(471);
402 expectNow(starting_time);
403 expectSetTime(starting_time + remaining);
404 expectSetEnabled(source::Enabled::On);
405 timer->restartOnce(remaining);
406 EXPECT_FALSE(timer->hasExpired());
407 EXPECT_EQ(std::nullopt, timer->getInterval());
408 expectSetEnabled(source::Enabled::On);
409 timer->setEnabled(true);
William A. Kennington III81282e12018-09-19 18:28:37 -0700410}
411
412} // namespace
413} // namespace utility
414} // namespace sdeventplus