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