proxygen
ConnectionManagerTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
17 
18 #include <gtest/gtest.h>
19 #include <gmock/gmock.h>
20 #include <gflags/gflags.h>
21 
22 using namespace folly;
23 using namespace testing;
24 using namespace wangle;
25 
26 namespace {
27 
28 class ConnectionManagerTest;
29 
30 class MockConnection : public ManagedConnection {
31  public:
32  using UniquePtr = std::unique_ptr<StrictMock<MockConnection>,
34 
35  static UniquePtr makeUnique(ConnectionManagerTest* test) {
36  UniquePtr p(new StrictMock<MockConnection>(test));
37  return p;
38  }
39 
40  explicit MockConnection(ConnectionManagerTest *test)
41  : test_(test) {
42  EXPECT_CALL(*this, isBusy())
43  .WillRepeatedly(Return(false));
44  EXPECT_CALL(*this, dumpConnectionState(testing::_))
45  .WillRepeatedly(Return());
46  ON_CALL(*this, closeWhenIdle())
47  .WillByDefault(Invoke([this] {
48  closeWhenIdle_ = true;
49  closeWhenIdleImpl();
50  }));
51  }
52 
53  MOCK_METHOD0(timeoutExpired_, void());
54  void timeoutExpired() noexcept override {
55  timeoutExpired_();
56  }
57 
58  MOCK_CONST_METHOD1(describe, void(std::ostream& os));
59 
60  MOCK_CONST_METHOD0(isBusy, bool());
61 
62  MOCK_CONST_METHOD0(getIdleTime, std::chrono::milliseconds());
63 
64  MOCK_METHOD0(notifyPendingShutdown, void());
65  MOCK_METHOD0(closeWhenIdle, void());
66  MOCK_METHOD0(dropConnection, void());
67  MOCK_METHOD1(dumpConnectionState, void(uint8_t));
68  MOCK_METHOD2(drainConnections, void(double, std::chrono::milliseconds));
69 
70  void setIdle(bool idle) {
71  idle_ = idle;
72  closeWhenIdleImpl();
73  }
74 
75  void closeWhenIdleImpl();
76 
77  ConnectionManagerTest* test_{nullptr};
78  bool idle_{false};
79  bool closeWhenIdle_{false};
80 };
81 
82 class ConnectionManagerTest: public testing::Test {
83 
84  public:
85  ConnectionManagerTest() {
86  cm_= ConnectionManager::makeUnique(&eventBase_,
87  std::chrono::milliseconds(100),
88  nullptr);
89  addConns(65);
90  }
91 
92  void SetUp() override {
93  }
94 
95  void addConns(uint64_t n) {
96  for (size_t i = 0; i < n; i++) {
97  conns_.insert(conns_.begin(), MockConnection::makeUnique(this));
98  cm_->addConnection(conns_.front().get());
99  }
100  }
101 
102  void removeConn(MockConnection* connection) {
103  for (auto& conn : conns_) {
104  if (conn.get() == connection) {
105  cm_->removeConnection(connection);
106  conn.reset();
107  }
108  }
109  }
110 
111  protected:
112  void testAddDuringCloseWhenIdle(bool deactivate);
113 
114  folly::EventBase eventBase_;
116  std::vector<MockConnection::UniquePtr> conns_;
117 };
118 
119 void MockConnection::closeWhenIdleImpl() {
120  if (idle_ && closeWhenIdle_) {
121  test_->removeConn(this);
122  }
123 }
124 
125 TEST_F(ConnectionManagerTest, testShutdownSequence) {
126  InSequence enforceOrder;
127 
128  // activate one connection, it should not be exempt from notifyPendingShutdown
129  cm_->onActivated(*conns_.front());
130  // make sure the idleIterator points to !end
131  cm_->onDeactivated(*conns_.back());
132  for (const auto& conn : conns_) {
133  EXPECT_CALL(*conn, notifyPendingShutdown());
134  }
135  cm_->initiateGracefulShutdown(std::chrono::milliseconds(50));
136  eventBase_.loopOnce();
137  for (const auto& conn : conns_) {
138  EXPECT_CALL(*conn, closeWhenIdle());
139  }
140 
141  eventBase_.loop();
142 }
143 
144 TEST_F(ConnectionManagerTest, testRemoveDrainIterator) {
145  addConns(1);
146  InSequence enforceOrder;
147 
148  // activate one connection, it should not be exempt from notifyPendingShutdown
149  cm_->onActivated(*conns_.front());
150  for (size_t i = 0; i < conns_.size() - 1; i++) {
151  EXPECT_CALL(*conns_[i], notifyPendingShutdown());
152  }
153  auto conn65 = conns_[conns_.size() - 2].get();
154  auto conn66 = conns_[conns_.size() - 1].get();
155  eventBase_.runInLoop([&] {
156  // deactivate the drain iterator
157  cm_->onDeactivated(*conn65);
158  // remove the drain iterator
159  cm_->removeConnection(conn66);
160  // deactivate the new drain iterator, now it's the end of the list
161  cm_->onDeactivated(*conn65);
162  });
163  cm_->initiateGracefulShutdown(std::chrono::milliseconds(50));
164  // Schedule a loop callback to remove the connection pointed to by the drain
165  // iterator
166  eventBase_.loopOnce();
167  for (size_t i = 0; i < conns_.size() - 1; i++) {
168  EXPECT_CALL(*conns_[i], closeWhenIdle());
169  }
170 
171  eventBase_.loop();
172 }
173 
174 TEST_F(ConnectionManagerTest, testIdleGraceTimeout) {
175  InSequence enforceOrder;
176 
177  // Slow down the notifyPendingShutdown calls enough so that the idle grace
178  // timeout fires before the end of the loop.
179  // I would prefer a non-sleep solution to this, but I can't think how to do it
180  // without changing the class to expose internal details
181  for (const auto& conn : conns_) {
182  EXPECT_CALL(*conn, notifyPendingShutdown())
183  .WillOnce(Invoke([] { /* sleep override */ usleep(1000); }));
184  }
185  cm_->initiateGracefulShutdown(std::chrono::milliseconds(1));
186  eventBase_.loopOnce();
187  for (const auto& conn : conns_) {
188  EXPECT_CALL(*conn, closeWhenIdle());
189  }
190 
191  eventBase_.loop();
192 }
193 
194 TEST_F(ConnectionManagerTest, testDropAll) {
195  InSequence enforceOrder;
196 
197  for (const auto& conn : conns_) {
198  EXPECT_CALL(*conn, dropConnection())
199  .WillOnce(Invoke([&] { cm_->removeConnection(conn.get()); }));
200  }
201  cm_->dropAllConnections();
202 }
203 
204 TEST_F(ConnectionManagerTest, testDropPercent) {
205  InSequence enforceOrder;
206 
207  // Make sure we have exactly 100 connections.
208  const int numToAdd = 100 - conns_.size();
209  addConns(numToAdd);
210  const int numToRemove = conns_.size() - 100;
211  for (int i = 0; i < numToRemove; i++) {
212  removeConn(conns_.begin()->get());
213  }
214  EXPECT_EQ(100, cm_->getNumConnections());
215 
216  // Drop 20% of connections.
217  double pct = 0.2;
218  int numToDrop = 100 * pct;
219  auto connIter = conns_.begin();
220  while (connIter != conns_.end() && numToDrop > 0) {
221  EXPECT_CALL(*(*connIter), dropConnection());
222  --numToDrop;
223  ++connIter;
224  }
225  cm_->dropConnections(pct);
226 
227  // Make sure they are gone.
228  EXPECT_EQ(0, numToDrop);
229  EXPECT_EQ(80, cm_->getNumConnections());
230 
231  // Then drop 50% of the remaining 80 connections.
232  pct = 0.5;
233  numToDrop = 80 * pct;
234  while (connIter != conns_.end() && numToDrop > 0) {
235  EXPECT_CALL(*(*connIter), dropConnection());
236  --numToDrop;
237  ++connIter;
238  }
239  cm_->dropConnections(pct);
240 
241  // Make sure those are gone as well.
242  EXPECT_EQ(0, numToDrop);
243  EXPECT_EQ(40, cm_->getNumConnections());
244 }
245 
246 TEST_F(ConnectionManagerTest, testDrainPercent) {
247  InSequence enforceOrder;
248  double drain_percentage = .123;
249 
250  for (size_t i = 58 /* tail .123 of all conns */; i < conns_.size(); ++i) {
251  EXPECT_CALL(*conns_[i], notifyPendingShutdown());
252  }
253 
254  cm_->drainConnections(drain_percentage, std::chrono::milliseconds(50));
255 
256  for (size_t i = 58; i < conns_.size(); ++i) {
257  EXPECT_CALL(*conns_[i], closeWhenIdle());
258  }
259 
260  eventBase_.loop();
261 }
262 
263 TEST_F(ConnectionManagerTest, testDrainPctAfterAll) {
264  InSequence enforceOrder;
265  double drain_percentage = 0.1;
266 
267  for (const auto& conn : conns_) {
268  EXPECT_CALL(*conn, notifyPendingShutdown());
269  }
270 
271  cm_->initiateGracefulShutdown(std::chrono::milliseconds(50));
272  cm_->drainConnections(drain_percentage, std::chrono::milliseconds(50));
273  eventBase_.loopOnce();
274 
275  for (const auto& conn : conns_) {
276  EXPECT_CALL(*conn, closeWhenIdle());
277  }
278 
279  eventBase_.loop();
280 }
281 
282 TEST_F(ConnectionManagerTest, testDrainAllAfterPct) {
283  InSequence enforceOrder;
284  double drain_pct = 0.8;
285 
286  for (auto i = conns_.size() - static_cast<int>(conns_.size() * drain_pct);
287  i < conns_.size(); ++i) {
288  EXPECT_CALL(*conns_[i], notifyPendingShutdown());
289  }
290 
291  cm_->drainConnections(drain_pct, std::chrono::milliseconds(50));
292 
293  for (size_t i = 0;
294  i < conns_.size() - static_cast<size_t>(conns_.size() * drain_pct); ++i) {
295  EXPECT_CALL(*conns_[i], notifyPendingShutdown());
296  }
297 
298  cm_->initiateGracefulShutdown(std::chrono::milliseconds(50));
299  eventBase_.loopOnce();
300 
301  for (const auto& conn : conns_) {
302  EXPECT_CALL(*conn, closeWhenIdle());
303  }
304 
305  eventBase_.loop();
306 }
307 
308 TEST_F(ConnectionManagerTest, testDropIdle) {
309  for (const auto& conn : conns_) {
310  // Set everyone to be idle for 100ms
311  EXPECT_CALL(*conn, getIdleTime())
312  .WillRepeatedly(Return(std::chrono::milliseconds(100)));
313  }
314 
315  // Mark the first half of the connections idle
316  for (size_t i = 0; i < conns_.size() / 2; i++) {
317  cm_->onDeactivated(*conns_[i]);
318  }
319  // reactivate conn 0
320  cm_->onActivated(*conns_[0]);
321  // remove the first idle conn
322  cm_->removeConnection(conns_[1].get());
323 
324  InSequence enforceOrder;
325 
326  // Expect the remaining idle conns to drop
327  for (size_t i = 2; i < conns_.size() / 2; i++) {
328  EXPECT_CALL(*conns_[i], dropConnection())
329  .WillOnce(Invoke([this, i] { cm_->removeConnection(conns_[i].get()); }));
330  }
331 
332  cm_->dropIdleConnections(conns_.size());
333 }
334 
335 TEST_F(ConnectionManagerTest, testAddDuringShutdown) {
336  auto extraConn = MockConnection::makeUnique(this);
337  InSequence enforceOrder;
338 
339  // activate one connection, it should not be exempt from notifyPendingShutdown
340  cm_->onActivated(*conns_.front());
341  for (const auto& conn : conns_) {
342  EXPECT_CALL(*conn, notifyPendingShutdown());
343  }
344  cm_->initiateGracefulShutdown(std::chrono::milliseconds(50));
345  eventBase_.loopOnce();
346  conns_.insert(conns_.begin(), std::move(extraConn));
347  EXPECT_CALL(*conns_.front(), notifyPendingShutdown());
348  cm_->addConnection(conns_.front().get());
349  for (const auto& conn : conns_) {
350  EXPECT_CALL(*conn, closeWhenIdle());
351  }
352 
353  eventBase_.loop();
354 }
355 
356 TEST_F(ConnectionManagerTest, testAddDuringShutdownWithoutIdleGrace) {
357  auto extraConn = MockConnection::makeUnique(this);
358  InSequence enforceOrder;
359 
360  cm_->onActivated(*conns_.front());
361  for (const auto& conn : conns_) {
362  EXPECT_CALL(*conn, closeWhenIdle());
363  }
364  cm_->initiateGracefulShutdown(std::chrono::milliseconds(0));
365  eventBase_.loopOnce();
366 
367  conns_.insert(conns_.begin(), std::move(extraConn));
368  EXPECT_CALL(*conns_.front().get(), closeWhenIdle());
369  cm_->addConnection(conns_.front().get());
370  eventBase_.loop();
371 }
372 
373 void ConnectionManagerTest::testAddDuringCloseWhenIdle(bool deactivate) {
374  auto extraConn = MockConnection::makeUnique(this);
375  InSequence enforceOrder;
376 
377  // All conns will get closeWhenIdle
378  for (const auto& conn : conns_) {
379  conn->setIdle(true);
380  EXPECT_CALL(*conn, closeWhenIdle());
381  }
382  cm_->initiateGracefulShutdown(std::chrono::milliseconds(0));
383  // Add the extra conn in this state
384  extraConn->setIdle(true);
385  conns_.insert(conns_.begin(), std::move(extraConn));
386  cm_->addConnection(conns_.front().get());
387  // Shouldn't be deleted yet, call is delayed
388  ASSERT_TRUE(conns_.front().get() != nullptr);
389 
390  // Mark the connection as active
391  conns_.front()->setIdle(false);
392  if (deactivate) {
393  // mark it idle and move to the end of the list. The regular
394  // drainAllConnections code will find it and call closeWhenIdle. The
395  // second loop callback won't find the conn and be a no-op
396  cm_->onDeactivated(*conns_.front());
397  conns_.front()->setIdle(true);
398  }
399  EXPECT_CALL(*conns_.front(), closeWhenIdle());
400  eventBase_.loop();
401  if (!deactivate) {
402  // drainAllConnections didn't find it, closeWhenIdle was invoked by the
403  // second loop callback.
404  cm_->onDeactivated(*conns_.front());
405  conns_.front()->setIdle(true);
406  }
407  ASSERT_TRUE(conns_.front().get() == nullptr);
408 }
409 
410 TEST_F(ConnectionManagerTest, testAddDuringCloseWhenIdleActive) {
411  testAddDuringCloseWhenIdle(false);
412 }
413 
414 TEST_F(ConnectionManagerTest, testAddDuringCloseWhenIdleInactive) {
415  testAddDuringCloseWhenIdle(true);
416 }
417 }
#define MOCK_CONST_METHOD1(m,...)
struct @64 makeUnique
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
std::unique_ptr< ConnectionManager, Destructor > UniquePtr
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
PolymorphicAction< internal::InvokeAction< FunctionImpl > > Invoke(FunctionImpl function_impl)
TEST_F(AsyncSSLSocketWriteTest, write_coalescing1)
#define ON_CALL(obj, call)
#define MOCK_METHOD1(m,...)
#define MOCK_CONST_METHOD0(m,...)
#define EXPECT_CALL(obj, call)
const internal::AnythingMatcher _
#define MOCK_METHOD2(m,...)
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865
internal::ReturnAction< R > Return(R value)
#define MOCK_METHOD0(m,...)