]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tests/testEventLoop.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / tests / testEventLoop.cc
CommitLineData
4e0938ef 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
4e0938ef
AJ
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
f7f3304a 9#include "squid.h"
e1f7507e 10
a553a5a3 11#include <cppunit/TestAssert.h>
12
314782d4 13#include "AsyncEngine.h"
a553a5a3 14#include "EventLoop.h"
ed6e9fb9 15#include "mem/forward.h"
314782d4 16#include "SquidTime.h"
6ea5959e 17#include "stat.h"
602d9612 18#include "testEventLoop.h"
7f861c77
AJ
19#include "unitTestMain.h"
20
a553a5a3 21CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop );
22
a553a5a3 23/* init legacy static-initialized modules */
24
16555581 25void
26testEventLoop::setUp()
a553a5a3 27{
16555581 28 Mem::Init();
29 statInit();
30}
a553a5a3 31
32/*
33 * Test creating a EventLoop
34 */
35void
36testEventLoop::testCreate()
37{
38 EventLoop();
39}
40
cb9b9424 41#if POLISHED_MAIN_LOOP
a553a5a3 42
43/*
26ac0430 44 * Running the loop once is useful for integration with other loops, such as
a553a5a3 45 * migrating to it in incrementally.
46 *
8ff3fa2e 47 * This test works by having a custom dispatcher and engine which record how
48 * many times they are called.
a553a5a3 49 */
50
51class RecordDispatcher : public CompletionDispatcher
52{
53
54public:
55 int calls;
26ac0430 56 RecordDispatcher(): calls(0) {}
a553a5a3 57
26ac0430 58 bool dispatch() {
8ff3fa2e 59 ++calls;
60 /* claim we dispatched calls to be useful for the testStopOnIdle test.
61 */
62 return true;
63 }
a553a5a3 64};
65
cb9b9424 66#endif /* POLISHED_MAIN_LOOP */
67
8ff3fa2e 68class RecordingEngine : public AsyncEngine
69{
70
71public:
72 int calls;
73 int lasttimeout;
74 int return_timeout;
9dca980d 75 RecordingEngine(int aTimeout=0): calls(0), lasttimeout(0), return_timeout(aTimeout) {}
8ff3fa2e 76
26ac0430
AJ
77 virtual int checkEvents(int timeout) {
78 ++calls;
79 lasttimeout = timeout;
80 return return_timeout;
81 }
82};
8ff3fa2e 83
cb9b9424 84#if POLISHED_MAIN_LOOP
85
a553a5a3 86void
87testEventLoop::testRunOnce()
88{
89 EventLoop theLoop;
90 RecordDispatcher dispatcher;
91 theLoop.registerDispatcher(&dispatcher);
8ff3fa2e 92 RecordingEngine engine;
93 theLoop.registerEngine(&engine);
a553a5a3 94 theLoop.runOnce();
95 CPPUNIT_ASSERT_EQUAL(1, dispatcher.calls);
8ff3fa2e 96 CPPUNIT_ASSERT_EQUAL(1, engine.calls);
a553a5a3 97}
98
99/*
8ff3fa2e 100 * completion dispatchers registered with the event loop are invoked by the
101 * event loop.
a553a5a3 102 *
8ff3fa2e 103 * This test works by having a customer dispatcher which shuts the loop down
104 * once its been invoked twice.
a553a5a3 105 *
8ff3fa2e 106 * It also tests that loop.run() and loop.stop() work, because if they dont
107 * work, this test will either hang, or fail.
a553a5a3 108 */
109
110class ShutdownDispatcher : public CompletionDispatcher
111{
112
113public:
114 EventLoop &theLoop;
115 int calls;
26ac0430 116 ShutdownDispatcher(EventLoop & theLoop):theLoop(theLoop), calls(0) {}
a553a5a3 117
26ac0430 118 bool dispatch() {
8ff3fa2e 119 if (++calls == 2)
120 theLoop.stop();
121
122 return true;
123 }
a553a5a3 124};
125
126void
127testEventLoop::testRegisterDispatcher()
128{
129 EventLoop theLoop;
130 ShutdownDispatcher testDispatcher(theLoop);
131 theLoop.registerDispatcher(&testDispatcher);
132 theLoop.run();
8ff3fa2e 133 /* we should get two calls because the test dispatched returns true from
134 * dispatch(), and calls stop on the second call.
135 */
a553a5a3 136 CPPUNIT_ASSERT_EQUAL(2, testDispatcher.calls);
137}
8ff3fa2e 138
139/* test that a registered async engine is invoked on each loop run
140 * we do this with an intstrumented async engine.
141 */
142void
143testEventLoop::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 */
164void
165testEventLoop::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 */
181void
182testEventLoop::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);
bef81ea5 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());
8ff3fa2e 221}
222
cb9b9424 223#endif /* POLISHED_MAIN_LOOP */
224
8ff3fa2e 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
229class StubTime : public TimeEngine
230{
231
232public:
43d8f31d 233 StubTime() : calls(0) {}
8ff3fa2e 234
235 int calls;
26ac0430 236 void tick() {
8ff3fa2e 237 ++calls;
238 }
239};
240
241void
242testEventLoop::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}
bef81ea5 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 */
261void
262testEventLoop::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();
6289f91f 270 CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, first_engine.lasttimeout);
bef81ea5 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);
bef81ea5 281}
f53969cc 282