]>
Commit | Line | Data |
---|---|---|
4e0938ef | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
4e0938ef AJ |
3 | * |
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. | |
7 | */ | |
8 | ||
f7f3304a | 9 | #include "squid.h" |
e1f7507e | 10 | |
a553a5a3 | 11 | #include <cppunit/TestAssert.h> |
12 | ||
314782d4 | 13 | #include "AsyncEngine.h" |
a553a5a3 | 14 | #include "EventLoop.h" |
ed6e9fb9 | 15 | #include "mem/forward.h" |
314782d4 | 16 | #include "SquidTime.h" |
6ea5959e | 17 | #include "stat.h" |
602d9612 | 18 | #include "testEventLoop.h" |
7f861c77 AJ |
19 | #include "unitTestMain.h" |
20 | ||
a553a5a3 | 21 | CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop ); |
22 | ||
a553a5a3 | 23 | /* init legacy static-initialized modules */ |
24 | ||
16555581 | 25 | void |
26 | testEventLoop::setUp() | |
a553a5a3 | 27 | { |
16555581 | 28 | Mem::Init(); |
29 | statInit(); | |
30 | } | |
a553a5a3 | 31 | |
32 | /* | |
33 | * Test creating a EventLoop | |
34 | */ | |
35 | void | |
36 | testEventLoop::testCreate() | |
37 | { | |
38 | EventLoop(); | |
39 | } | |
40 | ||
cb9b9424 | 41 | #if POLISHED_MAIN_LOOP |
a553a5a3 | 42 | |
43 | /* | |
26ac0430 | 44 | * Running the loop once is useful for integration with other loops, such as |
a553a5a3 | 45 | * migrating to it in incrementally. |
46 | * | |
8ff3fa2e | 47 | * This test works by having a custom dispatcher and engine which record how |
48 | * many times they are called. | |
a553a5a3 | 49 | */ |
50 | ||
51 | class RecordDispatcher : public CompletionDispatcher | |
52 | { | |
53 | ||
54 | public: | |
55 | int calls; | |
26ac0430 | 56 | RecordDispatcher(): calls(0) {} |
a553a5a3 | 57 | |
26ac0430 | 58 | bool dispatch() { |
8ff3fa2e | 59 | ++calls; |
60 | /* claim we dispatched calls to be useful for the testStopOnIdle test. | |
61 | */ | |
62 | return true; | |
63 | } | |
a553a5a3 | 64 | }; |
65 | ||
cb9b9424 | 66 | #endif /* POLISHED_MAIN_LOOP */ |
67 | ||
8ff3fa2e | 68 | class RecordingEngine : public AsyncEngine |
69 | { | |
70 | ||
71 | public: | |
72 | int calls; | |
73 | int lasttimeout; | |
74 | int return_timeout; | |
9dca980d | 75 | RecordingEngine(int aTimeout=0): calls(0), lasttimeout(0), return_timeout(aTimeout) {} |
8ff3fa2e | 76 | |
26ac0430 AJ |
77 | virtual int checkEvents(int timeout) { |
78 | ++calls; | |
79 | lasttimeout = timeout; | |
80 | return return_timeout; | |
81 | } | |
82 | }; | |
8ff3fa2e | 83 | |
cb9b9424 | 84 | #if POLISHED_MAIN_LOOP |
85 | ||
a553a5a3 | 86 | void |
87 | testEventLoop::testRunOnce() | |
88 | { | |
89 | EventLoop theLoop; | |
90 | RecordDispatcher dispatcher; | |
91 | theLoop.registerDispatcher(&dispatcher); | |
8ff3fa2e | 92 | RecordingEngine engine; |
93 | theLoop.registerEngine(&engine); | |
a553a5a3 | 94 | theLoop.runOnce(); |
95 | CPPUNIT_ASSERT_EQUAL(1, dispatcher.calls); | |
8ff3fa2e | 96 | CPPUNIT_ASSERT_EQUAL(1, engine.calls); |
a553a5a3 | 97 | } |
98 | ||
99 | /* | |
8ff3fa2e | 100 | * completion dispatchers registered with the event loop are invoked by the |
101 | * event loop. | |
a553a5a3 | 102 | * |
8ff3fa2e | 103 | * This test works by having a customer dispatcher which shuts the loop down |
104 | * once its been invoked twice. | |
a553a5a3 | 105 | * |
8ff3fa2e | 106 | * It also tests that loop.run() and loop.stop() work, because if they dont |
107 | * work, this test will either hang, or fail. | |
a553a5a3 | 108 | */ |
109 | ||
110 | class ShutdownDispatcher : public CompletionDispatcher | |
111 | { | |
112 | ||
113 | public: | |
114 | EventLoop &theLoop; | |
115 | int calls; | |
26ac0430 | 116 | ShutdownDispatcher(EventLoop & theLoop):theLoop(theLoop), calls(0) {} |
a553a5a3 | 117 | |
26ac0430 | 118 | bool dispatch() { |
8ff3fa2e | 119 | if (++calls == 2) |
120 | theLoop.stop(); | |
121 | ||
122 | return true; | |
123 | } | |
a553a5a3 | 124 | }; |
125 | ||
126 | void | |
127 | testEventLoop::testRegisterDispatcher() | |
128 | { | |
129 | EventLoop theLoop; | |
130 | ShutdownDispatcher testDispatcher(theLoop); | |
131 | theLoop.registerDispatcher(&testDispatcher); | |
132 | theLoop.run(); | |
8ff3fa2e | 133 | /* we should get two calls because the test dispatched returns true from |
134 | * dispatch(), and calls stop on the second call. | |
135 | */ | |
a553a5a3 | 136 | CPPUNIT_ASSERT_EQUAL(2, testDispatcher.calls); |
137 | } | |
8ff3fa2e | 138 | |
139 | /* test that a registered async engine is invoked on each loop run | |
140 | * we do this with an intstrumented async engine. | |
141 | */ | |
142 | void | |
143 | testEventLoop::testRegisterEngine() | |
144 | { | |
145 | EventLoop theLoop; | |
146 | ShutdownDispatcher testDispatcher(theLoop); | |
147 | theLoop.registerDispatcher(&testDispatcher); | |
148 | RecordingEngine testEngine; | |
149 | theLoop.registerEngine(&testEngine); | |
150 | theLoop.run(); | |
151 | CPPUNIT_ASSERT_EQUAL(2, testEngine.calls); | |
152 | } | |
153 | ||
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 dont 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. | |
160 | * | |
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. | |
163 | */ | |
164 | void | |
165 | testEventLoop::testEngineTimeout() | |
166 | { | |
167 | EventLoop theLoop; | |
168 | RecordingEngine engineOne(5); | |
169 | RecordingEngine engineTwo; | |
170 | theLoop.registerEngine(&engineOne); | |
171 | theLoop.registerEngine(&engineTwo); | |
172 | theLoop.runOnce(); | |
173 | CPPUNIT_ASSERT_EQUAL(0, engineOne.lasttimeout); | |
174 | CPPUNIT_ASSERT_EQUAL(5, engineTwo.lasttimeout); | |
175 | } | |
176 | ||
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. | |
180 | */ | |
181 | void | |
182 | testEventLoop::testStopOnIdle() | |
183 | { | |
184 | EventLoop theLoop; | |
185 | /* trivial case - no dispatchers or engines, should quit immediately */ | |
186 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
187 | theLoop.run(); | |
188 | /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its | |
189 | * sufficient and handy | |
190 | */ | |
191 | EventDispatcher dispatcher; | |
192 | theLoop.registerDispatcher(&dispatcher); | |
193 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
194 | theLoop.run(); | |
195 | /* add an engine which is idle. | |
196 | */ | |
197 | RecordingEngine engine(AsyncEngine::EVENT_IDLE); | |
198 | theLoop.registerEngine(&engine); | |
199 | CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); | |
200 | CPPUNIT_ASSERT_EQUAL(1, engine.calls); | |
201 | theLoop.run(); | |
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 | |
205 | */ | |
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); | |
210 | theLoop.run(); | |
211 | /* run resets the error count ... */ | |
212 | CPPUNIT_ASSERT_EQUAL(11, failing_engine.calls); | |
bef81ea5 | 213 | |
214 | /* an engine that asks for a timeout should not be detected as idle: | |
215 | * use runOnce which should return false | |
216 | */ | |
217 | theLoop = EventLoop(); | |
218 | RecordingEngine non_idle_engine(1000); | |
219 | theLoop.registerEngine(&non_idle_engine); | |
220 | CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce()); | |
8ff3fa2e | 221 | } |
222 | ||
cb9b9424 | 223 | #endif /* POLISHED_MAIN_LOOP */ |
224 | ||
8ff3fa2e | 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. | |
227 | */ | |
228 | ||
229 | class StubTime : public TimeEngine | |
230 | { | |
231 | ||
232 | public: | |
43d8f31d | 233 | StubTime() : calls(0) {} |
8ff3fa2e | 234 | |
235 | int calls; | |
26ac0430 | 236 | void tick() { |
8ff3fa2e | 237 | ++calls; |
238 | } | |
239 | }; | |
240 | ||
241 | void | |
242 | testEventLoop::testSetTimeService() | |
243 | { | |
244 | EventLoop theLoop; | |
245 | StubTime myTime; | |
246 | /* the loop will not error without a time service */ | |
247 | theLoop.runOnce(); | |
248 | /* we can set the time service */ | |
249 | theLoop.setTimeService(&myTime); | |
250 | /* it invokes our tick() call */ | |
251 | theLoop.runOnce(); | |
252 | CPPUNIT_ASSERT_EQUAL(1, myTime.calls); | |
253 | /* it invokes our tick() call again */ | |
254 | theLoop.runOnce(); | |
255 | CPPUNIT_ASSERT_EQUAL(2, myTime.calls); | |
256 | } | |
bef81ea5 | 257 | |
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 | |
260 | */ | |
261 | void | |
262 | testEventLoop::testSetPrimaryEngine() | |
263 | { | |
264 | EventLoop theLoop; | |
265 | RecordingEngine first_engine(10); | |
266 | RecordingEngine second_engine(10); | |
267 | /* one engine - gets a timeout */ | |
268 | theLoop.registerEngine(&first_engine); | |
269 | theLoop.runOnce(); | |
6289f91f | 270 | CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, first_engine.lasttimeout); |
bef81ea5 | 271 | /* two engines - the second gets the timeout */ |
272 | theLoop.registerEngine(&second_engine); | |
273 | theLoop.runOnce(); | |
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); | |
278 | theLoop.runOnce(); | |
279 | CPPUNIT_ASSERT_EQUAL(10, first_engine.lasttimeout); | |
280 | CPPUNIT_ASSERT_EQUAL(0, second_engine.lasttimeout); | |
bef81ea5 | 281 | } |
f53969cc | 282 |