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