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 /* init legacy static-initialized modules */
21 testEventLoop::setUp()
28 * Test creating a EventLoop
31 testEventLoop::testCreate()
36 #if POLISHED_MAIN_LOOP
39 * Running the loop once is useful for integration with other loops, such as
40 * migrating to it in incrementally.
42 * This test works by having a custom dispatcher and engine which record how
43 * many times they are called.
46 class RecordDispatcher
: public CompletionDispatcher
51 RecordDispatcher(): calls(0) {}
55 /* claim we dispatched calls to be useful for the testStopOnIdle test.
61 #endif /* POLISHED_MAIN_LOOP */
63 class RecordingEngine
: public AsyncEngine
70 RecordingEngine(int return_timeout
=0): calls(0), lasttimeout(0),
71 return_timeout(return_timeout
) {}
73 virtual int checkEvents(int timeout
) {
75 lasttimeout
= timeout
;
76 return return_timeout
;
80 #if POLISHED_MAIN_LOOP
83 testEventLoop::testRunOnce()
86 RecordDispatcher dispatcher
;
87 theLoop
.registerDispatcher(&dispatcher
);
88 RecordingEngine engine
;
89 theLoop
.registerEngine(&engine
);
91 CPPUNIT_ASSERT_EQUAL(1, dispatcher
.calls
);
92 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
96 * completion dispatchers registered with the event loop are invoked by the
99 * This test works by having a customer dispatcher which shuts the loop down
100 * once its been invoked twice.
102 * It also tests that loop.run() and loop.stop() work, because if they dont
103 * work, this test will either hang, or fail.
106 class ShutdownDispatcher
: public CompletionDispatcher
112 ShutdownDispatcher(EventLoop
& theLoop
):theLoop(theLoop
), calls(0) {}
123 testEventLoop::testRegisterDispatcher()
126 ShutdownDispatcher
testDispatcher(theLoop
);
127 theLoop
.registerDispatcher(&testDispatcher
);
129 /* we should get two calls because the test dispatched returns true from
130 * dispatch(), and calls stop on the second call.
132 CPPUNIT_ASSERT_EQUAL(2, testDispatcher
.calls
);
135 /* test that a registered async engine is invoked on each loop run
136 * we do this with an intstrumented async engine.
139 testEventLoop::testRegisterEngine()
142 ShutdownDispatcher
testDispatcher(theLoop
);
143 theLoop
.registerDispatcher(&testDispatcher
);
144 RecordingEngine testEngine
;
145 theLoop
.registerEngine(&testEngine
);
147 CPPUNIT_ASSERT_EQUAL(2, testEngine
.calls
);
150 /* each AsyncEngine needs to be given a timeout. We want one engine in each
151 * loop to be given the timeout value - and the rest to have a timeout of 0.
152 * The last registered engine should be given this timeout, which will mean
153 * that we dont block in the loop until the last engine. This will allow for
154 * dynamic introduction and removal of engines, as long as the last engine
155 * is one which can do a os call rather than busy waiting.
157 * So - we want the timeout hints returned from the earlier engines to be
158 * tracked, and the lowest non-negative value given to the last engine.
161 testEventLoop::testEngineTimeout()
164 RecordingEngine
engineOne(5);
165 RecordingEngine engineTwo
;
166 theLoop
.registerEngine(&engineOne
);
167 theLoop
.registerEngine(&engineTwo
);
169 CPPUNIT_ASSERT_EQUAL(0, engineOne
.lasttimeout
);
170 CPPUNIT_ASSERT_EQUAL(5, engineTwo
.lasttimeout
);
173 /* An event loop with all idle engines, and nothing dispatched in a run should
174 * automatically quit. The runOnce call should return True when the loop is
175 * entirely idle to make it easy for people running the loop by hand.
178 testEventLoop::testStopOnIdle()
181 /* trivial case - no dispatchers or engines, should quit immediately */
182 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
184 /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its
185 * sufficient and handy
187 EventDispatcher dispatcher
;
188 theLoop
.registerDispatcher(&dispatcher
);
189 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
191 /* add an engine which is idle.
193 RecordingEngine
engine(AsyncEngine::EVENT_IDLE
);
194 theLoop
.registerEngine(&engine
);
195 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
196 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
198 CPPUNIT_ASSERT_EQUAL(2, engine
.calls
);
199 /* add an engine which is suffering errors. This should result in 10
200 * loops until the loop stops - because thats the error retry amount
202 RecordingEngine
failing_engine(AsyncEngine::EVENT_ERROR
);
203 theLoop
.registerEngine(&failing_engine
);
204 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
205 CPPUNIT_ASSERT_EQUAL(1, failing_engine
.calls
);
207 /* run resets the error count ... */
208 CPPUNIT_ASSERT_EQUAL(11, failing_engine
.calls
);
210 /* an engine that asks for a timeout should not be detected as idle:
211 * use runOnce which should return false
213 theLoop
= EventLoop();
214 RecordingEngine
non_idle_engine(1000);
215 theLoop
.registerEngine(&non_idle_engine
);
216 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
219 #endif /* POLISHED_MAIN_LOOP */
221 /* An event loop has a time service which is like an async engine but never
222 * generates events and there can only be one such service.
225 class StubTime
: public TimeEngine
229 StubTime() : calls(0) {}
238 testEventLoop::testSetTimeService()
242 /* the loop will not error without a time service */
244 /* we can set the time service */
245 theLoop
.setTimeService(&myTime
);
246 /* it invokes our tick() call */
248 CPPUNIT_ASSERT_EQUAL(1, myTime
.calls
);
249 /* it invokes our tick() call again */
251 CPPUNIT_ASSERT_EQUAL(2, myTime
.calls
);
254 /* one async engine is the primary engine - the engine that is allowed to block.
255 * this defaults to the last added one, but can be explicitly nominated
258 testEventLoop::testSetPrimaryEngine()
261 RecordingEngine
first_engine(10);
262 RecordingEngine
second_engine(10);
263 /* one engine - gets a timeout */
264 theLoop
.registerEngine(&first_engine
);
266 CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT
, first_engine
.lasttimeout
);
267 /* two engines - the second gets the timeout */
268 theLoop
.registerEngine(&second_engine
);
270 CPPUNIT_ASSERT_EQUAL(0, first_engine
.lasttimeout
);
271 CPPUNIT_ASSERT_EQUAL(10, second_engine
.lasttimeout
);
272 /* set the first engine to be primary explicitly and now gets the timeout */
273 theLoop
.setPrimaryEngine(&first_engine
);
275 CPPUNIT_ASSERT_EQUAL(10, first_engine
.lasttimeout
);
276 CPPUNIT_ASSERT_EQUAL(0, second_engine
.lasttimeout
);