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