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