1 #define SQUID_UNIT_TEST 1
4 #include <cppunit/TestAssert.h>
6 #include "testEventLoop.h"
11 #include "AsyncEngine.h"
12 #include "base/AsyncCallQueue.h"
16 CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop
);
18 /* stub functions to link successfully */
29 /* init legacy static-initialized modules */
32 testEventLoop::setUp()
39 * Test creating a EventLoop
42 testEventLoop::testCreate()
47 #if POLISHED_MAIN_LOOP
50 * Running the loop once is useful for integration with other loops, such as
51 * migrating to it in incrementally.
53 * This test works by having a custom dispatcher and engine which record how
54 * many times they are called.
57 class RecordDispatcher
: public CompletionDispatcher
62 RecordDispatcher(): calls(0) {}
66 /* claim we dispatched calls to be useful for the testStopOnIdle test.
72 #endif /* POLISHED_MAIN_LOOP */
74 class RecordingEngine
: public AsyncEngine
81 RecordingEngine(int return_timeout
=0): calls(0), lasttimeout(0),
82 return_timeout(return_timeout
) {}
84 virtual int checkEvents(int timeout
) {
86 lasttimeout
= timeout
;
87 return return_timeout
;
91 #if POLISHED_MAIN_LOOP
94 testEventLoop::testRunOnce()
97 RecordDispatcher dispatcher
;
98 theLoop
.registerDispatcher(&dispatcher
);
99 RecordingEngine engine
;
100 theLoop
.registerEngine(&engine
);
102 CPPUNIT_ASSERT_EQUAL(1, dispatcher
.calls
);
103 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
107 * completion dispatchers registered with the event loop are invoked by the
110 * This test works by having a customer dispatcher which shuts the loop down
111 * once its been invoked twice.
113 * It also tests that loop.run() and loop.stop() work, because if they dont
114 * work, this test will either hang, or fail.
117 class ShutdownDispatcher
: public CompletionDispatcher
123 ShutdownDispatcher(EventLoop
& theLoop
):theLoop(theLoop
), calls(0) {}
134 testEventLoop::testRegisterDispatcher()
137 ShutdownDispatcher
testDispatcher(theLoop
);
138 theLoop
.registerDispatcher(&testDispatcher
);
140 /* we should get two calls because the test dispatched returns true from
141 * dispatch(), and calls stop on the second call.
143 CPPUNIT_ASSERT_EQUAL(2, testDispatcher
.calls
);
146 /* test that a registered async engine is invoked on each loop run
147 * we do this with an intstrumented async engine.
150 testEventLoop::testRegisterEngine()
153 ShutdownDispatcher
testDispatcher(theLoop
);
154 theLoop
.registerDispatcher(&testDispatcher
);
155 RecordingEngine testEngine
;
156 theLoop
.registerEngine(&testEngine
);
158 CPPUNIT_ASSERT_EQUAL(2, testEngine
.calls
);
161 /* each AsyncEngine needs to be given a timeout. We want one engine in each
162 * loop to be given the timeout value - and the rest to have a timeout of 0.
163 * The last registered engine should be given this timeout, which will mean
164 * that we dont block in the loop until the last engine. This will allow for
165 * dynamic introduction and removal of engines, as long as the last engine
166 * is one which can do a os call rather than busy waiting.
168 * So - we want the timeout hints returned from the earlier engines to be
169 * tracked, and the lowest non-negative value given to the last engine.
172 testEventLoop::testEngineTimeout()
175 RecordingEngine
engineOne(5);
176 RecordingEngine engineTwo
;
177 theLoop
.registerEngine(&engineOne
);
178 theLoop
.registerEngine(&engineTwo
);
180 CPPUNIT_ASSERT_EQUAL(0, engineOne
.lasttimeout
);
181 CPPUNIT_ASSERT_EQUAL(5, engineTwo
.lasttimeout
);
184 /* An event loop with all idle engines, and nothing dispatched in a run should
185 * automatically quit. The runOnce call should return True when the loop is
186 * entirely idle to make it easy for people running the loop by hand.
189 testEventLoop::testStopOnIdle()
192 /* trivial case - no dispatchers or engines, should quit immediately */
193 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
195 /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its
196 * sufficient and handy
198 EventDispatcher dispatcher
;
199 theLoop
.registerDispatcher(&dispatcher
);
200 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
202 /* add an engine which is idle.
204 RecordingEngine
engine(AsyncEngine::EVENT_IDLE
);
205 theLoop
.registerEngine(&engine
);
206 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
207 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
209 CPPUNIT_ASSERT_EQUAL(2, engine
.calls
);
210 /* add an engine which is suffering errors. This should result in 10
211 * loops until the loop stops - because thats the error retry amount
213 RecordingEngine
failing_engine(AsyncEngine::EVENT_ERROR
);
214 theLoop
.registerEngine(&failing_engine
);
215 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
216 CPPUNIT_ASSERT_EQUAL(1, failing_engine
.calls
);
218 /* run resets the error count ... */
219 CPPUNIT_ASSERT_EQUAL(11, failing_engine
.calls
);
221 /* an engine that asks for a timeout should not be detected as idle:
222 * use runOnce which should return false
224 theLoop
= EventLoop();
225 RecordingEngine
non_idle_engine(1000);
226 theLoop
.registerEngine(&non_idle_engine
);
227 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
230 #endif /* POLISHED_MAIN_LOOP */
232 /* An event loop has a time service which is like an async engine but never
233 * generates events and there can only be one such service.
236 class StubTime
: public TimeEngine
240 StubTime() : calls(0) {}
249 testEventLoop::testSetTimeService()
253 /* the loop will not error without a time service */
255 /* we can set the time service */
256 theLoop
.setTimeService(&myTime
);
257 /* it invokes our tick() call */
259 CPPUNIT_ASSERT_EQUAL(1, myTime
.calls
);
260 /* it invokes our tick() call again */
262 CPPUNIT_ASSERT_EQUAL(2, myTime
.calls
);
265 /* one async engine is the primary engine - the engine that is allowed to block.
266 * this defaults to the last added one, but can be explicitly nominated
269 testEventLoop::testSetPrimaryEngine()
272 RecordingEngine
first_engine(10);
273 RecordingEngine
second_engine(10);
274 /* one engine - gets a timeout */
275 theLoop
.registerEngine(&first_engine
);
277 CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT
, first_engine
.lasttimeout
);
278 /* two engines - the second gets the timeout */
279 theLoop
.registerEngine(&second_engine
);
281 CPPUNIT_ASSERT_EQUAL(0, first_engine
.lasttimeout
);
282 CPPUNIT_ASSERT_EQUAL(10, second_engine
.lasttimeout
);
283 /* set the first engine to be primary explicitly and now gets the timeout */
284 theLoop
.setPrimaryEngine(&first_engine
);
286 CPPUNIT_ASSERT_EQUAL(10, first_engine
.lasttimeout
);
287 CPPUNIT_ASSERT_EQUAL(0, second_engine
.lasttimeout
);