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