blob: 9d05e550988480f5451d90211d129b3a5fec98cc [file] [log] [blame]
Matt Spinler59c29c72017-04-27 11:17:28 -05001/**
2 * Copyright © 2017 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <iostream>
17#include <chrono>
18#include <gtest/gtest.h>
Matt Spinlere824f982017-05-11 10:07:55 -050019#include "event.hpp"
Matt Spinler59c29c72017-04-27 11:17:28 -050020#include "timer.hpp"
21
22/**
23 * Testcases for the Timer class
24 */
25
26using namespace phosphor::fan::util;
27using namespace std::chrono;
28
Matt Spinler59c29c72017-04-27 11:17:28 -050029
30/**
31 * Class to ensure sd_events are correctly
32 * setup and destroyed.
33 */
34class TimerTest : public ::testing::Test
35{
36 public:
37 // systemd event handler
Matt Spinlere824f982017-05-11 10:07:55 -050038 phosphor::fan::event::EventPtr events;
Matt Spinler59c29c72017-04-27 11:17:28 -050039
40 // Need this so that events can be initialized.
41 int rc;
42
43 // Gets called as part of each TEST_F construction
44 TimerTest()
45 {
46 sd_event* event = nullptr;
47 auto rc = sd_event_default(&event);
48 EXPECT_GE(rc, 0);
49
Matt Spinlere824f982017-05-11 10:07:55 -050050 events.reset(event);
Matt Spinler59c29c72017-04-27 11:17:28 -050051 }
52};
53
54/**
55 * Helper class to hande tracking timer expirations
56 * via callback functions.
57 */
58class CallbackTester
59{
60 public:
61
62 CallbackTester() {}
63
64 size_t getCount()
65 {
66 return _count;
67 }
68
69 void callbackFunction()
70 {
71 _count++;
72 _gotCallback = true;
73 }
74
75 bool gotCallback()
76 {
77 return _gotCallback;
78 }
79
80 private:
81 bool _gotCallback = false;
82 size_t _count = 0;
83};
84
85
86/**
87 * Helper class that more closely mimics real usage,
88 * which is another class containing a timer and using
89 * one of its member functions as the callback.
90 */
91class CallbackTesterWithTimer : public CallbackTester
92{
93 public:
Matt Spinlere824f982017-05-11 10:07:55 -050094 CallbackTesterWithTimer(phosphor::fan::event::EventPtr& events) :
Matt Spinler59c29c72017-04-27 11:17:28 -050095 _timer(events,
96 std::bind(&CallbackTesterWithTimer::callbackFunction,
97 this))
98 {
99 }
100
101 void callbackFunction()
102 {
103 //restart the timer once from the callback
104 if (!_restarted)
105 {
106 _restarted = true;
107 auto time = duration_cast<microseconds>(seconds(1));
108 _timer.start(time, Timer::TimerType::oneshot);
109 }
110
111 CallbackTester::callbackFunction();
112 }
113
114 Timer& getTimer()
115 {
116 return _timer;
117 }
118
119 inline bool restarted() const
120 {
121 return _restarted;
122 }
123
124 private:
125
126 Timer _timer;
127 bool _restarted = false;
128};
129
130
131/**
132 * Test that a callback will occur after 2 seconds.
133 */
134TEST_F(TimerTest, timerExpiresAfter2seconds)
135{
136 CallbackTester tester;
137
138 Timer timer(events,
139 std::bind(&CallbackTester::callbackFunction, &tester));
140
141
142 auto time = duration_cast<microseconds>(seconds(2));
143
144 EXPECT_EQ(false, timer.running());
145
146 timer.start(time, Timer::TimerType::oneshot);
147 EXPECT_EQ(false, tester.gotCallback());
148 EXPECT_EQ(true, timer.running());
149
150 int count = 0;
151 auto sleepTime = duration_cast<microseconds>(seconds(1));
152
153 //Wait for 2 1s timeouts
154 while (count < 2)
155 {
156 // Returns 0 on timeout and positive number on dispatch
157 if (sd_event_run(events.get(), sleepTime.count()) == 0)
158 {
159 count++;
160 }
161 }
162
163 EXPECT_EQ(true, tester.gotCallback());
164 EXPECT_EQ(1, tester.getCount());
165 EXPECT_EQ(false, timer.running());
166}
167
168/**
169 * Test that a timer can be restarted.
170 */
171TEST_F(TimerTest, timerRestart)
172{
173 CallbackTester tester;
174
175 Timer timer(events,
176 std::bind(&CallbackTester::callbackFunction, &tester));
177
178
179 auto time = duration_cast<microseconds>(seconds(2));
180 timer.start(time, Timer::TimerType::oneshot);
181
182 //wait for a second
183 auto sleepTime = duration_cast<microseconds>(seconds(1));
184 auto rc = sd_event_run(events.get(), sleepTime.count());
185
186 //expect the timeout, not the dispatch
187 //and the timer should still be running
188 EXPECT_EQ(0, rc);
189 EXPECT_EQ(true, timer.running());
190
191 //Restart it
192 timer.start(time, Timer::TimerType::oneshot);
193
194 //Wait just 1s, make sure not done
195 rc = sd_event_run(events.get(), sleepTime.count());
196 EXPECT_EQ(0, rc);
197 EXPECT_EQ(true, timer.running());
198 EXPECT_EQ(false, tester.gotCallback());
199
200 //Wait 1 more second, this time expecting a dispatch
201 int count = 0;
202 while (count < 1)
203 {
204 // Returns 0 on timeout and positive number on dispatch
205 if (sd_event_run(events.get(), sleepTime.count()) == 0)
206 {
207 count++;
208 }
209 }
210
211 EXPECT_EQ(true, tester.gotCallback());
212 EXPECT_EQ(1, tester.getCount());
213 EXPECT_EQ(false, timer.running());
214}
215
216
217/**
218 * Test that a timer can be stopped.
219 */
220TEST_F(TimerTest, timerStop)
221{
222 CallbackTester tester;
223
224 Timer timer(events,
225 std::bind(&CallbackTester::callbackFunction, &tester));
226
227
228 auto time = duration_cast<microseconds>(seconds(2));
229 timer.start(time, Timer::TimerType::oneshot);
230
231 auto sleepTime = duration_cast<microseconds>(seconds(1));
232
233 //wait 1s
234 auto rc = sd_event_run(events.get(), sleepTime.count());
235
236 //expect the timeout, not the dispatch
237 EXPECT_EQ(rc, 0);
238 EXPECT_EQ(true, timer.running());
239
240 timer.stop();
241
242 EXPECT_EQ(false, timer.running());
243 EXPECT_EQ(false, tester.gotCallback());
244
245 //Wait another 2s, make sure no callbacks happened
246 sleepTime = duration_cast<microseconds>(seconds(2));
247 rc = sd_event_run(events.get(), sleepTime.count());
248
249 EXPECT_EQ(rc, 0);
250 EXPECT_EQ(false, timer.running());
251 EXPECT_EQ(false, tester.gotCallback());
252}
253
254
255/**
256 * Test that the timer can be restarted from within
257 * a callback function.
258 */
259TEST_F(TimerTest, timerRestartFromCallback)
260{
261 CallbackTesterWithTimer tester(events);
262
263 auto& timer = tester.getTimer();
264
265 auto time = duration_cast<microseconds>(seconds(2));
266 timer.start(time, Timer::TimerType::oneshot);
267
268 //after running for 2 seconds, the timer will get restarted
269 //for another 1s
270
271 int count = 0;
272 auto sleepTime = duration_cast<microseconds>(seconds(1));
273 while (count < 3)
274 {
275 // Returns 0 on timeout and positive number on dispatch
276 if (sd_event_run(events.get(), sleepTime.count()) == 0)
277 {
278 count++;
279 }
280 }
281
282 EXPECT_EQ(false, timer.running());
283 EXPECT_EQ(true, tester.gotCallback());
284 EXPECT_EQ(2, tester.getCount()); //2 callbacks
285 EXPECT_EQ(true, tester.restarted());
286}
287
288/**
289 * This shows what happens when the timer expires but
290 * sd_event_run never got called.
291 */
292TEST_F(TimerTest, timerNoEventRun)
293{
294 CallbackTester tester;
295
296 Timer timer(events,
297 std::bind(&CallbackTester::callbackFunction, &tester));
298
299
300 auto time = duration_cast<microseconds>(milliseconds(500));
301
302 timer.start(time, Timer::TimerType::oneshot);
303
304 sleep(1);
305
306 //The timer should have expired, but with no event processing
307 //it will still think it's running.
308
309 EXPECT_EQ(true, timer.running());
310 EXPECT_EQ(false, tester.gotCallback());
311
312 //Now process an event
313 auto sleepTime = duration_cast<microseconds>(milliseconds(5));
314 auto rc = sd_event_run(events.get(), sleepTime.count());
315
316 EXPECT_GT(rc, 0);
317 EXPECT_EQ(false, timer.running());
318 EXPECT_EQ(true, tester.gotCallback());
319}
320
321
322/**
323 * Tests that a timer in repeating mode will keep calling
324 * the callback.
325 */
326TEST_F(TimerTest, RepeatingTimer)
327{
328 CallbackTester tester;
329
330 Timer timer(events,
331 std::bind(&CallbackTester::callbackFunction, &tester));
332
333 auto time = duration_cast<microseconds>(seconds(1));
334 timer.start(time, Timer::TimerType::repeating);
335
336 int count = 0;
337 auto sleepTime = duration_cast<microseconds>(milliseconds(500));
338
339 while (count < 5)
340 {
341 if (sd_event_run(events.get(), sleepTime.count()) == 0)
342 {
343 count++;
344 }
345 }
346
347 EXPECT_EQ(true, timer.running());
348 EXPECT_EQ(true, tester.gotCallback());
349 EXPECT_EQ(4, tester.getCount());
350
351 timer.stop();
352
353 EXPECT_EQ(false, timer.running());
354}