testEventLoop.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 
11 #include <cppunit/TestAssert.h>
12 
13 #include "AsyncEngine.h"
14 #include "EventLoop.h"
15 #include "mem/forward.h"
16 #include "SquidTime.h"
17 #include "stat.h"
18 #include "testEventLoop.h"
19 #include "unitTestMain.h"
20 
22 
23 /* init legacy static-initialized modules */
24 
25 void
27 {
28  Mem::Init();
29  statInit();
30 }
31 
32 /*
33  * Test creating a EventLoop
34  */
35 void
37 {
38  EventLoop();
39 }
40 
41 #if POLISHED_MAIN_LOOP
42 
43 /*
44  * Running the loop once is useful for integration with other loops, such as
45  * migrating to it in incrementally.
46  *
47  * This test works by having a custom dispatcher and engine which record how
48  * many times they are called.
49  */
50 
51 class RecordDispatcher : public CompletionDispatcher
52 {
53 
54 public:
55  int calls;
56  RecordDispatcher(): calls(0) {}
57 
58  bool dispatch() {
59  ++calls;
60  /* claim we dispatched calls to be useful for the testStopOnIdle test.
61  */
62  return true;
63  }
64 };
65 
66 #endif /* POLISHED_MAIN_LOOP */
67 
69 {
70 
71 public:
72  int calls;
75  RecordingEngine(int aTimeout=0): calls(0), lasttimeout(0), return_timeout(aTimeout) {}
76 
77  virtual int checkEvents(int timeout) {
78  ++calls;
79  lasttimeout = timeout;
80  return return_timeout;
81  }
82 };
83 
84 #if POLISHED_MAIN_LOOP
85 
86 void
87 testEventLoop::testRunOnce()
88 {
89  EventLoop theLoop;
90  RecordDispatcher dispatcher;
91  theLoop.registerDispatcher(&dispatcher);
92  RecordingEngine engine;
93  theLoop.registerEngine(&engine);
94  theLoop.runOnce();
95  CPPUNIT_ASSERT_EQUAL(1, dispatcher.calls);
96  CPPUNIT_ASSERT_EQUAL(1, engine.calls);
97 }
98 
99 /*
100  * completion dispatchers registered with the event loop are invoked by the
101  * event loop.
102  *
103  * This test works by having a customer dispatcher which shuts the loop down
104  * once its been invoked twice.
105  *
106  * It also tests that loop.run() and loop.stop() work, because if they dont
107  * work, this test will either hang, or fail.
108  */
109 
110 class ShutdownDispatcher : public CompletionDispatcher
111 {
112 
113 public:
114  EventLoop &theLoop;
115  int calls;
116  ShutdownDispatcher(EventLoop & theLoop):theLoop(theLoop), calls(0) {}
117 
118  bool dispatch() {
119  if (++calls == 2)
120  theLoop.stop();
121 
122  return true;
123  }
124 };
125 
126 void
127 testEventLoop::testRegisterDispatcher()
128 {
129  EventLoop theLoop;
130  ShutdownDispatcher testDispatcher(theLoop);
131  theLoop.registerDispatcher(&testDispatcher);
132  theLoop.run();
133  /* we should get two calls because the test dispatched returns true from
134  * dispatch(), and calls stop on the second call.
135  */
136  CPPUNIT_ASSERT_EQUAL(2, testDispatcher.calls);
137 }
138 
139 /* test that a registered async engine is invoked on each loop run
140  * we do this with an intstrumented async engine.
141  */
142 void
143 testEventLoop::testRegisterEngine()
144 {
145  EventLoop theLoop;
146  ShutdownDispatcher testDispatcher(theLoop);
147  theLoop.registerDispatcher(&testDispatcher);
148  RecordingEngine testEngine;
149  theLoop.registerEngine(&testEngine);
150  theLoop.run();
151  CPPUNIT_ASSERT_EQUAL(2, testEngine.calls);
152 }
153 
154 /* each AsyncEngine needs to be given a timeout. We want one engine in each
155  * loop to be given the timeout value - and the rest to have a timeout of 0.
156  * The last registered engine should be given this timeout, which will mean
157  * that we dont block in the loop until the last engine. This will allow for
158  * dynamic introduction and removal of engines, as long as the last engine
159  * is one which can do a os call rather than busy waiting.
160  *
161  * So - we want the timeout hints returned from the earlier engines to be
162  * tracked, and the lowest non-negative value given to the last engine.
163  */
164 void
165 testEventLoop::testEngineTimeout()
166 {
167  EventLoop theLoop;
168  RecordingEngine engineOne(5);
169  RecordingEngine engineTwo;
170  theLoop.registerEngine(&engineOne);
171  theLoop.registerEngine(&engineTwo);
172  theLoop.runOnce();
173  CPPUNIT_ASSERT_EQUAL(0, engineOne.lasttimeout);
174  CPPUNIT_ASSERT_EQUAL(5, engineTwo.lasttimeout);
175 }
176 
177 /* An event loop with all idle engines, and nothing dispatched in a run should
178  * automatically quit. The runOnce call should return True when the loop is
179  * entirely idle to make it easy for people running the loop by hand.
180  */
181 void
182 testEventLoop::testStopOnIdle()
183 {
184  EventLoop theLoop;
185  /* trivial case - no dispatchers or engines, should quit immediately */
186  CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce());
187  theLoop.run();
188  /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its
189  * sufficient and handy
190  */
191  EventDispatcher dispatcher;
192  theLoop.registerDispatcher(&dispatcher);
193  CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce());
194  theLoop.run();
195  /* add an engine which is idle.
196  */
198  theLoop.registerEngine(&engine);
199  CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce());
200  CPPUNIT_ASSERT_EQUAL(1, engine.calls);
201  theLoop.run();
202  CPPUNIT_ASSERT_EQUAL(2, engine.calls);
203  /* add an engine which is suffering errors. This should result in 10
204  * loops until the loop stops - because thats the error retry amount
205  */
207  theLoop.registerEngine(&failing_engine);
208  CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce());
209  CPPUNIT_ASSERT_EQUAL(1, failing_engine.calls);
210  theLoop.run();
211  /* run resets the error count ... */
212  CPPUNIT_ASSERT_EQUAL(11, failing_engine.calls);
213 
214  /* an engine that asks for a timeout should not be detected as idle:
215  * use runOnce which should return false
216  */
217  theLoop = EventLoop();
218  RecordingEngine non_idle_engine(1000);
219  theLoop.registerEngine(&non_idle_engine);
220  CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce());
221 }
222 
223 #endif /* POLISHED_MAIN_LOOP */
224 
225 /* An event loop has a time service which is like an async engine but never
226  * generates events and there can only be one such service.
227  */
228 
229 class StubTime : public TimeEngine
230 {
231 
232 public:
233  StubTime() : calls(0) {}
234 
235  int calls;
236  void tick() {
237  ++calls;
238  }
239 };
240 
241 void
243 {
244  EventLoop theLoop;
245  StubTime myTime;
246  /* the loop will not error without a time service */
247  theLoop.runOnce();
248  /* we can set the time service */
249  theLoop.setTimeService(&myTime);
250  /* it invokes our tick() call */
251  theLoop.runOnce();
252  CPPUNIT_ASSERT_EQUAL(1, myTime.calls);
253  /* it invokes our tick() call again */
254  theLoop.runOnce();
255  CPPUNIT_ASSERT_EQUAL(2, myTime.calls);
256 }
257 
258 /* one async engine is the primary engine - the engine that is allowed to block.
259  * this defaults to the last added one, but can be explicitly nominated
260  */
261 void
263 {
264  EventLoop theLoop;
265  RecordingEngine first_engine(10);
266  RecordingEngine second_engine(10);
267  /* one engine - gets a timeout */
268  theLoop.registerEngine(&first_engine);
269  theLoop.runOnce();
270  CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, first_engine.lasttimeout);
271  /* two engines - the second gets the timeout */
272  theLoop.registerEngine(&second_engine);
273  theLoop.runOnce();
274  CPPUNIT_ASSERT_EQUAL(0, first_engine.lasttimeout);
275  CPPUNIT_ASSERT_EQUAL(10, second_engine.lasttimeout);
276  /* set the first engine to be primary explicitly and now gets the timeout */
277  theLoop.setPrimaryEngine(&first_engine);
278  theLoop.runOnce();
279  CPPUNIT_ASSERT_EQUAL(10, first_engine.lasttimeout);
280  CPPUNIT_ASSERT_EQUAL(0, second_engine.lasttimeout);
281 }
282 
void tick()
void run()
Definition: EventLoop.cc:76
virtual int checkEvents(int timeout)
void testSetTimeService()
void stop()
Definition: EventLoop.cc:168
#define EVENT_LOOP_TIMEOUT
Definition: EventLoop.h:14
void statInit(void)
Definition: stat.cc:1223
void Init()
Definition: old_api.cc:412
RecordingEngine(int aTimeout=0)
void testSetPrimaryEngine()
void setPrimaryEngine(AsyncEngine *engine)
Definition: EventLoop.cc:149
bool runOnce()
Definition: EventLoop.cc:89
void registerEngine(AsyncEngine *engine)
Definition: EventLoop.cc:70
void setTimeService(TimeEngine *engine)
Definition: EventLoop.cc:162
CPPUNIT_TEST_SUITE_REGISTRATION(testEventLoop)
virtual bool dispatch()=0

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors