xref: /openbmc/sdeventplus/test/utility/timer.cpp (revision 08ebb399)
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::SaveArg;
27 using testing::SetArgPointee;
28 using TestTimer = Timer<testClock>;
29 
30 ssize_t event_ref_times = 0;
31 
32 ACTION(EventRef)
33 {
34     event_ref_times++;
35 }
36 
37 ACTION(EventUnref)
38 {
39     ASSERT_LT(0, event_ref_times);
40     event_ref_times--;
41 }
42 
43 class 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     sd_event_source* const expected_source2 =
51         reinterpret_cast<sd_event_source*>(3456);
52     const milliseconds interval{134};
53     const milliseconds starting_time{10};
54     const milliseconds starting_time2{30};
55     sd_event_time_handler_t handler = nullptr;
56     void* handler_userdata;
57     std::unique_ptr<Event> event;
58     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 
91     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 
110     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)));
116         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)));
123 
124         // Having a callback proxy allows us to update the test callback
125         // dynamically, without changing it inside the timer
126         auto runCallback = [&](TestTimer&) {
127             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)));
140         expectSetEnabled(source::Enabled::On);
141         timer = std::make_unique<TestTimer>(*event, runCallback, interval);
142         EXPECT_EQ(expected_event, timer->get_event().get());
143     }
144 
145     void TearDown()
146     {
147         resetTimer();
148         event.reset();
149         EXPECT_EQ(0, event_ref_times);
150     }
151 };
152 
153 TEST_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 
172 TEST_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 
189 TEST_F(TimerTest, NewTimer)
190 {
191     EXPECT_FALSE(timer->hasExpired());
192     EXPECT_EQ(interval, timer->getInterval());
193 }
194 
195 TEST_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 
203 TEST_F(TimerTest, GetRemainingDisabled)
204 {
205     expectGetEnabled(source::Enabled::Off);
206     EXPECT_THROW(timer->getRemaining(), std::runtime_error);
207 }
208 
209 TEST_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 
220 TEST_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 
231 TEST_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 
250 TEST_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 
264 TEST_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 
276 TEST_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 
286 TEST_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 
296 TEST_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 
304 TEST_F(TimerTest, SetIntervalEmpty)
305 {
306     timer->setInterval(std::nullopt);
307     EXPECT_EQ(std::nullopt, timer->getInterval());
308     EXPECT_FALSE(timer->hasExpired());
309 }
310 
311 TEST_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 
330 TEST_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 
346 TEST_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     // update the callback and make sure it still works
384     timer->set_callback(std::bind([]() {}));
385     expectNow(new_time);
386     expectSetTime(new_time + interval);
387     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
388     EXPECT_EQ(1, called);
389 }
390 
391 TEST_F(TimerTest, SetValuesExpiredTimer)
392 {
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_TRUE(timer->hasExpired());
398     EXPECT_EQ(interval, timer->getInterval());
399 
400     // Timer should remain expired unless clearExpired() or reset()
401     expectSetEnabled(source::Enabled::On);
402     timer->setEnabled(true);
403     EXPECT_TRUE(timer->hasExpired());
404     expectNow(milliseconds(20));
405     expectSetTime(milliseconds(50));
406     timer->setRemaining(milliseconds(30));
407     EXPECT_TRUE(timer->hasExpired());
408     timer->setInterval(milliseconds(10));
409     EXPECT_TRUE(timer->hasExpired());
410     expectNow(milliseconds(20));
411     expectSetTime(milliseconds(30));
412     timer->resetRemaining();
413     EXPECT_TRUE(timer->hasExpired());
414 
415     timer->clearExpired();
416     EXPECT_FALSE(timer->hasExpired());
417 }
418 
419 TEST_F(TimerTest, Restart)
420 {
421     expireTimer();
422 
423     const milliseconds new_interval(471);
424     expectNow(starting_time);
425     expectSetTime(starting_time + new_interval);
426     expectSetEnabled(source::Enabled::On);
427     timer->restart(new_interval);
428     EXPECT_FALSE(timer->hasExpired());
429     EXPECT_EQ(new_interval, timer->getInterval());
430     expectSetEnabled(source::Enabled::On);
431     timer->setEnabled(true);
432 }
433 
434 TEST_F(TimerTest, RestartEmpty)
435 {
436     expireTimer();
437 
438     expectSetEnabled(source::Enabled::Off);
439     timer->restart(std::nullopt);
440     EXPECT_FALSE(timer->hasExpired());
441     EXPECT_EQ(std::nullopt, timer->getInterval());
442     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
443 }
444 
445 TEST_F(TimerTest, RestartOnce)
446 {
447     expireTimer();
448 
449     const milliseconds remaining(471);
450     expectNow(starting_time);
451     expectSetTime(starting_time + remaining);
452     expectSetEnabled(source::Enabled::On);
453     timer->restartOnce(remaining);
454     EXPECT_FALSE(timer->hasExpired());
455     EXPECT_EQ(std::nullopt, timer->getInterval());
456     expectSetEnabled(source::Enabled::On);
457     timer->setEnabled(true);
458 }
459 
460 } // namespace
461 } // namespace utility
462 } // namespace sdeventplus
463