From: Thomas Markwalder Date: Mon, 28 Oct 2019 17:50:10 +0000 (-0400) Subject: [#964,!577] Added unit tests X-Git-Tag: Kea-1.7.2~81 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d0682c2e324098c33b9437d34fbba2ec1079a3cd;p=thirdparty%2Fkea.git [#964,!577] Added unit tests src/lib/dhcp/tests/iface_mgr_unittest.cc Modified tests to verify that external socket callbacks receive the correct socket descriptor when invoked by IfaceMgr. src/lib/http/tests/server_client_unittests.cc TEST_F(HttpClientTest, closeIfOutOfBandwidth) - new test to verifies HttpClient::closeIfOutOfBandwidth() --- diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index bcb3552248..b542a2e055 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -695,14 +695,18 @@ public: int pipefd[2]; EXPECT_TRUE(pipe(pipefd) == 0); EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], - [&callback_ok](int /* fd */){ callback_ok = true; })); + [&callback_ok, &pipefd](int fd) { + callback_ok = (pipefd[0] == fd); + })); // Let's create a second pipe and register it as well int secondpipe[2]; EXPECT_TRUE(pipe(secondpipe) == 0); EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], - [&callback2_ok](int /* fd */){ callback2_ok = true; })); + [&callback2_ok, &secondpipe](int fd) { + callback2_ok = (secondpipe[0] == fd); + })); // Verify a call with no data and normal external sockets works ok. Pkt4Ptr pkt4; @@ -778,14 +782,18 @@ public: int pipefd[2]; EXPECT_TRUE(pipe(pipefd) == 0); EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], - [&callback_ok](int /* fd*/){ callback_ok = true; })); + [&callback_ok, &pipefd](int fd) { + callback_ok = (pipefd[0] == fd); + })); // Let's create a second pipe and register it as well int secondpipe[2]; EXPECT_TRUE(pipe(secondpipe) == 0); EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], - [&callback2_ok](int /*fd */){ callback2_ok = true; })); + [&callback2_ok, &secondpipe](int fd) { + callback2_ok = (secondpipe[0] == fd); + })); // Verify a call with no data and normal external sockets works ok. Pkt6Ptr pkt6; diff --git a/src/lib/http/tests/server_client_unittests.cc b/src/lib/http/tests/server_client_unittests.cc index dd846eb131..7db256f2ea 100644 --- a/src/lib/http/tests/server_client_unittests.cc +++ b/src/lib/http/tests/server_client_unittests.cc @@ -1402,6 +1402,7 @@ public: if (++resp_num > 1) { io_service_.stop(); } + EXPECT_FALSE(ec); }, HttpClient::RequestTimeout(10000), @@ -1458,6 +1459,140 @@ public: EXPECT_EQ(-1, monitor.registered_fd_); } + /// @brief Tests detection and handling out-of-bandwidth socket events + /// + /// It initiates a transacation and verifies that a mid-transacation call + /// to HttpClient::closeIfOutOfBandwidth() has no affect on the connection. + /// After succesful completion of the transaction, a second call is made + /// HttpClient::closeIfOutOfBandwidth(). This should result in the connection + /// being closed. + /// This step is repeated to verify that after an OOB closure, transactions + /// to the same destination can be processed. + /// + /// Lastly, we verify that HttpClient::stop() closes the connection correctly. + /// + /// @param version HTTP version to be used. + void testCloseIfOutOfBandwidth(const HttpVersion& version) { + // Start the server. + ASSERT_NO_THROW(listener_.start()); + + // Create a client and specify the URL on which the server can be reached. + HttpClient client(io_service_); + Url url("http://127.0.0.1:18123"); + + // Initiate request to the server. + PostHttpRequestJsonPtr request1 = createRequest("sequence", 1, version); + HttpResponseJsonPtr response1(new HttpResponseJson()); + unsigned resp_num = 0; + ExternalMonitor monitor; + + ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, + [this, &client, &resp_num, &monitor](const boost::system::error_code& ec, + const HttpResponsePtr&, + const std::string&) { + if (++resp_num == 1) { + io_service_.stop(); + } + + EXPECT_EQ(1, monitor.connect_cnt_); // We should have 1 connect. + EXPECT_EQ(0, monitor.close_cnt_); // We should have 0 closes + ASSERT_GT(monitor.registered_fd_, -1); // We should have a valid fd. + int orig_fd = monitor.registered_fd_; + + // Test our socket for OOBness. + client.closeIfOutOfBandwidth(monitor.registered_fd_); + + // Since we're in a transaction, we should have no closes and + // the same valid fd. + EXPECT_EQ(0, monitor.close_cnt_); + ASSERT_EQ(monitor.registered_fd_, orig_fd); + + EXPECT_FALSE(ec); + }, + HttpClient::RequestTimeout(10000), + boost::bind(&ExternalMonitor::connectHandler, &monitor, _1, _2), + boost::bind(&ExternalMonitor::closeHandler, &monitor, _1) + )); + + // Actually trigger the requests. The requests should be handlded by the + // server one after another. While the first request is being processed + // the server should queue another one. + ASSERT_NO_THROW(runIOService()); + + // Make sure that we received a response. + ASSERT_TRUE(response1); + ConstElementPtr sequence1 = response1->getJsonElement("sequence"); + ASSERT_TRUE(sequence1); + EXPECT_EQ(1, sequence1->intValue()); + + // We should have had 1 connect invocations, no closes + // and a valid registered fd + EXPECT_EQ(1, monitor.connect_cnt_); + EXPECT_EQ(0, monitor.close_cnt_); + EXPECT_GT(monitor.registered_fd_, -1); + + // Test our socket for OOBness. + client.closeIfOutOfBandwidth(monitor.registered_fd_); + + // Since we're in a transaction, we should have no closes and + // the same valid fd. + EXPECT_EQ(1, monitor.close_cnt_); + EXPECT_EQ(-1, monitor.registered_fd_); + + // Now let's do another request to the destination to verify that + // we'll reopen the connection without issue. + PostHttpRequestJsonPtr request2 = createRequest("sequence", 2, version); + HttpResponseJsonPtr response2(new HttpResponseJson()); + resp_num = 0; + ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2, + [this, &client, &resp_num, &monitor](const boost::system::error_code& ec, + const HttpResponsePtr&, + const std::string&) { + if (++resp_num == 1) { + io_service_.stop(); + } + + EXPECT_EQ(2, monitor.connect_cnt_); // We should have 1 connect. + EXPECT_EQ(1, monitor.close_cnt_); // We should have 0 closes + ASSERT_GT(monitor.registered_fd_, -1); // We should have a valid fd. + int orig_fd = monitor.registered_fd_; + + // Test our socket for OOBness. + client.closeIfOutOfBandwidth(monitor.registered_fd_); + + // Since we're in a transaction, we should have no closes and + // the same valid fd. + EXPECT_EQ(1, monitor.close_cnt_); + ASSERT_EQ(monitor.registered_fd_, orig_fd); + + EXPECT_FALSE(ec); + }, + HttpClient::RequestTimeout(10000), + boost::bind(&ExternalMonitor::connectHandler, &monitor, _1, _2), + boost::bind(&ExternalMonitor::closeHandler, &monitor, _1) + )); + + // Actually trigger the requests. The requests should be handlded by the + // server one after another. While the first request is being processed + // the server should queue another one. + ASSERT_NO_THROW(runIOService()); + + // Make sure that we received the second response. + ASSERT_TRUE(response2); + ConstElementPtr sequence2 = response2->getJsonElement("sequence"); + ASSERT_TRUE(sequence2); + EXPECT_EQ(2, sequence2->intValue()); + + // Stopping the client the close the connection. + client.stop(); + + // We should have had 2 connect invocations, 2 closes + // and an invalid registered fd + EXPECT_EQ(2, monitor.connect_cnt_); + EXPECT_EQ(2, monitor.close_cnt_); + EXPECT_EQ(-1, monitor.registered_fd_); + } + /// @brief Simulates external registery of Connection TCP sockets /// /// Provides methods compatible with Connection callbacks for connnect @@ -1827,4 +1962,9 @@ TEST_F(HttpClientTest, connectCloseCallbacks) { ASSERT_NO_FATAL_FAILURE(testConnectCloseCallbacks(HttpVersion(1, 1))); } +/// Tests that HttpClient::closeIfOutOfBandwidth works correctly. +TEST_F(HttpClientTest, closeIfOutOfBandwidth) { + ASSERT_NO_FATAL_FAILURE(testCloseIfOutOfBandwidth(HttpVersion(1, 1))); +} + }