xref: /openbmc/sdeventplus/test/utility/timer.cpp (revision 27b7301a)
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     const milliseconds interval{134};
51     const milliseconds starting_time{10};
52     sd_event_time_handler_t handler = nullptr;
53     void* handler_userdata;
54     std::unique_ptr<Event> event;
55     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 
88     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 
107     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)));
113         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)));
120 
121         // Having a callback proxy allows us to update the test callback
122         // dynamically, without changing it inside the timer
123         auto runCallback = [&](TestTimer&) {
124             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)));
137         expectSetEnabled(source::Enabled::On);
138         timer = std::make_unique<TestTimer>(*event, runCallback, interval);
139         EXPECT_EQ(expected_event, timer->get_event().get());
140     }
141 
142     void TearDown()
143     {
144         resetTimer();
145         event.reset();
146         EXPECT_EQ(0, event_ref_times);
147     }
148 };
149 
150 TEST_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 
169 TEST_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 
186 TEST_F(TimerTest, NewTimer)
187 {
188     EXPECT_FALSE(timer->hasExpired());
189     EXPECT_EQ(interval, timer->getInterval());
190 }
191 
192 TEST_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 
200 TEST_F(TimerTest, GetRemainingDisabled)
201 {
202     expectGetEnabled(source::Enabled::Off);
203     EXPECT_THROW(timer->getRemaining(), std::runtime_error);
204 }
205 
206 TEST_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 
217 TEST_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 
228 TEST_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 
247 TEST_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 
261 TEST_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 
273 TEST_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 
283 TEST_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 
293 TEST_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 
301 TEST_F(TimerTest, SetIntervalEmpty)
302 {
303     timer->setInterval(std::nullopt);
304     EXPECT_EQ(std::nullopt, timer->getInterval());
305     EXPECT_FALSE(timer->hasExpired());
306 }
307 
308 TEST_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 
327 TEST_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 
343 TEST_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 
371 TEST_F(TimerTest, Restart)
372 {
373     expireTimer();
374 
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());
382     expectSetEnabled(source::Enabled::On);
383     timer->setEnabled(true);
384 }
385 
386 TEST_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 
397 TEST_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);
410 }
411 
412 } // namespace
413 } // namespace utility
414 } // namespace sdeventplus
415