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