]>
Commit | Line | Data |
---|---|---|
f5bffbd5 | 1 | #include "config.h" |
e1f7507e | 2 | |
a553a5a3 | 3 | #include <cppunit/TestAssert.h> |
4 | ||
a553a5a3 | 5 | #include "testEventLoop.h" |
6 | #include "EventLoop.h" | |
e1f7507e | 7 | #include "Mem.h" |
a553a5a3 | 8 | |
e1f7507e AJ |
9 | #if 0 |
10 | #include "AsyncEngine.h" | |
882255af | 11 | #include "base/AsyncCallQueue.h" |
e1f7507e AJ |
12 | #include "event.h" |
13 | #endif | |
a553a5a3 | 14 | |
15 | CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop ); | |
16 | ||
17 | /* stub functions to link successfully */ | |
18 | void | |
19 | shut_down(int) | |
20 | {} | |
21 | ||
757a2291 GS |
22 | void |
23 | reconfigure(int) | |
24 | {} | |
25 | ||
a553a5a3 | 26 | /* end stubs */ |
27 | ||
28 | /* init legacy static-initialized modules */ | |
29 | ||
16555581 | 30 | void |
31 | testEventLoop::setUp() | |
a553a5a3 | 32 | { |
16555581 | 33 | Mem::Init(); |
34 | statInit(); | |
35 | } | |
a553a5a3 | 36 | |
37 | /* | |
38 | * Test creating a EventLoop | |
39 | */ | |
40 | void | |
41 | testEventLoop::testCreate() | |
42 | { | |
43 | EventLoop(); | |
44 | } | |
45 | ||
cb9b9424 | 46 | #if POLISHED_MAIN_LOOP |
a553a5a3 | 47 | |
48 | /* | |
26ac0430 | 49 | * Running the loop once is useful for integration with other loops, such as |
a553a5a3 | 50 | * migrating to it in incrementally. |
51 | * | |
8ff3fa2e | 52 | * This test works by having a custom dispatcher and engine which record how |
53 | * many times they are called. | |
a553a5a3 | 54 | */ |
55 | ||
56 | class RecordDispatcher : public CompletionDispatcher | |
57 | { | |
58 | ||
59 | public: | |
60 | int calls; | |
26ac0430 | 61 | RecordDispatcher(): calls(0) {} |
a553a5a3 | 62 | |
26ac0430 | 63 | bool dispatch() { |
8ff3fa2e | 64 | ++calls; |
65 | /* claim we dispatched calls to be useful for the testStopOnIdle test. | |
66 | */ | |
67 | return true; | |
68 | } | |
a553a5a3 | 69 | }; |
70 | ||
cb9b9424 | 71 | #endif /* POLISHED_MAIN_LOOP */ |
72 | ||
8ff3fa2e | 73 | class RecordingEngine : public AsyncEngine |
74 | { | |
75 | ||
76 | public: | |
77 | int calls; | |
78 | int lasttimeout; | |
79 | int return_timeout; | |
80 | RecordingEngine(int return_timeout=0): calls(0), lasttimeout(0), | |
26ac0430 | 81 | return_timeout(return_timeout) {} |
8ff3fa2e | 82 | |
26ac0430 AJ |
83 | virtual int checkEvents(int timeout) { |
84 | ++calls; | |
85 | lasttimeout = timeout; | |
86 | return return_timeout; | |
87 | } | |
88 | }; | |
8ff3fa2e | 89 | |
cb9b9424 | 90 | #if POLISHED_MAIN_LOOP |
91 | ||
a553a5a3 | 92 | void |
93 | testEventLoop::testRunOnce() | |
94 | { | |
95 | EventLoop theLoop; | |
96 | RecordDispatcher dispatcher; | |
97 | theLoop.registerDispatcher(&dispatcher); | |
8ff3fa2e | 98 | RecordingEngine engine; |
99 | theLoop.registerEngine(&engine); | |
a553a5a3 | 100 | theLoop.runOnce(); |
101 | CPPUNIT_ASSERT_EQUAL(1, dispatcher.calls); | |
8ff3fa2e | 102 | CPPUNIT_ASSERT_EQUAL(1, engine.calls); |
a553a5a3 | 103 | } |
104 | ||
105 | /* | |
8ff3fa2e | 106 | * completion dispatchers registered with the event loop are invoked by the |
107 | * event loop. | |
a553a5a3 | 108 | * |
8ff3fa2e | 109 | * This test works by having a customer dispatcher which shuts the loop down |
110 | * once its been invoked twice. | |
a553a5a3 | 111 | * |
8ff3fa2e | 112 | * It also tests that loop.run() and loop.stop() work, because if they dont |
113 | * work, this test will either hang, or fail. | |
a553a5a3 | 114 | */ |
115 | ||
116 | class ShutdownDispatcher : public CompletionDispatcher | |
117 | { | |
118 | ||
119 | public: | |
120 | EventLoop &theLoop; | |
121 | int calls; | |
26ac0430 | 122 | ShutdownDispatcher(EventLoop & theLoop):theLoop(theLoop), calls(0) {} |
a553a5a3 | 123 | |
26ac0430 | 124 | bool dispatch() { |
8ff3fa2e | 125 | if (++calls == 2) |
126 | theLoop.stop(); | |
127 | ||
128 | return true; | |
129 | } | |
a553a5a3 | 130 | }; |
131 | ||
132 | void | |
133 | testEventLoop::testRegisterDispatcher() | |
134 | { | |
135 | EventLoop theLoop; | |
136 | ShutdownDispatcher testDispatcher(theLoop); | |
137 | theLoop.registerDispatcher(&testDispatcher); | |
138 | theLoop.run(); | |
8ff3fa2e | 139 | /* we should get two calls because the test dispatched returns true from |
140 | * dispatch(), and calls stop on the second call. | |
141 | */ | |
a553a5a3 | 142 | CPPUNIT_ASSERT_EQUAL(2, testDispatcher.calls); |
143 | } | |
8ff3fa2e | 144 | |
145 | /* test that a registered async engine is invoked on each loop run | |
146 | * we do this with an intstrumented async engine. | |
147 | */ | |
148 | void | |
149 | testEventLoop::testRegisterEngine() | |
150 | { | |
151 | EventLoop theLoop; | |
152 | ShutdownDispatcher testDispatcher(theLoop); | |
153 | theLoop.registerDispatcher(&testDispatcher); | |
154 | RecordingEngine testEngine; | |
155 | theLoop.registerEngine(&testEngine); | |
156 | theLoop.run(); | |
157 | CPPUNIT_ASSERT_EQUAL(2, testEngine.calls); | |
158 | } | |
159 | ||
160 | /* each AsyncEngine needs to be given a timeout. We want one engine in each | |
161 | * loop to be given the timeout value - and the rest to have a timeout of 0. | |
162 | * The last registered engine should be given this timeout, which will mean | |
163 | * that we dont block in the loop until the last engine. This will allow for | |
164 | * dynamic introduction and removal of engines, as long as the last engine | |
165 | * is one which can do a os call rather than busy waiting. | |
166 | * | |
167 | * So - we want the timeout hints returned from the earlier engines to be | |
168 | * tracked, and the lowest non-negative value given to the last engine. | |
169 | */ | |
170 | void | |
171 | testEventLoop::testEngineTimeout() | |
172 | { | |
173 | EventLoop theLoop; | |
174 | RecordingEngine engineOne(5); | |
175 | RecordingEngine engineTwo; | |
176 | theLoop.registerEngine(&engineOne); | |
177 | theLoop.registerEngine(&engineTwo); | |
178 | theLoop.runOnce(); | |
179 | CPPUNIT_ASSERT_EQUAL(0, engineOne.lasttimeout); | |
180 | CPPUNIT_ASSERT_EQUAL(5, engineTwo.lasttimeout); | |
181 | } | |
182 | ||
183 | /* An event loop with all idle engines, and nothing dispatched in a run should | |
184 | * automatically quit. The runOnce call should return True when the loop is | |
185 | * entirely idle to make it easy for people running the loop by hand. | |
186 | */ | |
187 | void | |
188 | testEventLoop::testStopOnIdle() | |
189 | { | |
190 | EventLoop theLoop; | |
191 | /* trivial case - no dispatchers or engines, should quit immediately */ | |
192 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
193 | theLoop.run(); | |
194 | /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its | |
195 | * sufficient and handy | |
196 | */ | |
197 | EventDispatcher dispatcher; | |
198 | theLoop.registerDispatcher(&dispatcher); | |
199 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
200 | theLoop.run(); | |
201 | /* add an engine which is idle. | |
202 | */ | |
203 | RecordingEngine engine(AsyncEngine::EVENT_IDLE); | |
204 | theLoop.registerEngine(&engine); | |
205 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
206 | CPPUNIT_ASSERT_EQUAL(1, engine.calls); | |
207 | theLoop.run(); | |
208 | CPPUNIT_ASSERT_EQUAL(2, engine.calls); | |
209 | /* add an engine which is suffering errors. This should result in 10 | |
210 | * loops until the loop stops - because thats the error retry amount | |
211 | */ | |
212 | RecordingEngine failing_engine(AsyncEngine::EVENT_ERROR); | |
213 | theLoop.registerEngine(&failing_engine); | |
214 | CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce()); | |
215 | CPPUNIT_ASSERT_EQUAL(1, failing_engine.calls); | |
216 | theLoop.run(); | |
217 | /* run resets the error count ... */ | |
218 | CPPUNIT_ASSERT_EQUAL(11, failing_engine.calls); | |
bef81ea5 | 219 | |
220 | /* an engine that asks for a timeout should not be detected as idle: | |
221 | * use runOnce which should return false | |
222 | */ | |
223 | theLoop = EventLoop(); | |
224 | RecordingEngine non_idle_engine(1000); | |
225 | theLoop.registerEngine(&non_idle_engine); | |
226 | CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce()); | |
8ff3fa2e | 227 | } |
228 | ||
cb9b9424 | 229 | #endif /* POLISHED_MAIN_LOOP */ |
230 | ||
8ff3fa2e | 231 | /* An event loop has a time service which is like an async engine but never |
232 | * generates events and there can only be one such service. | |
233 | */ | |
234 | ||
235 | class StubTime : public TimeEngine | |
236 | { | |
237 | ||
238 | public: | |
43d8f31d | 239 | StubTime() : calls(0) {} |
8ff3fa2e | 240 | |
241 | int calls; | |
26ac0430 | 242 | void tick() { |
8ff3fa2e | 243 | ++calls; |
244 | } | |
245 | }; | |
246 | ||
247 | void | |
248 | testEventLoop::testSetTimeService() | |
249 | { | |
250 | EventLoop theLoop; | |
251 | StubTime myTime; | |
252 | /* the loop will not error without a time service */ | |
253 | theLoop.runOnce(); | |
254 | /* we can set the time service */ | |
255 | theLoop.setTimeService(&myTime); | |
256 | /* it invokes our tick() call */ | |
257 | theLoop.runOnce(); | |
258 | CPPUNIT_ASSERT_EQUAL(1, myTime.calls); | |
259 | /* it invokes our tick() call again */ | |
260 | theLoop.runOnce(); | |
261 | CPPUNIT_ASSERT_EQUAL(2, myTime.calls); | |
262 | } | |
bef81ea5 | 263 | |
264 | /* one async engine is the primary engine - the engine that is allowed to block. | |
265 | * this defaults to the last added one, but can be explicitly nominated | |
266 | */ | |
267 | void | |
268 | testEventLoop::testSetPrimaryEngine() | |
269 | { | |
270 | EventLoop theLoop; | |
271 | RecordingEngine first_engine(10); | |
272 | RecordingEngine second_engine(10); | |
273 | /* one engine - gets a timeout */ | |
274 | theLoop.registerEngine(&first_engine); | |
275 | theLoop.runOnce(); | |
6289f91f | 276 | CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, first_engine.lasttimeout); |
bef81ea5 | 277 | /* two engines - the second gets the timeout */ |
278 | theLoop.registerEngine(&second_engine); | |
279 | theLoop.runOnce(); | |
280 | CPPUNIT_ASSERT_EQUAL(0, first_engine.lasttimeout); | |
281 | CPPUNIT_ASSERT_EQUAL(10, second_engine.lasttimeout); | |
282 | /* set the first engine to be primary explicitly and now gets the timeout */ | |
283 | theLoop.setPrimaryEngine(&first_engine); | |
284 | theLoop.runOnce(); | |
285 | CPPUNIT_ASSERT_EQUAL(10, first_engine.lasttimeout); | |
286 | CPPUNIT_ASSERT_EQUAL(0, second_engine.lasttimeout); | |
bef81ea5 | 287 | } |