]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2089] added d2 stats unittests
authorRazvan Becheriu <razvan@isc.org>
Tue, 5 Oct 2021 17:51:47 +0000 (20:51 +0300)
committerRazvan Becheriu <razvan@isc.org>
Fri, 8 Oct 2021 11:47:19 +0000 (11:47 +0000)
src/lib/d2srv/tests/dns_client_unittests.cc

index a5e39c15d7b42a52e0e743316448f1d7a62aa266..36ba16e49a1fb5b5ef4c04a1295e097596828bbe 100644 (file)
@@ -58,32 +58,58 @@ const long TEST_TIMEOUT = 5 * 1000;
 // timeout is hit. This will result in test failure.
 class DNSClientTest : public virtual D2StatTest, DNSClient::Callback {
 public:
+    /// @brief The IOService which handles IO operations.
     IOService service_;
+
+    /// @brief The UDP socket.
+    std::unique_ptr<udp::socket> socket_;
+
+    /// @brief The UDP socket endpoint.
+    std::unique_ptr<udp::endpoint> endpoint_;
+
+    /// @brief DNS client response.
     D2UpdateMessagePtr response_;
+
+    /// @brief The status of the DNS client update callback.
     DNSClient::Status status_;
+
+    /// @brief The receive buffer.
     uint8_t receive_buffer_[MAX_SIZE];
+
+    /// @brief The DNS client performing DNS update.
     DNSClientPtr dns_client_;
+
+    /// @brief The flag which indicates if the response should be corrupted.
     bool corrupt_response_;
+
+    /// @brief The flag which indicates if a response is expected.
     bool expect_response_;
+
+    /// @brief The timeout timer.
     asiolink::IntervalTimer test_timer_;
+
+    /// @brief The number of received DNS updates.
     int received_;
+
+    /// @brief The number of expected DNS updates.
     int expected_;
 
+    /// @brief The flag which indicates if the server should continue
+    /// with receiving DNS updates.
+    bool go_on_;
+
     /// @brief Constructor
-    //
-    // This constructor overrides the default logging level of asiodns logger to
-    // prevent it from emitting debug messages from IOFetch class. Such an error
-    // message can be emitted if timeout occurs when DNSClient class is
-    // waiting for a response. Some of the tests are checking DNSClient behavior
-    // in case when response from the server is not received. Tests output would
-    // become messy if such errors were logged.
-    DNSClientTest()
-        : service_(),
-          status_(DNSClient::SUCCESS),
-          corrupt_response_(false),
-          expect_response_(true),
-          test_timer_(service_),
-          received_(0), expected_(0) {
+    ///
+    /// This constructor overrides the default logging level of asiodns logger to
+    /// prevent it from emitting debug messages from IOFetch class. Such an error
+    /// message can be emitted if timeout occurs when DNSClient class is
+    /// waiting for a response. Some of the tests are checking DNSClient behavior
+    /// in case when response from the server is not received. Tests output would
+    /// become messy if such errors were logged.
+    DNSClientTest() : service_(), socket_(), endpoint_(),
+                      status_(DNSClient::SUCCESS), corrupt_response_(false),
+                      expect_response_(true), test_timer_(service_),
+                      received_(0), expected_(0), go_on_(false) {
         asiodns::logger.setSeverity(isc::log::INFO);
         response_.reset();
         dns_client_.reset(new DNSClient(response_, this));
@@ -94,22 +120,21 @@ public:
     }
 
     /// @brief Destructor
-    //
-    // Sets the asiodns logging level back to DEBUG.
+    ///
+    /// Sets the asiodns logging level back to DEBUG.
     virtual ~DNSClientTest() {
         asiodns::logger.setSeverity(isc::log::DEBUG);
     };
 
     /// @brief Exchange completion callback
-    //
-    // This callback is called when the exchange with the DNS server is
-    // complete or an error occurred. This includes the occurrence of a timeout.
-    //
-    // @param status A status code returned by DNSClient.
+    ///
+    /// This callback is called when the exchange with the DNS server is
+    /// complete or an error occurred. This includes the occurrence of a timeout.
+    ///
+    /// @param status A status code returned by DNSClient.
     virtual void operator()(DNSClient::Status status) {
         status_ = status;
-        if (!expected_ || (expected_ == ++received_))
-        {
+        if (!expected_ || (expected_ == ++received_)) {
             service_.stop();
         }
 
@@ -139,27 +164,27 @@ public:
     }
 
     /// @brief Handler invoked when test timeout is hit
-    //
-    // This callback stops all running (hanging) tasks on IO service.
+    ///
+    /// This callback stops all running (hanging) tasks on IO service.
     void testTimeoutHandler() {
         service_.stop();
         FAIL() << "Test timeout hit.";
     }
 
     /// @brief Handler invoked when test request is received
-    //
-    // This callback handler is installed when performing async read on a
-    // socket to emulate reception of the DNS Update request by a server.
-    // As a result, this handler will send an appropriate DNS Update response
-    // message back to the address from which the request has come.
-    //
-    // @param socket A pointer to a socket used to receive a query and send a
-    // response.
-    // @param remote A pointer to an object which specifies the host (address
-    // and port) from which a request has come.
-    // @param receive_length A length (in bytes) of the received data.
-    // @param corrupt_response A bool value which indicates that the server's
-    // response should be invalid (true) or valid (false)
+    ///
+    /// This callback handler is installed when performing async read on a
+    /// socket to emulate reception of the DNS Update request by a server.
+    /// As a result, this handler will send an appropriate DNS Update response
+    /// message back to the address from which the request has come.
+    ///
+    /// @param socket A pointer to a socket used to receive a query and send a
+    /// response.
+    /// @param remote A pointer to an object which specifies the host (address
+    /// and port) from which a request has come.
+    /// @param receive_length A length (in bytes) of the received data.
+    /// @param corrupt_response A bool value which indicates that the server's
+    /// response should be invalid (true) or valid (false)
     void udpReceiveHandler(udp::socket* socket, udp::endpoint* remote,
                            size_t receive_length, const bool corrupt_response) {
         // The easiest way to create a response message is to copy the entire
@@ -183,31 +208,39 @@ public:
         socket->send_to(boost::asio::buffer(response_buf.getData(),
                                             response_buf.getLength()),
                         *remote);
+
+        if (go_on_) {
+            socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+                                                            sizeof(receive_buffer_)),
+                                        *endpoint_,
+                                        std::bind(&DNSClientTest::udpReceiveHandler,
+                                                  this, socket_.get(),
+                                                  endpoint_.get(), ph::_2,
+                                                  corrupt_response));
+        }
     }
 
     /// @brief Request handler for testing clients using TSIG
-    //
-    // This callback handler is installed when performing async read on a
-    // socket to emulate reception of the DNS Update request with TSIG by a
-    // server.  As a result, this handler will send an appropriate DNS Update
-    // response message back to the address from which the request has come.
-    //
-    // @param socket A pointer to a socket used to receive a query and send a
-    // response.
-    // @param remote A pointer to an object which specifies the host (address
-    // and port) from which a request has come.
-    // @param receive_length A length (in bytes) of the received data.
-    // @param corrupt_response A bool value which indicates that the server's
-    // response should be invalid (true) or valid (false)
-    // @param client_key TSIG key the server should use to verify the inbound
-    // request.  If the pointer is NULL, the server will not attempt to
-    // verify the request.
-    // @param server_key TSIG key the server should use to sign the outbound
-    // request. If the pointer is NULL, the server will not sign the outbound
-    // response.  If the pointer is not NULL and not the same value as the
-    // client_key, the server will use a new context to sign the response then
-    // the one used to verify it.  This allows us to simulate the server
-    // signing with the wrong key.
+    ///
+    /// This callback handler is installed when performing async read on a
+    /// socket to emulate reception of the DNS Update request with TSIG by a
+    /// server.  As a result, this handler will send an appropriate DNS Update
+    /// response message back to the address from which the request has come.
+    ///
+    /// @param socket A pointer to a socket used to receive a query and send a
+    /// response.
+    /// @param remote A pointer to an object which specifies the host (address
+    /// and port) from which a request has come.
+    /// @param receive_length A length (in bytes) of the received data.
+    /// @param client_key TSIG key the server should use to verify the inbound
+    /// request.  If the pointer is NULL, the server will not attempt to
+    /// verify the request.
+    /// @param server_key TSIG key the server should use to sign the outbound
+    /// request. If the pointer is NULL, the server will not sign the outbound
+    /// response.  If the pointer is not NULL and not the same value as the
+    /// client_key, the server will use a new context to sign the response then
+    /// the one used to verify it.  This allows us to simulate the server
+    /// signing with the wrong key.
     void TSIGReceiveHandler(udp::socket* socket, udp::endpoint* remote,
                             size_t receive_length,
                             D2TsigKeyPtr client_key,
@@ -255,14 +288,15 @@ public:
 
         response.toWire(renderer, context.get());
         // A response message is now ready to send. Send it!
-        socket->send_to(boost::asio::buffer(renderer.getData(), renderer.getLength()),
+        socket->send_to(boost::asio::buffer(renderer.getData(),
+                        renderer.getLength()),
                         *remote);
     }
 
-    // This test verifies that when invalid response placeholder object is
-    // passed to a constructor, constructor throws the appropriate exception.
-    // It also verifies that the constructor will not throw if the supplied
-    // callback object is NULL.
+    /// @brief This test verifies that when invalid response placeholder object
+    /// is passed to a constructor which throws the appropriate exception.
+    /// It also verifies that the constructor will not throw if the supplied
+    /// callback object is NULL.
     void runConstructorTest() {
         EXPECT_NO_THROW(DNSClient(response_, NULL, DNSClient::UDP));
 
@@ -273,8 +307,8 @@ public:
                      isc::NotImplemented);
     }
 
-    // This test verifies that it accepted timeout values belong to the range of
-    // <0, DNSClient::getMaxTimeout()>.
+    /// @brief This test verifies that it accepted timeout values belong to the
+    /// range of <0, DNSClient::getMaxTimeout()>.
     void runInvalidTimeoutTest() {
 
         expect_response_ = false;
@@ -299,9 +333,9 @@ public:
                      isc::BadValue);
     }
 
-    // This test verifies the DNSClient behavior when a server does not respond
-    // do the DNS Update message. In such case, the callback function is
-    // expected to be called and the TIME_OUT error code should be returned.
+    /// @brief This test verifies the DNSClient behavior when a server does not
+    /// respond do the DNS Update message. In such case, the callback function
+    /// is expected to be called and the TIME_OUT error code should be returned.
     void runSendNoReceiveTest() {
         // We expect no response from a server.
         expect_response_ = false;
@@ -329,7 +363,7 @@ public:
         // completion callback will be triggered. The doUpdate function returns
         // immediately.
         EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
-                                             TEST_PORT, message, timeout));
+                                              TEST_PORT, message, timeout));
 
         // This starts the execution of tasks posted to IOService. run() blocks
         // until stop() is called in the completion callback function.
@@ -337,10 +371,11 @@ public:
 
     }
 
-    // This test verifies that DNSClient can send DNS Update and receive a
-    // corresponding response from a server.
+    /// @brief This test verifies that DNSClient can send DNS Update and receive
+    /// a corresponding response from a server.
     void runSendReceiveTest(const bool corrupt_response,
                             const bool two_sends) {
+        go_on_ = two_sends;
         corrupt_response_ = corrupt_response;
 
         // Create a request DNS Update message.
@@ -357,10 +392,11 @@ public:
         // responses. The reuse address option is set so as both sockets can
         // use the same address. This new socket is bound to the test address
         // and port, where requests will be sent.
-        udp::socket udp_socket(service_.get_io_service(), boost::asio::ip::udp::v4());
-        udp_socket.set_option(socket_base::reuse_address(true));
-        udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
-                                      TEST_PORT));
+        socket_.reset(new udp::socket(service_.get_io_service(),
+                                      boost::asio::ip::udp::v4()));
+        socket_->set_option(socket_base::reuse_address(true));
+        socket_->bind(udp::endpoint(address::from_string(TEST_ADDRESS),
+                                    TEST_PORT));
         // Once socket is created, we can post an IO request to receive some
         // packet from this socket. This is asynchronous operation and
         // nothing is received until another IO request to send a query is
@@ -373,13 +409,14 @@ public:
         // Callback function will send a response to this address and port.
         // The last parameter holds a length of the received request. It is
         // required to construct a response.
-        udp::endpoint remote;
-        udp_socket.async_receive_from(boost::asio::buffer(receive_buffer_,
-                                                   sizeof(receive_buffer_)),
-                                      remote,
-                                      std::bind(&DNSClientTest::udpReceiveHandler,
-                                                this, &udp_socket, &remote, ph::_2,
-                                                corrupt_response));
+        endpoint_.reset(new udp::endpoint());
+        socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+                                                        sizeof(receive_buffer_)),
+                                    *endpoint_,
+                                    std::bind(&DNSClientTest::udpReceiveHandler,
+                                              this, socket_.get(),
+                                              endpoint_.get(), ph::_2,
+                                              corrupt_response));
 
         // The socket is now ready to receive the data. Let's post some request
         // message then. Set timeout to some reasonable value to make sure that
@@ -402,7 +439,7 @@ public:
         // "send" and "receive" operations.
         service_.run();
 
-        udp_socket.close();
+        socket_->close();
 
         // Since the callback, operator(), calls stop() on the io_service,
         // we must reset it in order for subsequent calls to run() or
@@ -410,14 +447,13 @@ public:
         service_.get_io_service().reset();
     }
 
-    // Performs a single request-response exchange with or without TSIG
-    //
-    // @param client_key TSIG passed to dns_client and also used by the
-    // "server" to verify the request.
-    // request.
-    // @param server_key TSIG key the "server" should use to sign the response.
-    // If this is NULL, then client_key is used.
-    // @param should_pass indicates if the test should pass.
+    /// @brief Performs a single request-response exchange with or without TSIG.
+    ///
+    /// @param client_key TSIG passed to dns_client and also used by the
+    /// "server" to verify the request.
+    /// @param server_key TSIG key the "server" should use to sign the response.
+    /// If this is NULL, then client_key is used.
+    /// @param should_pass indicates if the test should pass.
     void runTSIGTest(D2TsigKeyPtr client_key, D2TsigKeyPtr server_key,
                      bool should_pass = true) {
         // Tell operator() method if we expect an invalid response.
@@ -468,6 +504,15 @@ public:
 // valid. Constructor should throw exceptions when parameters are invalid.
 TEST_F(DNSClientTest, constructor) {
     runConstructorTest();
+    StatMap stats_upd = {
+        { "update-sent", 0},
+        { "update-signed", 0},
+        { "update-unsigned", 0},
+        { "update-success", 0},
+        { "update-timeout", 0},
+        { "update-error", 0}
+    };
+    checkStats(stats_upd);
 }
 
 // This test verifies that the maximal allowed timeout value is maximal int
@@ -479,6 +524,15 @@ TEST_F(DNSClientTest, getMaxTimeout) {
 // Verify that timeout is reported when no response is received from DNS.
 TEST_F(DNSClientTest, timeout) {
     runSendNoReceiveTest();
+    StatMap stats_upd = {
+        { "update-sent", 1},
+        { "update-signed", 0},
+        { "update-unsigned", 1},
+        { "update-success", 0},
+        { "update-timeout", 1},
+        { "update-error", 0}
+    };
+    checkStats(stats_upd);
 }
 
 // Verify that exception is thrown when invalid (too high) timeout value is
@@ -553,6 +607,15 @@ TEST_F(DNSClientTest, runTSIGTest) {
 TEST_F(DNSClientTest, sendReceive) {
     // false means that server response is not corrupted.
     runSendReceiveTest(false, false);
+    StatMap stats_upd = {
+        { "update-sent", 1},
+        { "update-signed", 0},
+        { "update-unsigned", 1},
+        { "update-success", 1},
+        { "update-timeout", 0},
+        { "update-error", 0}
+    };
+    checkStats(stats_upd);
 }
 
 // Verify that the DNSClient reports an error when the response is received from
@@ -560,6 +623,15 @@ TEST_F(DNSClientTest, sendReceive) {
 TEST_F(DNSClientTest, sendReceiveCorrupted) {
     // true means that server's response is corrupted.
     runSendReceiveTest(true, false);
+    StatMap stats_upd = {
+        { "update-sent", 1},
+        { "update-signed", 0},
+        { "update-unsigned", 1},
+        { "update-success", 0},
+        { "update-timeout", 0},
+        { "update-error", 1}
+    };
+    checkStats(stats_upd);
 }
 
 // Verify that it is possible to use the same DNSClient instance to
@@ -572,6 +644,15 @@ TEST_F(DNSClientTest, sendReceiveTwice) {
     runSendReceiveTest(false, false);
     runSendReceiveTest(false, false);
     EXPECT_EQ(2, received_);
+    StatMap stats_upd = {
+        { "update-sent", 2},
+        { "update-signed", 0},
+        { "update-unsigned", 2},
+        { "update-success", 2},
+        { "update-timeout", 0},
+        { "update-error", 0}
+    };
+    checkStats(stats_upd);
 }
 
 // Verify that it is possible to use the DNSClient instance to perform the
@@ -580,14 +661,17 @@ TEST_F(DNSClientTest, sendReceiveTwice) {
 // 2. send
 // 3. receive
 // 4. receive
-// @todo  THIS Test does not function. The method runSendReceive only
-// schedules one "server" receive.  In other words only one request is
-// listened for and then received. Once it is received, the operator()
-// method calls stop() on the io_service, which causes the second receive
-// to be cancelled.  It is also unclear, what the asio layer does with a
-// second receive on the same socket.
-TEST_F(DNSClientTest, DISABLED_concurrentSendReceive) {
+TEST_F(DNSClientTest, concurrentSendReceive) {
     runSendReceiveTest(false, true);
+    StatMap stats_upd = {
+        { "update-sent", 2},
+        { "update-signed", 0},
+        { "update-unsigned", 2},
+        { "update-success", 2},
+        { "update-timeout", 0},
+        { "update-error", 0}
+    };
+    checkStats(stats_upd);
 }
 
 } // End of anonymous namespace