2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
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.
11 #include <cppunit/TestAssert.h>
13 #include "AsyncEngine.h"
14 #include "EventLoop.h"
15 #include "mem/forward.h"
16 #include "SquidTime.h"
18 #include "testEventLoop.h"
19 #include "unitTestMain.h"
21 CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop
);
23 /* init legacy static-initialized modules */
26 testEventLoop::setUp()
33 * Test creating a EventLoop
36 testEventLoop::testCreate()
41 #if POLISHED_MAIN_LOOP
44 * Running the loop once is useful for integration with other loops, such as
45 * migrating to it in incrementally.
47 * This test works by having a custom dispatcher and engine which record how
48 * many times they are called.
51 class RecordDispatcher
: public CompletionDispatcher
56 RecordDispatcher(): calls(0) {}
60 /* claim we dispatched calls to be useful for the testStopOnIdle test.
66 #endif /* POLISHED_MAIN_LOOP */
68 class RecordingEngine
: public AsyncEngine
75 RecordingEngine(int aTimeout
=0): calls(0), lasttimeout(0), return_timeout(aTimeout
) {}
77 virtual int checkEvents(int timeout
) {
79 lasttimeout
= timeout
;
80 return return_timeout
;
84 #if POLISHED_MAIN_LOOP
87 testEventLoop::testRunOnce()
90 RecordDispatcher dispatcher
;
91 theLoop
.registerDispatcher(&dispatcher
);
92 RecordingEngine engine
;
93 theLoop
.registerEngine(&engine
);
95 CPPUNIT_ASSERT_EQUAL(1, dispatcher
.calls
);
96 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
100 * completion dispatchers registered with the event loop are invoked by the
103 * This test works by having a customer dispatcher which shuts the loop down
104 * once its been invoked twice.
106 * It also tests that loop.run() and loop.stop() work, because if they do not
107 * work, this test will either hang, or fail.
110 class ShutdownDispatcher
: public CompletionDispatcher
116 ShutdownDispatcher(EventLoop
& theLoop
):theLoop(theLoop
), calls(0) {}
127 testEventLoop::testRegisterDispatcher()
130 ShutdownDispatcher
testDispatcher(theLoop
);
131 theLoop
.registerDispatcher(&testDispatcher
);
133 /* we should get two calls because the test dispatched returns true from
134 * dispatch(), and calls stop on the second call.
136 CPPUNIT_ASSERT_EQUAL(2, testDispatcher
.calls
);
139 /* test that a registered async engine is invoked on each loop run
140 * we do this with an intstrumented async engine.
143 testEventLoop::testRegisterEngine()
146 ShutdownDispatcher
testDispatcher(theLoop
);
147 theLoop
.registerDispatcher(&testDispatcher
);
148 RecordingEngine testEngine
;
149 theLoop
.registerEngine(&testEngine
);
151 CPPUNIT_ASSERT_EQUAL(2, testEngine
.calls
);
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 do not 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.
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.
165 testEventLoop::testEngineTimeout()
168 RecordingEngine
engineOne(5);
169 RecordingEngine engineTwo
;
170 theLoop
.registerEngine(&engineOne
);
171 theLoop
.registerEngine(&engineTwo
);
173 CPPUNIT_ASSERT_EQUAL(0, engineOne
.lasttimeout
);
174 CPPUNIT_ASSERT_EQUAL(5, engineTwo
.lasttimeout
);
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.
182 testEventLoop::testStopOnIdle()
185 /* trivial case - no dispatchers or engines, should quit immediately */
186 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
188 /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its
189 * sufficient and handy
191 EventDispatcher dispatcher
;
192 theLoop
.registerDispatcher(&dispatcher
);
193 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
195 /* add an engine which is idle.
197 RecordingEngine
engine(AsyncEngine::EVENT_IDLE
);
198 theLoop
.registerEngine(&engine
);
199 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
200 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
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
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
);
211 /* run resets the error count ... */
212 CPPUNIT_ASSERT_EQUAL(11, failing_engine
.calls
);
214 /* an engine that asks for a timeout should not be detected as idle:
215 * use runOnce which should return false
217 theLoop
= EventLoop();
218 RecordingEngine
non_idle_engine(1000);
219 theLoop
.registerEngine(&non_idle_engine
);
220 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
223 #endif /* POLISHED_MAIN_LOOP */
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.
229 class StubTime
: public TimeEngine
233 StubTime() : calls(0) {}
242 testEventLoop::testSetTimeService()
246 /* the loop will not error without a time service */
248 /* we can set the time service */
249 theLoop
.setTimeService(&myTime
);
250 /* it invokes our tick() call */
252 CPPUNIT_ASSERT_EQUAL(1, myTime
.calls
);
253 /* it invokes our tick() call again */
255 CPPUNIT_ASSERT_EQUAL(2, myTime
.calls
);
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
262 testEventLoop::testSetPrimaryEngine()
265 RecordingEngine
first_engine(10);
266 RecordingEngine
second_engine(10);
267 /* one engine - gets a timeout */
268 theLoop
.registerEngine(&first_engine
);
270 CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT
, first_engine
.lasttimeout
);
271 /* two engines - the second gets the timeout */
272 theLoop
.registerEngine(&second_engine
);
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
);
279 CPPUNIT_ASSERT_EQUAL(10, first_engine
.lasttimeout
);
280 CPPUNIT_ASSERT_EQUAL(0, second_engine
.lasttimeout
);