1 // Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8 #include <asiolink/asio_wrapper.h>
9 #include <asiolink/asiolink.h>
10
11 #include <boost/date_time/posix_time/posix_time.hpp>
12 #include <gtest/gtest.h>
13
14 namespace {
15 // TODO: Consider this margin
16 const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
17 boost::posix_time::milliseconds(50);
18 }
19
20 using namespace isc::asiolink;
21
22 // This fixture is for testing IntervalTimer. Some callback functors are
23 // registered as callback function of the timer to test if they are called
24 // or not.
25 class IntervalTimerTest : public ::testing::Test {
26 protected:
IntervalTimerTest()27 IntervalTimerTest() :
28 io_service_(), timer_called_(false), timer_cancel_success_(false)
29 {}
~IntervalTimerTest()30 ~IntervalTimerTest() {}
31 class TimerCallBack : public std::unary_function<void, void> {
32 public:
TimerCallBack(IntervalTimerTest * test_obj)33 TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
operator ()() const34 void operator()() const {
35 test_obj_->timer_called_ = true;
36 test_obj_->io_service_.stop();
37 return;
38 }
39 private:
40 IntervalTimerTest* test_obj_;
41 };
42 class TimerCallBackCounter : public std::unary_function<void, void> {
43 public:
TimerCallBackCounter(IntervalTimerTest * test_obj)44 TimerCallBackCounter(IntervalTimerTest* test_obj) :
45 test_obj_(test_obj)
46 {
47 counter_ = 0;
48 }
operator ()()49 void operator()() {
50 ++counter_;
51 return;
52 }
53 int counter_;
54 private:
55 IntervalTimerTest* test_obj_;
56 };
57 class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
58 public:
TimerCallBackCancelDeleter(IntervalTimerTest * test_obj,IntervalTimer * timer,TimerCallBackCounter & counter)59 TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
60 IntervalTimer* timer,
61 TimerCallBackCounter& counter)
62 : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0),
63 prev_counter_(-1)
64 {}
operator ()()65 void operator()() {
66 ++count_;
67 if (count_ == 1) {
68 // First time of call back.
69 // Store the value of counter_.counter_.
70 prev_counter_ = counter_.counter_;
71 delete timer_;
72 } else if (count_ == 2) {
73 // Second time of call back.
74 // Stop io_service to stop all timers.
75 test_obj_->io_service_.stop();
76 // Compare the value of counter_.counter_ with stored one.
77 // If TimerCallBackCounter was not called (expected behavior),
78 // they are same.
79 if (counter_.counter_ == prev_counter_) {
80 test_obj_->timer_cancel_success_ = true;
81 }
82 }
83 return;
84 }
85 private:
86 IntervalTimerTest* test_obj_;
87 IntervalTimer* timer_;
88 TimerCallBackCounter& counter_;
89 int count_;
90 int prev_counter_;
91 };
92 class TimerCallBackCanceller {
93 public:
TimerCallBackCanceller(unsigned int & counter,IntervalTimer & itimer)94 TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
95 counter_(counter), itimer_(itimer)
96 {}
operator ()()97 void operator()() {
98 ++counter_;
99 itimer_.cancel();
100 }
101 private:
102 unsigned int& counter_;
103 IntervalTimer& itimer_;
104 };
105 class TimerCallBackOverwriter : public std::unary_function<void, void> {
106 public:
TimerCallBackOverwriter(IntervalTimerTest * test_obj,IntervalTimer & timer)107 TimerCallBackOverwriter(IntervalTimerTest* test_obj,
108 IntervalTimer& timer)
109 : test_obj_(test_obj), timer_(timer), count_(0)
110 {}
operator ()()111 void operator()() {
112 ++count_;
113 if (count_ == 1) {
114 // First time of call back.
115 // Call setup() to update callback function to TimerCallBack.
116 test_obj_->timer_called_ = false;
117 timer_.setup(TimerCallBack(test_obj_), 100);
118 } else if (count_ == 2) {
119 // Second time of call back.
120 // If it reaches here, re-setup() is failed (unexpected).
121 // We should stop here.
122 test_obj_->io_service_.stop();
123 }
124 return;
125 }
126 private:
127 IntervalTimerTest* test_obj_;
128 IntervalTimer& timer_;
129 int count_;
130 };
131 class TimerCallBackAccumulator: public std::unary_function<void, void> {
132 public:
TimerCallBackAccumulator(IntervalTimerTest * test_obj,int & counter)133 TimerCallBackAccumulator(IntervalTimerTest* test_obj, int &counter) :
134 test_obj_(test_obj), counter_(counter) {
135 }
operator ()()136 void operator()() {
137 ++counter_;
138 return;
139 }
140 private:
141 IntervalTimerTest* test_obj_;
142 // Reference to integer accumulator
143 int& counter_;
144 };
145 protected:
146 IOService io_service_;
147 bool timer_called_;
148 bool timer_cancel_success_;
149 };
150
TEST_F(IntervalTimerTest,invalidArgumentToIntervalTimer)151 TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
152 // Create asio_link::IntervalTimer and setup.
153 IntervalTimer itimer(io_service_);
154 // expect throw if call back function is empty
155 EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
156 isc::InvalidParameter);
157 // expect throw if interval is negative.
158 EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
159 }
160
TEST_F(IntervalTimerTest,startIntervalTimer)161 TEST_F(IntervalTimerTest, startIntervalTimer) {
162 // Create asio_link::IntervalTimer and setup.
163 // Then run IOService and test if the callback function is called.
164 IntervalTimer itimer(io_service_);
165 timer_called_ = false;
166 // store start time
167 boost::posix_time::ptime start;
168 start = boost::posix_time::microsec_clock::universal_time();
169 // setup timer
170 itimer.setup(TimerCallBack(this), 100);
171 EXPECT_EQ(100, itimer.getInterval());
172 io_service_.run();
173 // Control reaches here after io_service_ was stopped by TimerCallBack.
174
175 // delta: difference between elapsed time and 100 milliseconds.
176 boost::posix_time::time_duration test_runtime =
177 boost::posix_time::microsec_clock::universal_time() - start;
178 EXPECT_FALSE(test_runtime.is_negative()) <<
179 "test duration " << test_runtime <<
180 " negative - clock skew?";
181 // Expect TimerCallBack is called; timer_called_ is true
182 EXPECT_TRUE(timer_called_);
183 // Expect test_runtime is 100 milliseconds or longer.
184 // Allow 1% of clock skew
185 EXPECT_TRUE(test_runtime >= boost::posix_time::milliseconds(99)) <<
186 "test runtime " << test_runtime.total_milliseconds() <<
187 "msec >= 100";
188 }
189
TEST_F(IntervalTimerTest,destructIntervalTimer)190 TEST_F(IntervalTimerTest, destructIntervalTimer) {
191 // This code isn't exception safe, but we'd rather keep the code
192 // simpler and more readable as this is only for tests and if it throws
193 // the program would immediately terminate anyway.
194
195 // The call back function will not be called after the timer is
196 // destroyed.
197 //
198 // There are two timers:
199 // itimer_counter (A)
200 // (Calls TimerCallBackCounter)
201 // - increments internal counter in callback function
202 // itimer_canceller (B)
203 // (Calls TimerCallBackCancelDeleter)
204 // - first time of callback, it stores the counter value of
205 // callback_canceller and destroys itimer_counter
206 // - second time of callback, it compares the counter value of
207 // callback_canceller with stored value
208 // if they are same the timer was not called; expected result
209 // if they are different the timer was called after destroyed
210 //
211 // 0 100 200 300 400 500 600 (ms)
212 // (A) i--------+----x
213 // ^
214 // |destroy itimer_counter
215 // (B) i-------------+--------------s
216 // ^stop io_service
217 // and check if itimer_counter have been
218 // stopped
219
220 // itimer_counter will be deleted in TimerCallBackCancelDeleter
221 IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
222 IntervalTimer itimer_canceller(io_service_);
223 timer_cancel_success_ = false;
224 TimerCallBackCounter callback_canceller(this);
225 itimer_counter->setup(callback_canceller, 200);
226 itimer_canceller.setup(
227 TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
228 300);
229 io_service_.run();
230 EXPECT_TRUE(timer_cancel_success_);
231 }
232
TEST_F(IntervalTimerTest,cancel)233 TEST_F(IntervalTimerTest, cancel) {
234 // Similar to destructIntervalTimer test, but the first timer explicitly
235 // cancels itself on first callback.
236 IntervalTimer itimer_counter(io_service_);
237 IntervalTimer itimer_watcher(io_service_);
238 unsigned int counter = 0;
239 itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
240 itimer_watcher.setup(TimerCallBack(this), 200);
241 io_service_.run();
242 EXPECT_EQ(1, counter);
243 EXPECT_EQ(0, itimer_counter.getInterval());
244
245 // canceling an already canceled timer shouldn't cause any surprise.
246 EXPECT_NO_THROW(itimer_counter.cancel());
247 }
248
TEST_F(IntervalTimerTest,overwriteIntervalTimer)249 TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
250 // Call setup() multiple times to update call back function and interval.
251 //
252 // There are two timers:
253 // itimer (A)
254 // (Calls TimerCallBackCounter / TimerCallBack)
255 // - increments internal counter in callback function
256 // (TimerCallBackCounter)
257 // interval: 300 milliseconds
258 // - io_service_.stop() (TimerCallBack)
259 // interval: 100 milliseconds
260 // itimer_overwriter (B)
261 // (Calls TimerCallBackOverwriter)
262 // - first time of callback, it calls setup() to change call back
263 // function to TimerCallBack and interval of itimer to 100
264 // milliseconds
265 // after 300 + 100 milliseconds from the beginning of this test,
266 // TimerCallBack() will be called and io_service_ stops.
267 // - second time of callback, it means the test fails.
268 //
269 // 0 100 200 300 400 500 600 700 800 (ms)
270 // (A) i-------------+----C----s
271 // ^ ^stop io_service
272 // |change call back function and interval
273 // (B) i------------------+-------------------S
274 // ^(stop io_service on fail)
275 //
276
277 IntervalTimer itimer(io_service_);
278 IntervalTimer itimer_overwriter(io_service_);
279 // store start time
280 boost::posix_time::ptime start;
281 start = boost::posix_time::microsec_clock::universal_time();
282 itimer.setup(TimerCallBackCounter(this), 300);
283 itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
284 io_service_.run();
285 // Control reaches here after io_service_ was stopped by
286 // TimerCallBackCounter or TimerCallBackOverwriter.
287
288 // Expect callback function is updated: TimerCallBack is called
289 EXPECT_TRUE(timer_called_);
290 // Expect interval is updated: return value of getInterval() is updated
291 EXPECT_EQ(itimer.getInterval(), 100);
292 }
293
294 // This test verifies that timers operate correctly based on their mode.
TEST_F(IntervalTimerTest,intervalModeTest)295 TEST_F(IntervalTimerTest, intervalModeTest) {
296 // Create a timer to control the duration of the test.
297 IntervalTimer test_timer(io_service_);
298 test_timer.setup(TimerCallBack(this), 2000);
299
300 // Create an timer which automatically reschedules itself. Use the
301 // accumulator callback to increment local counter for it.
302 int repeater_count = 0;
303 IntervalTimer repeater(io_service_);
304 repeater.setup(TimerCallBackAccumulator(this, repeater_count), 10);
305
306 // Create a one-shot timer. Use the accumulator callback to increment
307 // local counter variable for it.
308 int one_shot_count = 0;
309 IntervalTimer one_shot(io_service_);
310 one_shot.setup(TimerCallBackAccumulator(this, one_shot_count), 10,
311 IntervalTimer::ONE_SHOT);
312
313 // As long as service runs at least one event handler, loop until
314 // we've hit our goals. It won't return zero unless is out of
315 // work or the service has been stopped by the test timer.
316 int cnt = 0;
317 while (((cnt = io_service_.get_io_service().run_one()) > 0)
318 && (repeater_count < 5)) {
319 // deliberately empty
320 };
321
322 // If cnt is zero, then something went wrong.
323 EXPECT_TRUE(cnt > 0);
324
325 // The loop stopped make sure it was for the right reason.
326 EXPECT_EQ(repeater_count, 5);
327 EXPECT_EQ(one_shot_count, 1);
328 }
329
330 // This test verifies that the same timer can be reused in either mode.
TEST_F(IntervalTimerTest,timerReuseTest)331 TEST_F(IntervalTimerTest, timerReuseTest) {
332 // Create a timer to control the duration of the test.
333 IntervalTimer test_timer(io_service_);
334 test_timer.setup(TimerCallBack(this), 2000);
335
336 // Create a one-shot timer. Use the accumulator callback to increment
337 // local counter variable for it.
338 int one_shot_count = 0;
339 IntervalTimer one_shot(io_service_);
340 TimerCallBackAccumulator callback(this, one_shot_count);
341 one_shot.setup(callback, 10, IntervalTimer::ONE_SHOT);
342
343 // Run until a single event handler executes. This should be our
344 // one-shot expiring.
345 io_service_.run_one();
346
347 // Verify the timer expired once.
348 ASSERT_EQ(one_shot_count, 1);
349
350 // Setup the one-shot to go again.
351 one_shot.setup(callback, 10, IntervalTimer::ONE_SHOT);
352
353 // Run until a single event handler executes. This should be our
354 // one-shot expiring.
355 io_service_.run_one();
356
357 // Verify the timer expired once.
358 ASSERT_EQ(one_shot_count, 2);
359
360 // Setup the timer to be repeating.
361 one_shot.setup(callback, 10, IntervalTimer::REPEATING);
362
363 // As long as service runs at least one event handler, loop until
364 // we've hit our goals. It won't return zero unless is out of
365 // work or the service has been stopped by the test timer.
366 int cnt = 0;
367 while ((cnt = io_service_.get_io_service().run_one())
368 && (one_shot_count < 4)) {
369 // deliberately empty
370 };
371
372 // If cnt is zero, then something went wrong.
373 EXPECT_TRUE(cnt > 0);
374
375 // Verify the timer repeated.
376 EXPECT_GE(one_shot_count, 4);
377 }
378