xref: /openbmc/sdeventplus/test/utility/timer.cpp (revision fc18eb03)
1 #include <chrono>
2 #include <gmock/gmock.h>
3 #include <gtest/gtest.h>
4 #include <memory>
5 #include <optional>
6 #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 
13 namespace sdeventplus
14 {
15 namespace utility
16 {
17 namespace
18 {
19 
20 constexpr ClockId testClock = ClockId::Monotonic;
21 
22 using std::chrono::microseconds;
23 using std::chrono::milliseconds;
24 using testing::DoAll;
25 using testing::Return;
26 using testing::ReturnPointee;
27 using testing::SaveArg;
28 using testing::SetArgPointee;
29 using TestTimer = Timer<testClock>;
30 
31 ssize_t event_ref_times = 0;
32 
33 ACTION(EventRef)
34 {
35     event_ref_times++;
36 }
37 
38 ACTION(EventUnref)
39 {
40     ASSERT_LT(0, event_ref_times);
41     event_ref_times--;
42 }
43 
44 class 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);
51     sd_event_source* const expected_source2 =
52         reinterpret_cast<sd_event_source*>(3456);
53     const milliseconds interval{134};
54     const milliseconds starting_time{10};
55     const milliseconds starting_time2{30};
56     sd_event_time_handler_t handler = nullptr;
57     void* handler_userdata;
58     sd_event_destroy_t handler_destroy;
59     std::unique_ptr<Event> event;
60     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 
93     void resetTimer()
94     {
95         if (timer)
96         {
97             timer.reset();
98             handler_destroy(handler_userdata);
99         }
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 
112     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)));
118         event = std::make_unique<Event>(expected_event, &mock);
119         EXPECT_CALL(mock, sd_event_source_unref(expected_source))
120             .WillRepeatedly(Return(nullptr));
121         EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source,
122                                                                testing::_))
123             .WillRepeatedly(DoAll(SaveArg<1>(&handler_destroy), Return(0)));
124         EXPECT_CALL(mock,
125                     sd_event_source_set_userdata(expected_source, testing::_))
126             .WillRepeatedly(
127                 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
128         EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
129             .WillRepeatedly(ReturnPointee(&handler_userdata));
130 
131         // Having a callback proxy allows us to update the test callback
132         // dynamically, without changing it inside the timer
133         auto runCallback = [&](TestTimer&) {
134             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)));
147         expectSetEnabled(source::Enabled::On);
148         timer = std::make_unique<TestTimer>(*event, runCallback, interval);
149         EXPECT_EQ(expected_event, timer->get_event().get());
150     }
151 
152     void TearDown()
153     {
154         resetTimer();
155         event.reset();
156         EXPECT_EQ(0, event_ref_times);
157     }
158 };
159 
160 TEST_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 
179 TEST_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 
196 TEST_F(TimerTest, NewTimer)
197 {
198     EXPECT_FALSE(timer->hasExpired());
199     EXPECT_EQ(interval, timer->getInterval());
200 }
201 
202 TEST_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 
210 TEST_F(TimerTest, GetRemainingDisabled)
211 {
212     expectGetEnabled(source::Enabled::Off);
213     EXPECT_THROW(timer->getRemaining(), std::runtime_error);
214 }
215 
216 TEST_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 
227 TEST_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 
238 TEST_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 
257 TEST_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 
271 TEST_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 
283 TEST_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 
293 TEST_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 
303 TEST_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 
311 TEST_F(TimerTest, SetIntervalEmpty)
312 {
313     timer->setInterval(std::nullopt);
314     EXPECT_EQ(std::nullopt, timer->getInterval());
315     EXPECT_FALSE(timer->hasExpired());
316 }
317 
318 TEST_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 
337 TEST_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 
353 TEST_F(TimerTest, CallbackMove)
354 {
355     size_t called = 0;
356     callback = [&]() { ++called; };
357 
358     expectNow(starting_time2);
359     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;
364     EXPECT_CALL(mock,
365                 sd_event_source_set_userdata(expected_source2, testing::_))
366         .WillOnce(DoAll(SaveArg<1>(&local_userdata), Return(nullptr)));
367     EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source2))
368         .WillRepeatedly(ReturnPointee(&local_userdata));
369     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)))
379         .WillOnce(Return(0));
380     TestTimer local_timer(*event, nullptr);
381 
382     // Move assign
383     local_timer = std::move(*timer);
384     local_destroy(local_userdata);
385     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);
396 
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);
403 }
404 
405 TEST_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 
433 TEST_F(TimerTest, Restart)
434 {
435     expireTimer();
436 
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());
444     expectSetEnabled(source::Enabled::On);
445     timer->setEnabled(true);
446 }
447 
448 TEST_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 
459 TEST_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);
472 }
473 
474 } // namespace
475 } // namespace utility
476 } // namespace sdeventplus
477