]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/testEventLoop.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / tests / testEventLoop.cc
1 /*
2 * Copyright (C) 1996-2018 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
21 CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop );
22
23 /* init legacy static-initialized modules */
24
25 void
26 testEventLoop::setUp()
27 {
28 Mem::Init();
29 statInit();
30 }
31
32 /*
33 * Test creating a EventLoop
34 */
35 void
36 testEventLoop::testCreate()
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
68 class RecordingEngine : public AsyncEngine
69 {
70
71 public:
72 int calls;
73 int lasttimeout;
74 int return_timeout;
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 */
197 RecordingEngine engine(AsyncEngine::EVENT_IDLE);
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 */
206 RecordingEngine failing_engine(AsyncEngine::EVENT_ERROR);
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
242 testEventLoop::testSetTimeService()
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
262 testEventLoop::testSetPrimaryEngine()
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