5 #include <cppunit/TestAssert.h>
7 #include "testEventLoop.h"
12 #include "AsyncEngine.h"
13 #include "AsyncCallQueue.h"
17 CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop
);
19 /* stub functions to link successfully */
30 /* init legacy static-initialized modules */
33 testEventLoop::setUp()
40 * Test creating a EventLoop
43 testEventLoop::testCreate()
48 #if POLISHED_MAIN_LOOP
51 * Running the loop once is useful for integration with other loops, such as
52 * migrating to it in incrementally.
54 * This test works by having a custom dispatcher and engine which record how
55 * many times they are called.
58 class RecordDispatcher
: public CompletionDispatcher
63 RecordDispatcher(): calls(0)
69 /* claim we dispatched calls to be useful for the testStopOnIdle test.
75 #endif /* POLISHED_MAIN_LOOP */
77 class RecordingEngine
: public AsyncEngine
84 RecordingEngine(int return_timeout
=0): calls(0), lasttimeout(0),
85 return_timeout(return_timeout
)
88 virtual int checkEvents(int timeout
)
91 lasttimeout
= timeout
;
92 return return_timeout
;
96 #if POLISHED_MAIN_LOOP
99 testEventLoop::testRunOnce()
102 RecordDispatcher dispatcher
;
103 theLoop
.registerDispatcher(&dispatcher
);
104 RecordingEngine engine
;
105 theLoop
.registerEngine(&engine
);
107 CPPUNIT_ASSERT_EQUAL(1, dispatcher
.calls
);
108 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
112 * completion dispatchers registered with the event loop are invoked by the
115 * This test works by having a customer dispatcher which shuts the loop down
116 * once its been invoked twice.
118 * It also tests that loop.run() and loop.stop() work, because if they dont
119 * work, this test will either hang, or fail.
122 class ShutdownDispatcher
: public CompletionDispatcher
128 ShutdownDispatcher(EventLoop
& theLoop
):theLoop(theLoop
), calls(0)
141 testEventLoop::testRegisterDispatcher()
144 ShutdownDispatcher
testDispatcher(theLoop
);
145 theLoop
.registerDispatcher(&testDispatcher
);
147 /* we should get two calls because the test dispatched returns true from
148 * dispatch(), and calls stop on the second call.
150 CPPUNIT_ASSERT_EQUAL(2, testDispatcher
.calls
);
153 /* test that a registered async engine is invoked on each loop run
154 * we do this with an intstrumented async engine.
157 testEventLoop::testRegisterEngine()
160 ShutdownDispatcher
testDispatcher(theLoop
);
161 theLoop
.registerDispatcher(&testDispatcher
);
162 RecordingEngine testEngine
;
163 theLoop
.registerEngine(&testEngine
);
165 CPPUNIT_ASSERT_EQUAL(2, testEngine
.calls
);
168 /* each AsyncEngine needs to be given a timeout. We want one engine in each
169 * loop to be given the timeout value - and the rest to have a timeout of 0.
170 * The last registered engine should be given this timeout, which will mean
171 * that we dont block in the loop until the last engine. This will allow for
172 * dynamic introduction and removal of engines, as long as the last engine
173 * is one which can do a os call rather than busy waiting.
175 * So - we want the timeout hints returned from the earlier engines to be
176 * tracked, and the lowest non-negative value given to the last engine.
179 testEventLoop::testEngineTimeout()
182 RecordingEngine
engineOne(5);
183 RecordingEngine engineTwo
;
184 theLoop
.registerEngine(&engineOne
);
185 theLoop
.registerEngine(&engineTwo
);
187 CPPUNIT_ASSERT_EQUAL(0, engineOne
.lasttimeout
);
188 CPPUNIT_ASSERT_EQUAL(5, engineTwo
.lasttimeout
);
191 /* An event loop with all idle engines, and nothing dispatched in a run should
192 * automatically quit. The runOnce call should return True when the loop is
193 * entirely idle to make it easy for people running the loop by hand.
196 testEventLoop::testStopOnIdle()
199 /* trivial case - no dispatchers or engines, should quit immediately */
200 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
202 /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its
203 * sufficient and handy
205 EventDispatcher dispatcher
;
206 theLoop
.registerDispatcher(&dispatcher
);
207 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
209 /* add an engine which is idle.
211 RecordingEngine
engine(AsyncEngine::EVENT_IDLE
);
212 theLoop
.registerEngine(&engine
);
213 CPPUNIT_ASSERT_EQUAL(true, theLoop
.runOnce());
214 CPPUNIT_ASSERT_EQUAL(1, engine
.calls
);
216 CPPUNIT_ASSERT_EQUAL(2, engine
.calls
);
217 /* add an engine which is suffering errors. This should result in 10
218 * loops until the loop stops - because thats the error retry amount
220 RecordingEngine
failing_engine(AsyncEngine::EVENT_ERROR
);
221 theLoop
.registerEngine(&failing_engine
);
222 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
223 CPPUNIT_ASSERT_EQUAL(1, failing_engine
.calls
);
225 /* run resets the error count ... */
226 CPPUNIT_ASSERT_EQUAL(11, failing_engine
.calls
);
228 /* an engine that asks for a timeout should not be detected as idle:
229 * use runOnce which should return false
231 theLoop
= EventLoop();
232 RecordingEngine
non_idle_engine(1000);
233 theLoop
.registerEngine(&non_idle_engine
);
234 CPPUNIT_ASSERT_EQUAL(false, theLoop
.runOnce());
237 #endif /* POLISHED_MAIN_LOOP */
239 /* An event loop has a time service which is like an async engine but never
240 * generates events and there can only be one such service.
243 class StubTime
: public TimeEngine
247 StubTime() : calls(0) {}
257 testEventLoop::testSetTimeService()
261 /* the loop will not error without a time service */
263 /* we can set the time service */
264 theLoop
.setTimeService(&myTime
);
265 /* it invokes our tick() call */
267 CPPUNIT_ASSERT_EQUAL(1, myTime
.calls
);
268 /* it invokes our tick() call again */
270 CPPUNIT_ASSERT_EQUAL(2, myTime
.calls
);
273 /* one async engine is the primary engine - the engine that is allowed to block.
274 * this defaults to the last added one, but can be explicitly nominated
277 testEventLoop::testSetPrimaryEngine()
280 RecordingEngine
first_engine(10);
281 RecordingEngine
second_engine(10);
282 /* one engine - gets a timeout */
283 theLoop
.registerEngine(&first_engine
);
285 CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT
, first_engine
.lasttimeout
);
286 /* two engines - the second gets the timeout */
287 theLoop
.registerEngine(&second_engine
);
289 CPPUNIT_ASSERT_EQUAL(0, first_engine
.lasttimeout
);
290 CPPUNIT_ASSERT_EQUAL(10, second_engine
.lasttimeout
);
291 /* set the first engine to be primary explicitly and now gets the timeout */
292 theLoop
.setPrimaryEngine(&first_engine
);
294 CPPUNIT_ASSERT_EQUAL(10, first_engine
.lasttimeout
);
295 CPPUNIT_ASSERT_EQUAL(0, second_engine
.lasttimeout
);