testEventLoop.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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 
9 #include "squid.h"
10 #include "AsyncEngine.h"
11 #include "compat/cppunit.h"
12 #include "EventLoop.h"
13 #include "time/Engine.h"
14 #include "unitTestMain.h"
15 
16 #include <cppunit/TestAssert.h>
17 
18 /*
19  * test the EventLoop implementation
20  */
21 
22 class TestEventLoop : public CPPUNIT_NS::TestFixture
23 {
32 
33 protected:
34  void testCreate();
35  void testRunOnce();
36  void testEngineTimeout();
37  void testEngineErrors();
38  void testSetTimeService();
39  void testSetPrimaryEngine();
40  /* TODO:
41  * test that engine which errors a couple of times, then returns 0, then
42  * errors 10 times in a row triggers a fail on the 10th time around
43  */
44 };
45 
47 
48 void
50 {
51  EventLoop();
52 }
53 
55 {
56 public:
57  RecordingEngine(int aTimeout = 0) : return_timeout(aTimeout) {}
58 
59  int checkEvents(int timeout) override {
60  ++calls;
61  lasttimeout = timeout;
62  return return_timeout;
63  }
64 
65  int calls = 0;
66  int lasttimeout = 0;
67  int return_timeout = 0;
68 };
69 
70 /* test that a registered async engine is invoked on each loop run
71  * we do this with an instrumented async engine.
72  */
73 void
75 {
76  {
77  /* trivial case - no engine, should quit immediately */
78  EventLoop theLoop;
79  CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce());
80  }
81 
82  {
83  /* An event loop with all idle engines, and nothing dispatched in a run should
84  * automatically quit. The runOnce call should return True when the loop is
85  * entirely idle to make it easy for people running the loop by hand.
86  */
87  EventLoop theLoop;
89  theLoop.registerEngine(&engine);
90  CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce());
91  CPPUNIT_ASSERT_EQUAL(1, engine.calls);
92  theLoop.run();
93  CPPUNIT_ASSERT_EQUAL(2, engine.calls);
94  }
95 
96  {
97  /* an engine that asks for a timeout should not be detected as idle:
98  * use runOnce which should return false
99  */
100  EventLoop theLoop;
101  RecordingEngine engine;
102  theLoop.registerEngine(&engine);
103  CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce());
104  CPPUNIT_ASSERT_EQUAL(1, engine.calls);
105  CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, engine.lasttimeout);
106  }
107 }
108 
109 /* each AsyncEngine needs to be given a timeout. We want one engine in each
110  * loop to be given the timeout value - and the rest to have a timeout of 0.
111  * The last registered engine should be given this timeout, which will mean
112  * that we do not block in the loop until the last engine. This will allow for
113  * dynamic introduction and removal of engines, as long as the last engine
114  * is one which can do a os call rather than busy waiting.
115  *
116  * So - we want the timeout hints returned from the earlier engines to be
117  * tracked, and the lowest non-negative value given to the last engine.
118  */
119 void
121 {
122  EventLoop theLoop;
123  RecordingEngine engineOne(5);
124  RecordingEngine engineTwo;
125  theLoop.registerEngine(&engineOne);
126  theLoop.registerEngine(&engineTwo);
127  theLoop.runOnce();
128  CPPUNIT_ASSERT_EQUAL(1, engineOne.calls);
129  CPPUNIT_ASSERT_EQUAL(0, engineOne.lasttimeout);
130  CPPUNIT_ASSERT_EQUAL(1, engineTwo.calls);
131  CPPUNIT_ASSERT_EQUAL(5, engineTwo.lasttimeout);
132 }
133 
134 /* An engine which is suffering errors. This should result in 10
135  * loops until the loop stops - because that's the error retry amount
136  * hard-coded into EventLoop::runOnce()
137  */
138 void
140 {
141  EventLoop theLoop;
143  theLoop.registerEngine(&failing_engine);
144  CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce());
145  CPPUNIT_ASSERT_EQUAL(1, failing_engine.calls);
146  CPPUNIT_ASSERT_EQUAL(1, theLoop.errcount);
147  theLoop.run();
148  /* run resets the error count ... */
149  CPPUNIT_ASSERT_EQUAL(10, theLoop.errcount);
150  CPPUNIT_ASSERT_EQUAL(11, failing_engine.calls);
151 }
152 
153 /* An event loop has a time service which is like an async engine but never
154  * generates events and there can only be one such service.
155  */
156 class StubTime : public Time::Engine
157 {
158 public:
159  StubTime() : calls(0) {}
160 
161  int calls;
162  void tick() override {
163  ++calls;
164  }
165 };
166 
167 void
169 {
170  EventLoop theLoop;
171  StubTime myTime;
172  /* the loop will not error without a time service */
173  theLoop.runOnce();
174  /* we can set the time service */
175  theLoop.setTimeService(&myTime);
176  /* it invokes our tick() call */
177  theLoop.runOnce();
178  CPPUNIT_ASSERT_EQUAL(1, myTime.calls);
179  /* it invokes our tick() call again */
180  theLoop.runOnce();
181  CPPUNIT_ASSERT_EQUAL(2, myTime.calls);
182 }
183 
184 /* one async engine is the primary engine - the engine that is allowed to block.
185  * this defaults to the last added one, but can be explicitly nominated
186  */
187 void
189 {
190  EventLoop theLoop;
191  RecordingEngine first_engine(10);
192  RecordingEngine second_engine(10);
193  /* one engine - gets a timeout */
194  theLoop.registerEngine(&first_engine);
195  theLoop.runOnce();
196  CPPUNIT_ASSERT_EQUAL(EVENT_LOOP_TIMEOUT, first_engine.lasttimeout);
197  /* two engines - the second gets the timeout */
198  theLoop.registerEngine(&second_engine);
199  theLoop.runOnce();
200  CPPUNIT_ASSERT_EQUAL(0, first_engine.lasttimeout);
201  CPPUNIT_ASSERT_EQUAL(10, second_engine.lasttimeout);
202  /* set the first engine to be primary explicitly and now gets the timeout */
203  theLoop.setPrimaryEngine(&first_engine);
204  theLoop.runOnce();
205  CPPUNIT_ASSERT_EQUAL(10, first_engine.lasttimeout);
206  CPPUNIT_ASSERT_EQUAL(0, second_engine.lasttimeout);
207 }
208 
209 int
210 main(int argc, char *argv[])
211 {
212  return TestProgram().run(argc, argv);
213 }
214 
event class for doing synthetic time etc
Definition: Engine.h:15
int main(int argc, char *argv[])
void testEngineErrors()
void run()
Definition: EventLoop.cc:76
implements test program's main() function while enabling customization
Definition: unitTestMain.h:25
CPPUNIT_TEST_SUITE(TestEventLoop)
void testEngineTimeout()
CPPUNIT_TEST(testCreate)
void testRunOnce()
void testSetPrimaryEngine()
int run(int argc, char *argv[])
Definition: unitTestMain.h:44
RecordingEngine(int aTimeout=0)
bool runOnce()
Definition: EventLoop.cc:89
CPPUNIT_TEST_SUITE_REGISTRATION(TestEventLoop)
int errcount
Definition: EventLoop.h:69
void setTimeService(Time::Engine *)
Definition: EventLoop.cc:162
#define EVENT_LOOP_TIMEOUT
Definition: EventLoop.h:16
int checkEvents(int timeout) override
void setPrimaryEngine(AsyncEngine *engine)
Definition: EventLoop.cc:149
void testSetTimeService()
void registerEngine(AsyncEngine *engine)
Definition: EventLoop.cc:70
void tick() override

 

Introduction

Documentation

Support

Miscellaneous