xref: /openbmc/sdeventplus/test/utility/timer.cpp (revision ba04ffb5)
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     }
140 
141     void TearDown()
142     {
143         resetTimer();
144         event.reset();
145         EXPECT_EQ(0, event_ref_times);
146     }
147 };
148 
149 TEST_F(TimerTest, NoCallback)
150 {
151     resetTimer();
152     expectNow(starting_time);
153     EXPECT_CALL(
154         mock, sd_event_add_time(expected_event, testing::_,
155                                 static_cast<clockid_t>(testClock),
156                                 microseconds(starting_time + interval).count(),
157                                 1000, testing::_, nullptr))
158         .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
159                         Return(0)));
160     expectSetEnabled(source::Enabled::On);
161     timer = std::make_unique<TestTimer>(*event, nullptr, interval);
162 
163     expectNow(starting_time);
164     expectSetTime(starting_time + interval);
165     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
166 }
167 
168 TEST_F(TimerTest, NoInterval)
169 {
170     resetTimer();
171     expectNow(starting_time);
172     EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_,
173                                         static_cast<clockid_t>(testClock),
174                                         microseconds(starting_time).count(),
175                                         1000, testing::_, nullptr))
176         .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler),
177                         Return(0)));
178     expectSetEnabled(source::Enabled::Off);
179     timer = std::make_unique<TestTimer>(*event, nullptr);
180 
181     EXPECT_EQ(std::nullopt, timer->getInterval());
182     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
183 }
184 
185 TEST_F(TimerTest, NewTimer)
186 {
187     EXPECT_FALSE(timer->hasExpired());
188     EXPECT_EQ(interval, timer->getInterval());
189 }
190 
191 TEST_F(TimerTest, IsEnabled)
192 {
193     expectGetEnabled(source::Enabled::On);
194     EXPECT_TRUE(timer->isEnabled());
195     expectGetEnabled(source::Enabled::Off);
196     EXPECT_FALSE(timer->isEnabled());
197 }
198 
199 TEST_F(TimerTest, GetRemainingDisabled)
200 {
201     expectGetEnabled(source::Enabled::Off);
202     EXPECT_THROW(timer->getRemaining(), std::runtime_error);
203 }
204 
205 TEST_F(TimerTest, GetRemainingNegative)
206 {
207     milliseconds now(675), end(453);
208     expectGetEnabled(source::Enabled::On);
209     EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
210         .WillOnce(
211             DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
212     expectNow(now);
213     EXPECT_EQ(milliseconds(0), timer->getRemaining());
214 }
215 
216 TEST_F(TimerTest, GetRemainingPositive)
217 {
218     milliseconds now(453), end(675);
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(end - now, timer->getRemaining());
225 }
226 
227 TEST_F(TimerTest, SetEnabled)
228 {
229     expectSetEnabled(source::Enabled::On);
230     timer->setEnabled(true);
231     EXPECT_FALSE(timer->hasExpired());
232     // Value should always be passed through regardless of current state
233     expectSetEnabled(source::Enabled::On);
234     timer->setEnabled(true);
235     EXPECT_FALSE(timer->hasExpired());
236 
237     expectSetEnabled(source::Enabled::Off);
238     timer->setEnabled(false);
239     EXPECT_FALSE(timer->hasExpired());
240     // Value should always be passed through regardless of current state
241     expectSetEnabled(source::Enabled::Off);
242     timer->setEnabled(false);
243     EXPECT_FALSE(timer->hasExpired());
244 }
245 
246 TEST_F(TimerTest, SetEnabledUnsetTimer)
247 {
248     // Force the timer to become unset
249     expectSetEnabled(source::Enabled::Off);
250     timer->restart(std::nullopt);
251 
252     // Setting an interval should not update the timer directly
253     timer->setInterval(milliseconds(90));
254 
255     expectSetEnabled(source::Enabled::Off);
256     timer->setEnabled(false);
257     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
258 }
259 
260 TEST_F(TimerTest, SetEnabledOneshot)
261 {
262     // Timer effectively becomes oneshot if it gets initialized but has
263     // the interval removed
264     timer->setInterval(std::nullopt);
265 
266     expectSetEnabled(source::Enabled::Off);
267     timer->setEnabled(false);
268     expectSetEnabled(source::Enabled::On);
269     timer->setEnabled(true);
270 }
271 
272 TEST_F(TimerTest, SetRemaining)
273 {
274     const milliseconds now(90), remaining(30);
275     expectNow(now);
276     expectSetTime(now + remaining);
277     timer->setRemaining(remaining);
278     EXPECT_EQ(interval, timer->getInterval());
279     EXPECT_FALSE(timer->hasExpired());
280 }
281 
282 TEST_F(TimerTest, ResetRemaining)
283 {
284     const milliseconds now(90);
285     expectNow(now);
286     expectSetTime(now + interval);
287     timer->resetRemaining();
288     EXPECT_EQ(interval, timer->getInterval());
289     EXPECT_FALSE(timer->hasExpired());
290 }
291 
292 TEST_F(TimerTest, SetInterval)
293 {
294     const milliseconds new_interval(40);
295     timer->setInterval(new_interval);
296     EXPECT_EQ(new_interval, timer->getInterval());
297     EXPECT_FALSE(timer->hasExpired());
298 }
299 
300 TEST_F(TimerTest, SetIntervalEmpty)
301 {
302     timer->setInterval(std::nullopt);
303     EXPECT_EQ(std::nullopt, timer->getInterval());
304     EXPECT_FALSE(timer->hasExpired());
305 }
306 
307 TEST_F(TimerTest, CallbackHappensLast)
308 {
309     const milliseconds new_time(90);
310     expectNow(new_time);
311     expectSetTime(new_time + interval);
312     callback = [&]() {
313         EXPECT_TRUE(timer->hasExpired());
314         expectSetEnabled(source::Enabled::On);
315         timer->setEnabled(true);
316         timer->clearExpired();
317         timer->setInterval(std::nullopt);
318     };
319     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
320     EXPECT_FALSE(timer->hasExpired());
321     EXPECT_EQ(std::nullopt, timer->getInterval());
322     expectSetEnabled(source::Enabled::On);
323     timer->setEnabled(true);
324 }
325 
326 TEST_F(TimerTest, CallbackOneshot)
327 {
328     // Make sure we try a one shot so we can test the callback
329     // correctly
330     timer->setInterval(std::nullopt);
331 
332     expectSetEnabled(source::Enabled::Off);
333     callback = [&]() {
334         EXPECT_TRUE(timer->hasExpired());
335         EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
336         timer->setInterval(interval);
337     };
338     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
339     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
340 }
341 
342 TEST_F(TimerTest, SetValuesExpiredTimer)
343 {
344     const milliseconds new_time(90);
345     expectNow(new_time);
346     expectSetTime(new_time + interval);
347     EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
348     EXPECT_TRUE(timer->hasExpired());
349     EXPECT_EQ(interval, timer->getInterval());
350 
351     // Timer should remain expired unless clearExpired() or reset()
352     expectSetEnabled(source::Enabled::On);
353     timer->setEnabled(true);
354     EXPECT_TRUE(timer->hasExpired());
355     expectNow(milliseconds(20));
356     expectSetTime(milliseconds(50));
357     timer->setRemaining(milliseconds(30));
358     EXPECT_TRUE(timer->hasExpired());
359     timer->setInterval(milliseconds(10));
360     EXPECT_TRUE(timer->hasExpired());
361     expectNow(milliseconds(20));
362     expectSetTime(milliseconds(30));
363     timer->resetRemaining();
364     EXPECT_TRUE(timer->hasExpired());
365 
366     timer->clearExpired();
367     EXPECT_FALSE(timer->hasExpired());
368 }
369 
370 TEST_F(TimerTest, Restart)
371 {
372     expireTimer();
373 
374     const milliseconds new_interval(471);
375     expectNow(starting_time);
376     expectSetTime(starting_time + new_interval);
377     expectSetEnabled(source::Enabled::On);
378     timer->restart(new_interval);
379     EXPECT_FALSE(timer->hasExpired());
380     EXPECT_EQ(new_interval, timer->getInterval());
381     expectSetEnabled(source::Enabled::On);
382     timer->setEnabled(true);
383 }
384 
385 TEST_F(TimerTest, RestartEmpty)
386 {
387     expireTimer();
388 
389     expectSetEnabled(source::Enabled::Off);
390     timer->restart(std::nullopt);
391     EXPECT_FALSE(timer->hasExpired());
392     EXPECT_EQ(std::nullopt, timer->getInterval());
393     EXPECT_THROW(timer->setEnabled(true), std::runtime_error);
394 }
395 
396 TEST_F(TimerTest, RestartOnce)
397 {
398     expireTimer();
399 
400     const milliseconds remaining(471);
401     expectNow(starting_time);
402     expectSetTime(starting_time + remaining);
403     expectSetEnabled(source::Enabled::On);
404     timer->restartOnce(remaining);
405     EXPECT_FALSE(timer->hasExpired());
406     EXPECT_EQ(std::nullopt, timer->getInterval());
407     expectSetEnabled(source::Enabled::On);
408     timer->setEnabled(true);
409 }
410 
411 } // namespace
412 } // namespace utility
413 } // namespace sdeventplus
414