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