PageRenderTime 145ms CodeModel.GetById 40ms app.highlight 64ms RepoModel.GetById 37ms app.codeStats 0ms

/mordor/tests/timer.cpp

http://github.com/mozy/mordor
C++ | 306 lines | 261 code | 33 blank | 12 comment | 2 complexity | fe44f42700f31cf90087f57dcfd3dfb8 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include <boost/bind.hpp>
  4
  5#include "mordor/timer.h"
  6#include "mordor/workerpool.h"
  7#include "mordor/test/test.h"
  8
  9using namespace Mordor;
 10using namespace Mordor::Test;
 11
 12static void
 13singleTimer(int &sequence, int &expected)
 14{
 15    ++sequence;
 16    MORDOR_TEST_ASSERT_EQUAL(sequence, expected);
 17}
 18
 19MORDOR_UNITTEST(Timer, single)
 20{
 21    int sequence = 0;
 22    TimerManager manager;
 23    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 24    manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence), 1));
 25    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
 26    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
 27    manager.executeTimers();
 28    ++sequence;
 29    MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
 30    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 31}
 32
 33MORDOR_UNITTEST(Timer, multiple)
 34{
 35    int sequence = 0;
 36    TimerManager manager;
 37    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 38    manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence),
 39        boost::ref(sequence)));
 40    manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence),
 41        boost::ref(sequence)));
 42    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
 43    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
 44    manager.executeTimers();
 45    ++sequence;
 46    MORDOR_TEST_ASSERT_EQUAL(sequence, 3);
 47    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 48}
 49
 50MORDOR_UNITTEST(Timer, cancel)
 51{
 52    int sequence = 0;
 53    TimerManager manager;
 54    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 55    Timer::ptr timer =
 56        manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence), 1));
 57    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
 58    timer->cancel();
 59    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 60    manager.executeTimers();
 61    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
 62}
 63
 64MORDOR_UNITTEST(Timer, idempotentCancel)
 65{
 66    int sequence = 0;
 67    TimerManager manager;
 68    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 69    Timer::ptr timer =
 70        manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence), 1));
 71    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
 72    timer->cancel();
 73    timer->cancel();
 74    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 75    manager.executeTimers();
 76    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
 77}
 78
 79MORDOR_UNITTEST(Timer, idempotentCancelAfterSuccess)
 80{
 81    int sequence = 0;
 82    TimerManager manager;
 83    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 84    Timer::ptr timer =
 85        manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence), 1));
 86    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
 87    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
 88    manager.executeTimers();
 89    ++sequence;
 90    MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
 91    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
 92    timer->cancel();
 93    timer->cancel();
 94}
 95
 96
 97MORDOR_UNITTEST(Timer, recurring)
 98{
 99    int sequence = 0;
100    int expected;
101    TimerManager manager;
102    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
103    Timer::ptr timer =
104        manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence),
105        boost::ref(expected)), true);
106    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
107    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
108    expected = 1;
109    manager.executeTimers();
110    ++sequence;
111    MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
112    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0u);
113    expected = 3;
114    manager.executeTimers();
115    ++sequence;
116    MORDOR_TEST_ASSERT_EQUAL(sequence, 4);
117    timer->cancel();
118    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
119}
120
121MORDOR_UNITTEST(Timer, later)
122{
123    int sequence = 0;
124    TimerManager manager;
125    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
126    Timer::ptr timer = manager.registerTimer(1000 * 1000 * 1000,
127        boost::bind(&singleTimer, boost::ref(sequence), 1));
128    MORDOR_TEST_ASSERT_ABOUT_EQUAL(manager.nextTimer(),
129        1000 * 1000 * 1000u, 100 * 1000 * 1000u);
130    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
131    manager.executeTimers();
132    ++sequence;
133    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
134    timer->cancel();
135    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
136}
137
138static unsigned long long fakeClock(unsigned long long &clock)
139{
140    return clock;
141}
142
143MORDOR_UNITTEST(Timer, rollover)
144{
145    // two minutes before the apocalypse
146    static unsigned long long clock = 0ULL - 120000000;
147    TimerManager::setClock(boost::bind(&fakeClock, boost::ref(clock)));
148
149    int sequence = 0;
150    TimerManager manager;
151
152    // sanity check - test passage of time
153    Timer::ptr timer1 = manager.registerTimer(60000000,
154        boost::bind(&singleTimer, boost::ref(sequence), boost::ref(sequence)));
155    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 60000000ULL);
156    clock += 30000000;
157    manager.executeTimers();
158    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);  // timer hasn't fired yet
159    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 30000000ULL); // timer is still 30 seconds out
160
161    // now create a few more timers for good measure
162    Timer::ptr timer2 = manager.registerTimer(15000000,     // pre-rollover
163        boost::bind(&singleTimer, boost::ref(sequence), boost::ref(sequence)));
164    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 15000000ULL);
165    Timer::ptr timer3 = manager.registerTimer(180000000,    // post-rollover
166        boost::bind(&singleTimer, boost::ref(sequence), boost::ref(sequence)));
167    // nextTimer() would return 0 now, because timer3 appears to be in the past
168
169    clock += 120000000; // overflow!!
170    manager.executeTimers();
171    MORDOR_TEST_ASSERT_EQUAL(sequence, 3);  // all timers should have fired
172    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ULL);   // no timers left
173
174    TimerManager::setClock();
175}
176
177namespace {
178// anonymous namespace so that the class is only visible in this compiling unit
179class TestTimerClass
180{
181public:
182    typedef boost::shared_ptr<TestTimerClass> ptr;
183    typedef boost::weak_ptr<TestTimerClass> weak_ptr;
184
185public:
186    TestTimerClass(int &i) : m_i(i) {}
187    ~TestTimerClass() { ++m_i; }
188    void timedOut(int expected) {
189        ++m_i;
190        MORDOR_TEST_ASSERT_EQUAL(m_i, expected);
191    }
192
193private:
194    int& m_i;
195};
196
197
198class TestTimerManager : public TimerManager
199{
200public:
201    std::vector<boost::function<void ()> > getExpiredTimers()
202    {
203        return processTimers();
204    }
205};
206
207}
208
209MORDOR_UNITTEST(Timer, timerConditonValid)
210{
211    TimerManager manager;
212    int sequence = 0;
213    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
214    TestTimerClass::ptr tester(new TestTimerClass(sequence));
215
216    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
217    Timer::ptr timer = manager.registerConditionTimer(0,
218        boost::bind(&TestTimerClass::timedOut, tester.get(), 1),
219        tester);
220    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0ull);
221    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
222    manager.executeTimers();
223    // TestTimerClass::timedOut is executed
224    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
225    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
226    tester.reset();
227    MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
228}
229
230MORDOR_UNITTEST(Timer, timerConditonInvalid)
231{
232    TimerManager manager;
233    int sequence = 0;
234    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
235    TestTimerClass::ptr tester(new TestTimerClass(sequence));
236
237    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
238    Timer::ptr timer = manager.registerConditionTimer(0,
239        boost::bind(&TestTimerClass::timedOut, tester.get(), 123456),
240        tester);
241    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0ull);
242    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
243    tester.reset();
244    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
245    manager.executeTimers();
246    // TestTimerClass::timedOut is NOT executed
247    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
248    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
249}
250
251MORDOR_UNITTEST(Timer, workerPoolConditionValid)
252{
253    TestTimerManager manager;
254    int sequence = 0;
255    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
256    TestTimerClass::ptr tester(new TestTimerClass(sequence));
257
258    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
259    Timer::ptr timer = manager.registerConditionTimer(0,
260        boost::bind(&TestTimerClass::timedOut, tester.get(), 1),
261        tester);
262    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0ull);
263    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
264    std::vector<boost::function<void ()> > dgs = manager.getExpiredTimers();
265    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
266    MORDOR_TEST_ASSERT_EQUAL(dgs.size(), 1u);
267    MORDOR_TEST_ASSERT(dgs.begin() != dgs.end());
268    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
269    // Use WorkerPool to execute the timers which is the IOManager behavior
270    WorkerPool pool;
271    pool.schedule(dgs.begin(), dgs.end());
272    pool.stop();
273    // TestTimerClass::timedOut is executed
274    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
275    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
276    tester.reset();
277    MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
278}
279
280MORDOR_UNITTEST(Timer, workerPoolConditonInvalid)
281{
282    TestTimerManager manager;
283    int sequence = 0;
284    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
285    TestTimerClass::ptr tester(new TestTimerClass(sequence));
286
287    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
288    Timer::ptr timer = manager.registerConditionTimer(0,
289        boost::bind(&TestTimerClass::timedOut, tester.get(), 123456),
290        tester);
291    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), 0ull);
292    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
293    std::vector<boost::function<void ()> > dgs = manager.getExpiredTimers();
294    MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
295    MORDOR_TEST_ASSERT_EQUAL(dgs.size(), 1u);
296    MORDOR_TEST_ASSERT(dgs.begin() != dgs.end());
297    MORDOR_TEST_ASSERT_EQUAL(manager.nextTimer(), ~0ull);
298    tester.reset();
299    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
300    // Use WorkerPool to execute the timers which is the IOManager behavior
301    WorkerPool pool;
302    pool.schedule(dgs.begin(), dgs.end());
303    pool.stop();
304    // TestTimerClass::timedOut is NOT executed
305    MORDOR_TEST_ASSERT_EQUAL(sequence, 1);
306}