xref: /openbmc/sdeventplus/test/utility/timer.cpp (revision 5320b1f5)
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             expectSetEnabled(source::Enabled::Off);
98             timer.reset();
99             handler_destroy(handler_userdata);
100         }
101     }
102 
103     void expireTimer()
104     {
105         const milliseconds new_time(90);
106         expectNow(new_time);
107         expectSetTime(new_time + interval);
108         EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
109         EXPECT_TRUE(timer->hasExpired());
110         EXPECT_EQ(interval, timer->getInterval());
111     }
112 
113     void SetUp()
114     {
115         EXPECT_CALL(mock, sd_event_ref(expected_event))
116             .WillRepeatedly(DoAll(EventRef(), Return(expected_event)));
117         EXPECT_CALL(mock, sd_event_unref(expected_event))
118             .WillRepeatedly(DoAll(EventUnref(), Return(nullptr)));
119         event = std::make_unique<Event>(expected_event, &mock);
120         EXPECT_CALL(mock, sd_event_source_unref(expected_source))
121             .WillRepeatedly(Return(nullptr));
122         EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source,
123                                                                testing::_))
124             .WillRepeatedly(DoAll(SaveArg<1>(&handler_destroy), Return(0)));
125         EXPECT_CALL(mock,
126                     sd_event_source_set_userdata(expected_source, testing::_))
127             .WillRepeatedly(
128                 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
129         EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
130             .WillRepeatedly(ReturnPointee(&handler_userdata));
131 
132         // Having a callback proxy allows us to update the test callback
133         // dynamically, without changing it inside the timer
134         auto runCallback = [&](TestTimer&) {
135             if (callback)
136             {
137                 callback();
138             }
139         };
140         expectNow(starting_time);
141         EXPECT_CALL(mock, sd_event_add_time(
142                               expected_event, testing::_,
143                               static_cast<clockid_t>(testClock),
144                               microseconds(starting_time + interval).count(),
145                               1000, testing::_, nullptr))
146             .WillOnce(DoAll(SetArgPointee<1>(expected_source),
147                             SaveArg<5>(&handler), Return(0)));
148         expectSetEnabled(source::Enabled::On);
149         timer = std::make_unique<TestTimer>(*event, runCallback, interval);
150         EXPECT_EQ(expected_event, timer->get_event().get());
151     }
152 
153     void TearDown()
154     {
155         resetTimer();
156         event.reset();
157         EXPECT_EQ(0, event_ref_times);
158     }
159 };
160 
161 TEST_F(TimerTest, NoCallback)
162 {
163     resetTimer();
164     expectNow(starting_time);
165     EXPECT_CALL(
166         mock, sd_event_add_time(expected_event, testing::_,
167                                 static_cast<clockid_t>(testClock),
168                                 microseconds(starting_time + interval).count(),
169                                 1000, testing::_, nullptr))
170         .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
171                         Return(0)));
172     expectSetEnabled(source::Enabled::On);
173     timer = std::make_unique<TestTimer>(*event, nullptr, interval);
174 
175     expectNow(starting_time);
176     expectSetTime(starting_time + interval);
177     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
178 }
179 
180 TEST_F(TimerTest, NoInterval)
181 {
182     resetTimer();
183     expectNow(starting_time);
184     EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
185                                         static_cast<clockid_t>(testClock),
186                                         microseconds(starting_time).count(),
187                                         1000, testing::_, nullptr))
188         .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
189                         Return(0)));
190     expectSetEnabled(source::Enabled::Off);
191     timer = std::make_unique<TestTimer>(*event, nullptr);
192 
193     EXPECT_EQ(std::nullopt, timer->getInterval());
194     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
195 }
196 
197 TEST_F(TimerTest, NewTimer)
198 {
199     EXPECT_FALSE(timer->hasExpired());
200     EXPECT_EQ(interval, timer->getInterval());
201 }
202 
203 TEST_F(TimerTest, IsEnabled)
204 {
205     expectGetEnabled(source::Enabled::On);
206     EXPECT_TRUE(timer->isEnabled());
207     expectGetEnabled(source::Enabled::Off);
208     EXPECT_FALSE(timer->isEnabled());
209 }
210 
211 TEST_F(TimerTest, GetRemainingDisabled)
212 {
213     expectGetEnabled(source::Enabled::Off);
214     EXPECT_THROW(timer->getRemaining(), std::runtime_error);
215 }
216 
217 TEST_F(TimerTest, GetRemainingNegative)
218 {
219     milliseconds now(675), end(453);
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(milliseconds(0), timer->getRemaining());
226 }
227 
228 TEST_F(TimerTest, GetRemainingPositive)
229 {
230     milliseconds now(453), end(675);
231     expectGetEnabled(source::Enabled::On);
232     EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
233         .WillOnce(
234             DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
235     expectNow(now);
236     EXPECT_EQ(end - now, timer->getRemaining());
237 }
238 
239 TEST_F(TimerTest, SetEnabled)
240 {
241     expectSetEnabled(source::Enabled::On);
242     timer->setEnabled(true);
243     EXPECT_FALSE(timer->hasExpired());
244     // Value should always be passed through regardless of current state
245     expectSetEnabled(source::Enabled::On);
246     timer->setEnabled(true);
247     EXPECT_FALSE(timer->hasExpired());
248 
249     expectSetEnabled(source::Enabled::Off);
250     timer->setEnabled(false);
251     EXPECT_FALSE(timer->hasExpired());
252     // Value should always be passed through regardless of current state
253     expectSetEnabled(source::Enabled::Off);
254     timer->setEnabled(false);
255     EXPECT_FALSE(timer->hasExpired());
256 }
257 
258 TEST_F(TimerTest, SetEnabledUnsetTimer)
259 {
260     // Force the timer to become unset
261     expectSetEnabled(source::Enabled::Off);
262     timer->restart(std::nullopt);
263 
264     // Setting an interval should not update the timer directly
265     timer->setInterval(milliseconds(90));
266 
267     expectSetEnabled(source::Enabled::Off);
268     timer->setEnabled(false);
269     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
270 }
271 
272 TEST_F(TimerTest, SetEnabledOneshot)
273 {
274     // Timer effectively becomes oneshot if it gets initialized but has
275     // the interval removed
276     timer->setInterval(std::nullopt);
277 
278     expectSetEnabled(source::Enabled::Off);
279     timer->setEnabled(false);
280     expectSetEnabled(source::Enabled::On);
281     timer->setEnabled(true);
282 }
283 
284 TEST_F(TimerTest, SetRemaining)
285 {
286     const milliseconds now(90), remaining(30);
287     expectNow(now);
288     expectSetTime(now + remaining);
289     timer->setRemaining(remaining);
290     EXPECT_EQ(interval, timer->getInterval());
291     EXPECT_FALSE(timer->hasExpired());
292 }
293 
294 TEST_F(TimerTest, ResetRemaining)
295 {
296     const milliseconds now(90);
297     expectNow(now);
298     expectSetTime(now + interval);
299     timer->resetRemaining();
300     EXPECT_EQ(interval, timer->getInterval());
301     EXPECT_FALSE(timer->hasExpired());
302 }
303 
304 TEST_F(TimerTest, SetInterval)
305 {
306     const milliseconds new_interval(40);
307     timer->setInterval(new_interval);
308     EXPECT_EQ(new_interval, timer->getInterval());
309     EXPECT_FALSE(timer->hasExpired());
310 }
311 
312 TEST_F(TimerTest, SetIntervalEmpty)
313 {
314     timer->setInterval(std::nullopt);
315     EXPECT_EQ(std::nullopt, timer->getInterval());
316     EXPECT_FALSE(timer->hasExpired());
317 }
318 
319 TEST_F(TimerTest, CallbackHappensLast)
320 {
321     const milliseconds new_time(90);
322     expectNow(new_time);
323     expectSetTime(new_time + interval);
324     callback = [&]() {
325         EXPECT_TRUE(timer->hasExpired());
326         expectSetEnabled(source::Enabled::On);
327         timer->setEnabled(true);
328         timer->clearExpired();
329         timer->setInterval(std::nullopt);
330     };
331     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
332     EXPECT_FALSE(timer->hasExpired());
333     EXPECT_EQ(std::nullopt, timer->getInterval());
334     expectSetEnabled(source::Enabled::On);
335     timer->setEnabled(true);
336 }
337 
338 TEST_F(TimerTest, CallbackOneshot)
339 {
340     // Make sure we try a one shot so we can test the callback
341     // correctly
342     timer->setInterval(std::nullopt);
343 
344     expectSetEnabled(source::Enabled::Off);
345     callback = [&]() {
346         EXPECT_TRUE(timer->hasExpired());
347         EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
348         timer->setInterval(interval);
349     };
350     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
351     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
352 }
353 
354 TEST_F(TimerTest, CallbackMove)
355 {
356     size_t called = 0;
357     callback = [&]() { ++called; };
358 
359     expectNow(starting_time2);
360     sd_event_destroy_t local_destroy;
361     EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source2,
362                                                            testing::_))
363         .WillOnce(DoAll(SaveArg<1>(&local_destroy), Return(0)));
364     void* local_userdata;
365     EXPECT_CALL(mock,
366                 sd_event_source_set_userdata(expected_source2, testing::_))
367         .WillOnce(DoAll(SaveArg<1>(&local_userdata), Return(nullptr)));
368     EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source2))
369         .WillRepeatedly(ReturnPointee(&local_userdata));
370     EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
371                                         static_cast<clockid_t>(testClock),
372                                         microseconds(starting_time2).count(),
373                                         1000, testing::_, nullptr))
374         .WillOnce(DoAll(SetArgPointee<1>(expected_source2), Return(0)));
375     EXPECT_CALL(mock, sd_event_source_unref(expected_source2))
376         .WillOnce(Return(nullptr));
377     EXPECT_CALL(mock,
378                 sd_event_source_set_enabled(
379                     expected_source2, static_cast<int>(source::Enabled::Off)))
380         .WillOnce(Return(0))
381         .WillOnce(Return(0));
382     TestTimer local_timer(*event, nullptr);
383 
384     // Move assign
385     local_timer = std::move(*timer);
386     local_destroy(local_userdata);
387     timer.reset();
388 
389     // Move construct
390     timer = std::make_unique<TestTimer>(std::move(local_timer));
391 
392     // handler_userdata should have been updated and the callback should work
393     const milliseconds new_time(90);
394     expectNow(new_time);
395     expectSetTime(new_time + interval);
396     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
397     EXPECT_EQ(1, called);
398 
399     // update the callback and make sure it still works
400     timer->set_callback(std::bind([]() {}));
401     expectNow(new_time);
402     expectSetTime(new_time + interval);
403     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
404     EXPECT_EQ(1, called);
405 }
406 
407 TEST_F(TimerTest, SetValuesExpiredTimer)
408 {
409     const milliseconds new_time(90);
410     expectNow(new_time);
411     expectSetTime(new_time + interval);
412     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
413     EXPECT_TRUE(timer->hasExpired());
414     EXPECT_EQ(interval, timer->getInterval());
415 
416     // Timer should remain expired unless clearExpired() or reset()
417     expectSetEnabled(source::Enabled::On);
418     timer->setEnabled(true);
419     EXPECT_TRUE(timer->hasExpired());
420     expectNow(milliseconds(20));
421     expectSetTime(milliseconds(50));
422     timer->setRemaining(milliseconds(30));
423     EXPECT_TRUE(timer->hasExpired());
424     timer->setInterval(milliseconds(10));
425     EXPECT_TRUE(timer->hasExpired());
426     expectNow(milliseconds(20));
427     expectSetTime(milliseconds(30));
428     timer->resetRemaining();
429     EXPECT_TRUE(timer->hasExpired());
430 
431     timer->clearExpired();
432     EXPECT_FALSE(timer->hasExpired());
433 }
434 
435 TEST_F(TimerTest, Restart)
436 {
437     expireTimer();
438 
439     const milliseconds new_interval(471);
440     expectNow(starting_time);
441     expectSetTime(starting_time + new_interval);
442     expectSetEnabled(source::Enabled::On);
443     timer->restart(new_interval);
444     EXPECT_FALSE(timer->hasExpired());
445     EXPECT_EQ(new_interval, timer->getInterval());
446     expectSetEnabled(source::Enabled::On);
447     timer->setEnabled(true);
448 }
449 
450 TEST_F(TimerTest, RestartEmpty)
451 {
452     expireTimer();
453 
454     expectSetEnabled(source::Enabled::Off);
455     timer->restart(std::nullopt);
456     EXPECT_FALSE(timer->hasExpired());
457     EXPECT_EQ(std::nullopt, timer->getInterval());
458     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
459 }
460 
461 TEST_F(TimerTest, RestartOnce)
462 {
463     expireTimer();
464 
465     const milliseconds remaining(471);
466     expectNow(starting_time);
467     expectSetTime(starting_time + remaining);
468     expectSetEnabled(source::Enabled::On);
469     timer->restartOnce(remaining);
470     EXPECT_FALSE(timer->hasExpired());
471     EXPECT_EQ(std::nullopt, timer->getInterval());
472     expectSetEnabled(source::Enabled::On);
473     timer->setEnabled(true);
474 }
475 
476 } // namespace
477 } // namespace utility
478 } // namespace sdeventplus
479