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