123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- // Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include <config.h>
- #include <asiolink/interval_timer.h>
- #include <dhcp_ddns/ncr_io.h>
- #include <dhcp_ddns/ncr_udp.h>
- #include <util/time_utilities.h>
- #include <test_utils.h>
- #include <boost/asio/ip/udp.hpp>
- #include <boost/function.hpp>
- #include <boost/bind.hpp>
- #include <gtest/gtest.h>
- #include <algorithm>
- #include <sys/select.h>
- using namespace std;
- using namespace isc;
- using namespace isc::dhcp_ddns;
- namespace {
- /// @brief Defines a list of valid JSON NameChangeRequest test messages.
- const char *valid_msgs[] =
- {
- // Valid Add.
- "{"
- " \"change-type\" : 0 , "
- " \"forward-change\" : true , "
- " \"reverse-change\" : false , "
- " \"fqdn\" : \"walah.walah.com\" , "
- " \"ip-address\" : \"192.168.2.1\" , "
- " \"dhcid\" : \"010203040A7F8E3D\" , "
- " \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
- "}",
- // Valid Remove.
- "{"
- " \"change-type\" : 1 , "
- " \"forward-change\" : true , "
- " \"reverse-change\" : false , "
- " \"fqdn\" : \"walah.walah.com\" , "
- " \"ip-address\" : \"192.168.2.1\" , "
- " \"dhcid\" : \"010203040A7F8E3D\" , "
- " \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
- "}",
- // Valid Add with IPv6 address
- "{"
- " \"change-type\" : 0 , "
- " \"forward-change\" : true , "
- " \"reverse-change\" : false , "
- " \"fqdn\" : \"walah.walah.com\" , "
- " \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
- " \"dhcid\" : \"010203040A7F8E3D\" , "
- " \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
- "}"
- };
- const char* TEST_ADDRESS = "127.0.0.1";
- const uint32_t LISTENER_PORT = 5301;
- const uint32_t SENDER_PORT = LISTENER_PORT+1;
- const long TEST_TIMEOUT = 5 * 1000;
- /// @brief A NOP derivation for constructor test purposes.
- class SimpleListenHandler : public NameChangeListener::RequestReceiveHandler {
- public:
- virtual void operator ()(const NameChangeListener::Result,
- NameChangeRequestPtr&) {
- }
- };
- /// @brief Tests the NameChangeUDPListener constructors.
- /// This test verifies that:
- /// 1. Given valid parameters, the listener constructor works
- TEST(NameChangeUDPListenerBasicTest, constructionTests) {
- // Verify the default constructor works.
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- uint32_t port = LISTENER_PORT;
- isc::asiolink::IOService io_service;
- SimpleListenHandler ncr_handler;
- // Verify that valid constructor works.
- EXPECT_NO_THROW(NameChangeUDPListener(ip_address, port, FMT_JSON,
- ncr_handler));
- }
- /// @brief Tests NameChangeUDPListener starting and stopping listening .
- /// This test verifies that the listener will:
- /// 1. Enter listening state
- /// 2. If in the listening state, does not allow calls to start listening
- /// 3. Exist the listening state
- /// 4. Return to the listening state after stopping
- TEST(NameChangeUDPListenerBasicTest, basicListenTests) {
- // Verify the default constructor works.
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- uint32_t port = LISTENER_PORT;
- isc::asiolink::IOService io_service;
- SimpleListenHandler ncr_handler;
- NameChangeListenerPtr listener;
- ASSERT_NO_THROW(listener.reset(
- new NameChangeUDPListener(ip_address, port, FMT_JSON, ncr_handler)));
- // Verify that we can start listening.
- EXPECT_NO_THROW(listener->startListening(io_service));
- // Verify that we are in listening mode.
- EXPECT_TRUE(listener->amListening());
- // Verify that a read is in progress.
- EXPECT_TRUE(listener->isIoPending());
- // Verify that attempting to listen when we already are is an error.
- EXPECT_THROW(listener->startListening(io_service), NcrListenerError);
- // Verify that we can stop listening.
- EXPECT_NO_THROW(listener->stopListening());
- EXPECT_FALSE(listener->amListening());
- // Verify that IO pending is still true, as IO cancel event has not yet
- // occurred.
- EXPECT_TRUE(listener->isIoPending());
- // Verify that IO pending is false, after cancel event occurs.
- EXPECT_NO_THROW(io_service.run_one());
- EXPECT_FALSE(listener->isIoPending());
- // Verify that attempting to stop listening when we are not is ok.
- EXPECT_NO_THROW(listener->stopListening());
- // Verify that we can re-enter listening.
- EXPECT_NO_THROW(listener->startListening(io_service));
- EXPECT_TRUE(listener->amListening());
- }
- /// @brief Compares two NameChangeRequests for equality.
- bool checkSendVsReceived(NameChangeRequestPtr sent_ncr,
- NameChangeRequestPtr received_ncr) {
- return ((sent_ncr && received_ncr) &&
- (*sent_ncr == *received_ncr));
- }
- /// @brief Text fixture for testing NameChangeUDPListener
- class NameChangeUDPListenerTest : public virtual ::testing::Test,
- NameChangeListener::RequestReceiveHandler {
- public:
- isc::asiolink::IOService io_service_;
- NameChangeListener::Result result_;
- NameChangeRequestPtr sent_ncr_;
- NameChangeRequestPtr received_ncr_;
- NameChangeListenerPtr listener_;
- isc::asiolink::IntervalTimer test_timer_;
- /// @brief Constructor
- //
- // Instantiates the listener member and the test timer. The timer is used
- // to ensure a test doesn't go awry and hang forever.
- NameChangeUDPListenerTest()
- : io_service_(), result_(NameChangeListener::SUCCESS),
- test_timer_(io_service_) {
- isc::asiolink::IOAddress addr(TEST_ADDRESS);
- listener_.reset(new NameChangeUDPListener(addr, LISTENER_PORT,
- FMT_JSON, *this, true));
- // Set the test timeout to break any running tasks if they hang.
- test_timer_.setup(boost::bind(&NameChangeUDPListenerTest::
- testTimeoutHandler, this),
- TEST_TIMEOUT);
- }
- virtual ~NameChangeUDPListenerTest(){
- }
- /// @brief Converts JSON string into an NCR and sends it to the listener.
- ///
- void sendNcr(const std::string& msg) {
- // Build an NCR from json string. This verifies that the
- // test string is valid.
- ASSERT_NO_THROW(sent_ncr_ = NameChangeRequest::fromJSON(msg));
- // Now use the NCR to write JSON to an output buffer.
- isc::util::OutputBuffer ncr_buffer(1024);
- ASSERT_NO_THROW(sent_ncr_->toFormat(FMT_JSON, ncr_buffer));
- // Create a UDP socket through which our "sender" will send the NCR.
- boost::asio::ip::udp::socket
- udp_socket(io_service_.get_io_service(), boost::asio::ip::udp::v4());
- // Create an endpoint pointed at the listener.
- boost::asio::ip::udp::endpoint
- listener_endpoint(boost::asio::ip::address::from_string(TEST_ADDRESS),
- LISTENER_PORT);
- // A response message is now ready to send. Send it!
- // Note this uses a synchronous send so it ships immediately.
- // If listener isn't in listening mode, it will get missed.
- udp_socket.send_to(boost::asio::buffer(ncr_buffer.getData(),
- ncr_buffer.getLength()),
- listener_endpoint);
- }
- /// @brief RequestReceiveHandler operator implementation for receiving NCRs.
- ///
- /// The fixture acts as the "application" layer. It derives from
- /// RequestReceiveHandler and as such implements operator() in order to
- /// receive NCRs.
- virtual void operator ()(const NameChangeListener::Result result,
- NameChangeRequestPtr& ncr) {
- // save the result and the NCR we received
- result_ = result;
- received_ncr_ = ncr;
- }
- // @brief Handler invoked when test timeout is hit.
- //
- // This callback stops all running (hanging) tasks on IO service.
- void testTimeoutHandler() {
- io_service_.stop();
- FAIL() << "Test timeout hit.";
- }
- };
- /// @brief Tests NameChangeUDPListener ability to receive NCRs.
- /// This test verifies that a listener can enter listening mode and
- /// receive NCRs in wire format on its UDP socket; reconstruct the
- /// NCRs and delivery them to the "application" layer.
- TEST_F(NameChangeUDPListenerTest, basicReceivetest) {
- // Verify we can enter listening mode.
- ASSERT_FALSE(listener_->amListening());
- ASSERT_NO_THROW(listener_->startListening(io_service_));
- ASSERT_TRUE(listener_->amListening());
- ASSERT_TRUE(listener_->isIoPending());
- // Iterate over a series of requests, sending and receiving one
- /// at time.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- for (int i = 0; i < num_msgs; i++) {
- // We are not verifying ability to send, so if we can't test is over.
- ASSERT_NO_THROW(sendNcr(valid_msgs[i]));
- // Execute no more then one event, which should be receive complete.
- EXPECT_NO_THROW(io_service_.run_one());
- // Verify the "application" status value for a successful complete.
- EXPECT_EQ(NameChangeListener::SUCCESS, result_);
- // Verify the received request matches the sent request.
- EXPECT_TRUE(checkSendVsReceived(sent_ncr_, received_ncr_));
- }
- // Verify we can gracefully stop listening.
- EXPECT_NO_THROW(listener_->stopListening());
- EXPECT_FALSE(listener_->amListening());
- // Verify that IO pending is false, after cancel event occurs.
- EXPECT_NO_THROW(io_service_.run_one());
- EXPECT_FALSE(listener_->isIoPending());
- }
- /// @brief A NOP derivation for constructor test purposes.
- class SimpleSendHandler : public NameChangeSender::RequestSendHandler {
- public:
- SimpleSendHandler() : pass_count_(0), error_count_(0) {
- }
- virtual void operator ()(const NameChangeSender::Result result,
- NameChangeRequestPtr&) {
- if (result == NameChangeSender::SUCCESS) {
- ++pass_count_;
- } else {
- ++error_count_;
- }
- }
- int pass_count_;
- int error_count_;
- };
- /// @brief Tests the NameChangeUDPSender constructors.
- /// This test verifies that:
- /// 1. Constructing with a max queue size of 0 is not allowed
- /// 2. Given valid parameters, the sender constructor works
- /// 3. Default construction provides default max queue size
- /// 4. Construction with a custom max queue size works
- TEST(NameChangeUDPSenderBasicTest, constructionTests) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- uint32_t port = SENDER_PORT;
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Verify that constructing with an queue size of zero is not allowed.
- EXPECT_THROW(NameChangeUDPSender(ip_address, port,
- ip_address, port, FMT_JSON, ncr_handler, 0), NcrSenderError);
- NameChangeSenderPtr sender;
- // Verify that valid constructor works.
- EXPECT_NO_THROW(sender.reset(
- new NameChangeUDPSender(ip_address, port, ip_address, port,
- FMT_JSON, ncr_handler)));
- // Verify that send queue default max is correct.
- size_t expected = NameChangeSender::MAX_QUEUE_DEFAULT;
- EXPECT_EQ(expected, sender->getQueueMaxSize());
- // Verify that constructor with a valid custom queue size works.
- EXPECT_NO_THROW(sender.reset(
- new NameChangeUDPSender(ip_address, port, ip_address, port,
- FMT_JSON, ncr_handler, 100)));
- EXPECT_EQ(100, sender->getQueueMaxSize());
- }
- /// @brief Tests NameChangeUDPSender basic send functionality
- /// This test verifies that:
- TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Tests are based on a list of messages, get the count now.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- // Create the sender, setting the queue max equal to the number of
- // messages we will have in the list.
- NameChangeUDPSender sender(ip_address, SENDER_PORT, ip_address,
- LISTENER_PORT, FMT_JSON, ncr_handler,
- num_msgs, true);
- // Verify that we can start sending.
- EXPECT_NO_THROW(sender.startSending(io_service));
- EXPECT_TRUE(sender.amSending());
- // Verify that attempting to send when we already are is an error.
- EXPECT_THROW(sender.startSending(io_service), NcrSenderError);
- // Verify that we can stop sending.
- EXPECT_NO_THROW(sender.stopSending());
- EXPECT_FALSE(sender.amSending());
- // Verify that attempting to stop sending when we are not is ok.
- EXPECT_NO_THROW(sender.stopSending());
- // Verify that we can re-enter sending after stopping.
- EXPECT_NO_THROW(sender.startSending(io_service));
- EXPECT_TRUE(sender.amSending());
- // Fetch the sender's select-fd.
- int select_fd = sender.getSelectFd();
- // Verify select_fd is valid and currently shows no ready to read.
- ASSERT_NE(util::WatchSocket::SOCKET_NOT_VALID, select_fd);
- // Make sure select_fd does evaluates to not ready via select and
- // that ioReady() method agrees.
- ASSERT_EQ(0, selectCheck(select_fd));
- ASSERT_FALSE(sender.ioReady());
- // Iterate over a series of messages, sending each one. Since we
- // do not invoke IOService::run, then the messages should accumulate
- // in the queue.
- NameChangeRequestPtr ncr;
- NameChangeRequestPtr ncr2;
- for (int i = 0; i < num_msgs; i++) {
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- // Verify that the queue count increments in step with each send.
- EXPECT_EQ(i+1, sender.getQueueSize());
- // Verify that peekAt(i) returns the NCR we just added.
- ASSERT_NO_THROW(ncr2 = sender.peekAt(i));
- ASSERT_TRUE(ncr2);
- EXPECT_TRUE(*ncr == *ncr2);
- }
- // Verify that attempting to peek beyond the end of the queue, throws.
- ASSERT_THROW(sender.peekAt(sender.getQueueSize()+1), NcrSenderError);
- // Verify that attempting to send an additional message results in a
- // queue full exception.
- EXPECT_THROW(sender.sendRequest(ncr), NcrSenderQueueFull);
- // Loop for the number of valid messages. So long as there is at least
- // on NCR in the queue, select-fd indicate ready to read. Invoke
- // IOService::run_one. This should complete the send of exactly one
- // message and the queue count should decrement accordingly.
- for (int i = num_msgs; i > 0; i--) {
- // Make sure select_fd does evaluates to ready via select and
- // that ioReady() method agrees.
- ASSERT_TRUE(selectCheck(select_fd) > 0);
- ASSERT_TRUE(sender.ioReady());
- // Execute at one ready handler.
- ASSERT_NO_THROW(sender.runReadyIO());
- // Verify that the queue count decrements in step with each run.
- EXPECT_EQ(i-1, sender.getQueueSize());
- }
- // Make sure select_fd does evaluates to not ready via select and
- // that ioReady() method agrees.
- ASSERT_EQ(0, selectCheck(select_fd));
- ASSERT_FALSE(sender.ioReady());
- // Verify that the queue is empty.
- EXPECT_EQ(0, sender.getQueueSize());
- // Verify that we can add back to the queue
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- EXPECT_EQ(1, sender.getQueueSize());
- // Verify that we can remove the current entry at the front of the queue.
- EXPECT_NO_THROW(sender.skipNext());
- EXPECT_EQ(0, sender.getQueueSize());
- // Verify that flushing the queue is not allowed in sending state.
- EXPECT_THROW(sender.clearSendQueue(), NcrSenderError);
- // Put num_msgs messages on the queue.
- for (int i = 0; i < num_msgs; i++) {
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- }
- // Make sure we have number of messages expected.
- EXPECT_EQ(num_msgs, sender.getQueueSize());
- // Verify that we can gracefully stop sending.
- EXPECT_NO_THROW(sender.stopSending());
- EXPECT_FALSE(sender.amSending());
- // Verify that the queue is preserved after leaving sending state.
- EXPECT_EQ(num_msgs - 1, sender.getQueueSize());
- // Verify that flushing the queue works when not sending.
- EXPECT_NO_THROW(sender.clearSendQueue());
- EXPECT_EQ(0, sender.getQueueSize());
- }
- /// @brief Tests that sending gets kick-started if the queue isn't empty
- /// when startSending is called.
- TEST(NameChangeUDPSenderBasicTest, autoStart) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Tests are based on a list of messages, get the count now.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- // Create the sender, setting the queue max equal to the number of
- // messages we will have in the list.
- NameChangeUDPSender sender(ip_address, SENDER_PORT, ip_address,
- LISTENER_PORT, FMT_JSON, ncr_handler,
- num_msgs, true);
- // Verify that we can start sending.
- EXPECT_NO_THROW(sender.startSending(io_service));
- EXPECT_TRUE(sender.amSending());
- // Queue up messages.
- NameChangeRequestPtr ncr;
- for (int i = 0; i < num_msgs; i++) {
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- }
- // Make sure queue count is what we expect.
- EXPECT_EQ(num_msgs, sender.getQueueSize());
- // Stop sending.
- ASSERT_NO_THROW(sender.stopSending());
- ASSERT_FALSE(sender.amSending());
- // We should have completed the first message only.
- EXPECT_EQ(--num_msgs, sender.getQueueSize());
- // Restart sending.
- EXPECT_NO_THROW(sender.startSending(io_service));
- // We should be able to loop through remaining messages and send them.
- for (int i = num_msgs; i > 0; i--) {
- // ioReady() should evaluate to true.
- ASSERT_TRUE(sender.ioReady());
- // Execute at one ready handler.
- ASSERT_NO_THROW(sender.runReadyIO());
- }
- // Verify that the queue is empty.
- EXPECT_EQ(0, sender.getQueueSize());
- }
- /// @brief Tests NameChangeUDPSender basic send with INADDR_ANY and port 0.
- TEST(NameChangeUDPSenderBasicTest, anyAddressSend) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOAddress any_address("0.0.0.0");
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Tests are based on a list of messages, get the count now.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- // Create the sender, setting the queue max equal to the number of
- // messages we will have in the list.
- NameChangeUDPSender sender(any_address, 0, ip_address, LISTENER_PORT,
- FMT_JSON, ncr_handler, num_msgs);
- // Enter send mode.
- ASSERT_NO_THROW(sender.startSending(io_service));
- EXPECT_TRUE(sender.amSending());
- // Create and queue up a message.
- NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- EXPECT_EQ(1, sender.getQueueSize());
- // Verify we have a ready IO, then execute at one ready handler.
- ASSERT_TRUE(sender.ioReady());
- ASSERT_NO_THROW(sender.runReadyIO());
- // Verify that sender shows no IO ready.
- // and that the queue is empty.
- ASSERT_FALSE(sender.ioReady());
- EXPECT_EQ(0, sender.getQueueSize());
- }
- /// @brief Test the NameChangeSender::assumeQueue method.
- TEST(NameChangeSender, assumeQueue) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- uint32_t port = SENDER_PORT;
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- NameChangeRequestPtr ncr;
- // Tests are based on a list of messages, get the count now.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- // Create two senders with queue max equal to the number of
- // messages we will have in the list.
- NameChangeUDPSender sender1(ip_address, port, ip_address, port,
- FMT_JSON, ncr_handler, num_msgs);
- NameChangeUDPSender sender2(ip_address, port+1, ip_address, port,
- FMT_JSON, ncr_handler, num_msgs);
- // Place sender1 into send mode and queue up messages.
- ASSERT_NO_THROW(sender1.startSending(io_service));
- for (int i = 0; i < num_msgs; i++) {
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- ASSERT_NO_THROW(sender1.sendRequest(ncr));
- }
- // Make sure sender1's queue count is as expected.
- ASSERT_EQ(num_msgs, sender1.getQueueSize());
- // Verify sender1 is sending, sender2 is not.
- ASSERT_TRUE(sender1.amSending());
- ASSERT_FALSE(sender2.amSending());
- // Transfer from sender1 to sender2 should fail because
- // sender1 is in send mode.
- ASSERT_THROW(sender2.assumeQueue(sender1), NcrSenderError);
- // Take sender1 out of send mode.
- ASSERT_NO_THROW(sender1.stopSending());
- ASSERT_FALSE(sender1.amSending());
- // Stopping should have completed the first message.
- --num_msgs;
- EXPECT_EQ(num_msgs, sender1.getQueueSize());
- // Transfer should succeed. Verify sender1 has none,
- // and sender2 has num_msgs queued.
- EXPECT_NO_THROW(sender2.assumeQueue(sender1));
- EXPECT_EQ(0, sender1.getQueueSize());
- EXPECT_EQ(num_msgs, sender2.getQueueSize());
- // Reduce sender1's max queue size.
- ASSERT_NO_THROW(sender1.setQueueMaxSize(num_msgs - 1));
- // Transfer should fail as sender1's queue is not large enough.
- ASSERT_THROW(sender1.assumeQueue(sender2), NcrSenderError);
- // Place sender1 into send mode and queue up a message.
- ASSERT_NO_THROW(sender1.startSending(io_service));
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
- ASSERT_NO_THROW(sender1.sendRequest(ncr));
- // Take sender1 out of send mode.
- ASSERT_NO_THROW(sender1.stopSending());
- // Try to transfer from sender1 to sender2. This should fail
- // as sender2's queue is not empty.
- ASSERT_THROW(sender2.assumeQueue(sender1), NcrSenderError);
- }
- /// @brief Text fixture that allows testing a listener and sender together
- /// It derives from both the receive and send handler classes and contains
- /// and instance of UDP listener and UDP sender.
- class NameChangeUDPTest : public virtual ::testing::Test,
- NameChangeListener::RequestReceiveHandler,
- NameChangeSender::RequestSendHandler {
- public:
- isc::asiolink::IOService io_service_;
- NameChangeListener::Result recv_result_;
- NameChangeSender::Result send_result_;
- NameChangeListenerPtr listener_;
- NameChangeSenderPtr sender_;
- isc::asiolink::IntervalTimer test_timer_;
- std::vector<NameChangeRequestPtr> sent_ncrs_;
- std::vector<NameChangeRequestPtr> received_ncrs_;
- NameChangeUDPTest()
- : io_service_(), recv_result_(NameChangeListener::SUCCESS),
- send_result_(NameChangeSender::SUCCESS), test_timer_(io_service_) {
- isc::asiolink::IOAddress addr(TEST_ADDRESS);
- // Create our listener instance. Note that reuse_address is true.
- listener_.reset(
- new NameChangeUDPListener(addr, LISTENER_PORT, FMT_JSON,
- *this, true));
- // Create our sender instance. Note that reuse_address is true.
- sender_.reset(
- new NameChangeUDPSender(addr, SENDER_PORT, addr, LISTENER_PORT,
- FMT_JSON, *this, 100, true));
- // Set the test timeout to break any running tasks if they hang.
- test_timer_.setup(boost::bind(&NameChangeUDPTest::testTimeoutHandler,
- this),
- TEST_TIMEOUT);
- }
- void reset_results() {
- sent_ncrs_.clear();
- received_ncrs_.clear();
- }
- /// @brief Implements the receive completion handler.
- virtual void operator ()(const NameChangeListener::Result result,
- NameChangeRequestPtr& ncr) {
- // save the result and the NCR received.
- recv_result_ = result;
- received_ncrs_.push_back(ncr);
- }
- /// @brief Implements the send completion handler.
- virtual void operator ()(const NameChangeSender::Result result,
- NameChangeRequestPtr& ncr) {
- // save the result and the NCR sent.
- send_result_ = result;
- sent_ncrs_.push_back(ncr);
- }
- // @brief Handler invoked when test timeout is hit.
- //
- // This callback stops all running (hanging) tasks on IO service.
- void testTimeoutHandler() {
- io_service_.stop();
- FAIL() << "Test timeout hit.";
- }
- };
- /// @brief Uses a sender and listener to test UDP-based NCR delivery
- /// Conducts a "round-trip" test using a sender to transmit a set of valid
- /// NCRs to a listener. The test verifies that what was sent matches what
- /// was received both in quantity and in content.
- TEST_F (NameChangeUDPTest, roundTripTest) {
- // Place the listener into listening state.
- ASSERT_NO_THROW(listener_->startListening(io_service_));
- EXPECT_TRUE(listener_->amListening());
- // Get the number of messages in the list of test messages.
- int num_msgs = sizeof(valid_msgs)/sizeof(char*);
- // Place the sender into sending state.
- ASSERT_NO_THROW(sender_->startSending(io_service_));
- EXPECT_TRUE(sender_->amSending());
- for (int i = 0; i < num_msgs; i++) {
- NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- sender_->sendRequest(ncr);
- EXPECT_EQ(i+1, sender_->getQueueSize());
- }
- // Execute callbacks until we have sent and received all of messages.
- while (sender_->getQueueSize() > 0 || (received_ncrs_.size() < num_msgs)) {
- EXPECT_NO_THROW(io_service_.run_one());
- }
- // Send queue should be empty.
- EXPECT_EQ(0, sender_->getQueueSize());
- // We should have the same number of sends and receives as we do messages.
- ASSERT_EQ(num_msgs, sent_ncrs_.size());
- ASSERT_EQ(num_msgs, received_ncrs_.size());
- // Verify that what we sent matches what we received.
- for (int i = 0; i < num_msgs; i++) {
- EXPECT_TRUE (checkSendVsReceived(sent_ncrs_[i], received_ncrs_[i]));
- }
- // Verify that we can gracefully stop listening.
- EXPECT_NO_THROW(listener_->stopListening());
- EXPECT_FALSE(listener_->amListening());
- // Verify that IO pending is false, after cancel event occurs.
- EXPECT_NO_THROW(io_service_.run_one());
- EXPECT_FALSE(listener_->isIoPending());
- // Verify that we can gracefully stop sending.
- EXPECT_NO_THROW(sender_->stopSending());
- EXPECT_FALSE(sender_->amSending());
- }
- // Tests error handling of a failure to mark the watch socket ready, when
- // sendRequestt() is called.
- TEST(NameChangeUDPSenderBasicTest, watchClosedBeforeSendRequest) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Create the sender and put into send mode.
- NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
- FMT_JSON, ncr_handler, 100, true);
- ASSERT_NO_THROW(sender.startSending(io_service));
- ASSERT_TRUE(sender.amSending());
- // Create an NCR.
- NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
- // Tamper with the watch socket by closing the select-fd.
- close(sender.getSelectFd());
- // Send should fail as we interferred by closing the select-fd.
- ASSERT_THROW(sender.sendRequest(ncr), util::WatchSocketError);
- // Verify we didn't invoke the handler.
- EXPECT_EQ(0, ncr_handler.pass_count_);
- EXPECT_EQ(0, ncr_handler.error_count_);
- // Request remains in the queue. Technically it was sent but its
- // completion handler won't get called.
- EXPECT_EQ(1, sender.getQueueSize());
- }
- // Tests error handling of a failure to mark the watch socket ready, when
- // sendNext() is called during completion handling.
- TEST(NameChangeUDPSenderBasicTest, watchClosedAfterSendRequest) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Create the sender and put into send mode.
- NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
- FMT_JSON, ncr_handler, 100, true);
- ASSERT_NO_THROW(sender.startSending(io_service));
- ASSERT_TRUE(sender.amSending());
- // Build and queue up 2 messages. No handlers will get called yet.
- for (int i = 0; i < 2; i++) {
- NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- sender.sendRequest(ncr);
- EXPECT_EQ(i+1, sender.getQueueSize());
- }
- // Tamper with the watch socket by closing the select-fd.
- close (sender.getSelectFd());
- // Run one handler. This should execute the send completion handler
- // after sending the first message. Duing completion handling, we will
- // attempt to queue the second message which should fail.
- ASSERT_NO_THROW(sender.runReadyIO());
- // Verify handler got called twice. First request should have be sent
- // without error, second call should have failed to send due to watch
- // socket markReady failure.
- EXPECT_EQ(1, ncr_handler.pass_count_);
- EXPECT_EQ(1, ncr_handler.error_count_);
- // The second request should still be in the queue.
- EXPECT_EQ(1, sender.getQueueSize());
- }
- // Tests error handling of a failure to clear the watch socket during
- // completion handling.
- TEST(NameChangeUDPSenderBasicTest, watchSocketBadRead) {
- isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- isc::asiolink::IOService io_service;
- SimpleSendHandler ncr_handler;
- // Create the sender and put into send mode.
- NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
- FMT_JSON, ncr_handler, 100, true);
- ASSERT_NO_THROW(sender.startSending(io_service));
- ASSERT_TRUE(sender.amSending());
- // Build and queue up 2 messages. No handlers will get called yet.
- for (int i = 0; i < 2; i++) {
- NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
- sender.sendRequest(ncr);
- EXPECT_EQ(i+1, sender.getQueueSize());
- }
- // Fetch the sender's select-fd.
- int select_fd = sender.getSelectFd();
- // Verify that select_fd appears ready.
- ASSERT_TRUE(selectCheck(select_fd) > 0);
- // Interfere by reading part of the marker from the select-fd.
- uint32_t buf = 0;
- ASSERT_EQ((read (select_fd, &buf, 1)), 1);
- ASSERT_NE(util::WatchSocket::MARKER, buf);
- // Run one handler. This should execute the send completion handler
- // after sending the message. Duing completion handling clearing the
- // watch socket should fail, which will close the socket, but not
- // result in a throw.
- ASSERT_NO_THROW(sender.runReadyIO());
- // Verify handler got called twice. First request should have be sent
- // without error, second call should have failed to send due to watch
- // socket markReady failure.
- EXPECT_EQ(1, ncr_handler.pass_count_);
- EXPECT_EQ(1, ncr_handler.error_count_);
- // The second request should still be in the queue.
- EXPECT_EQ(1, sender.getQueueSize());
- }
- } // end of anonymous namespace
|