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