|
@@ -12,13 +12,17 @@
|
|
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
-#include <gtest/gtest.h>
|
|
|
-#include <boost/bind.hpp>
|
|
|
+#include <algorithm>
|
|
|
#include <cstdlib>
|
|
|
#include <string>
|
|
|
+#include <iostream>
|
|
|
+#include <iomanip>
|
|
|
+#include <iterator>
|
|
|
#include <vector>
|
|
|
|
|
|
-#include <string.h>
|
|
|
+#include <gtest/gtest.h>
|
|
|
+#include <boost/bind.hpp>
|
|
|
+#include <boost/date_time/posix_time/posix_time_types.hpp>
|
|
|
|
|
|
#include <asio.hpp>
|
|
|
|
|
@@ -30,19 +34,27 @@
|
|
|
#include <dns/name.h>
|
|
|
#include <dns/rcode.h>
|
|
|
|
|
|
+#include <asiolink/asiolink_utilities.h>
|
|
|
+#include <asiolink/io_address.h>
|
|
|
+#include <asiolink/io_endpoint.h>
|
|
|
#include <asiolink/io_fetch.h>
|
|
|
#include <asiolink/io_service.h>
|
|
|
|
|
|
using namespace asio;
|
|
|
using namespace isc::dns;
|
|
|
-using asio::ip::udp;
|
|
|
+using namespace asio::ip;
|
|
|
+using namespace std;
|
|
|
|
|
|
namespace asiolink {
|
|
|
|
|
|
const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
|
|
|
const uint16_t TEST_PORT(5301);
|
|
|
-// FIXME Shouldn't we send something that is real message?
|
|
|
-const char TEST_DATA[] = "TEST DATA";
|
|
|
+const int SEND_INTERVAL = 250; // Interval in ms between TCP sends
|
|
|
+const size_t MAX_SIZE = 64 * 1024; // Should be able to take 64kB
|
|
|
+
|
|
|
+// The tests are complex, so debug output has been left in (although disabled).
|
|
|
+// Set this to true to enable it.
|
|
|
+const bool DEBUG = false;
|
|
|
|
|
|
/// \brief Test fixture for the asiolink::IOFetch.
|
|
|
class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
|
|
@@ -52,13 +64,26 @@ public:
|
|
|
IOFetch::Result expected_; ///< Expected result of the callback
|
|
|
bool run_; ///< Did the callback run already?
|
|
|
Question question_; ///< What to ask
|
|
|
- OutputBufferPtr buff_; ///< Buffer to hold result
|
|
|
+ OutputBufferPtr result_buff_; ///< Buffer to hold result of fetch
|
|
|
+ OutputBufferPtr msgbuf_; ///< Buffer corresponding to known question
|
|
|
IOFetch udp_fetch_; ///< For UDP query test
|
|
|
- //IOFetch tcp_fetch_; ///< For TCP query test
|
|
|
-
|
|
|
- // The next member is the buffer iin which the "server" (implemented by the
|
|
|
- // response handler method) receives the question sent by the fetch object.
|
|
|
- std::vector<char> server_buff_; ///< Server buffer
|
|
|
+ IOFetch tcp_fetch_; ///< For TCP query test
|
|
|
+ IOFetch::Protocol protocol_; ///< Protocol being tested
|
|
|
+ size_t cumulative_; ///< Cumulative data received by "server".
|
|
|
+ deadline_timer timer_; ///< Timer to measure timeouts
|
|
|
+
|
|
|
+ // The next member is the buffer in which the "server" (implemented by the
|
|
|
+ // response handler methods in this class) receives the question sent by the
|
|
|
+ // fetch object.
|
|
|
+ uint8_t receive_buffer_[MAX_SIZE]; ///< Server receive buffer
|
|
|
+ vector<uint8_t> send_buffer_; ///< Server send buffer
|
|
|
+ uint16_t send_cumulative_; ///< Data sent so far
|
|
|
+
|
|
|
+ // Other data.
|
|
|
+ string return_data_; ///< Data returned by server
|
|
|
+ string test_data_; ///< Large string - here for convenience
|
|
|
+ bool debug_; ///< true to enable debug output
|
|
|
+ size_t tcp_send_size_; ///< Max size of TCP send
|
|
|
|
|
|
/// \brief Constructor
|
|
|
IOFetchTest() :
|
|
@@ -66,126 +91,518 @@ public:
|
|
|
expected_(IOFetch::NOTSET),
|
|
|
run_(false),
|
|
|
question_(Name("example.net"), RRClass::IN(), RRType::A()),
|
|
|
- buff_(new OutputBuffer(512)),
|
|
|
- udp_fetch_(IPPROTO_UDP, service_, question_, IOAddress(TEST_HOST),
|
|
|
- TEST_PORT, buff_, this, 100),
|
|
|
- server_buff_(512)
|
|
|
- // tcp_fetch_(service_, question_, IOAddress(TEST_HOST), TEST_PORT,
|
|
|
- // buff_, this, 100, IPPROTO_UDP)
|
|
|
- { }
|
|
|
+ result_buff_(new OutputBuffer(512)),
|
|
|
+ msgbuf_(new OutputBuffer(512)),
|
|
|
+ udp_fetch_(IOFetch::UDP, service_, question_, IOAddress(TEST_HOST),
|
|
|
+ TEST_PORT, result_buff_, this, 100),
|
|
|
+ tcp_fetch_(IOFetch::TCP, service_, question_, IOAddress(TEST_HOST),
|
|
|
+ TEST_PORT, result_buff_, this, (16 * SEND_INTERVAL)),
|
|
|
+ // Timeout interval chosen to ensure no timeout
|
|
|
+ protocol_(IOFetch::TCP), // for initialization - will be changed
|
|
|
+ cumulative_(0),
|
|
|
+ timer_(service_.get_io_service()),
|
|
|
+ receive_buffer_(),
|
|
|
+ send_buffer_(),
|
|
|
+ send_cumulative_(0),
|
|
|
+ return_data_(""),
|
|
|
+ test_data_(""),
|
|
|
+ debug_(DEBUG),
|
|
|
+ tcp_send_size_(0)
|
|
|
+ {
|
|
|
+ // Construct the data buffer for question we expect to receive.
|
|
|
+ Message msg(Message::RENDER);
|
|
|
+ msg.setQid(0);
|
|
|
+ msg.setOpcode(Opcode::QUERY());
|
|
|
+ msg.setRcode(Rcode::NOERROR());
|
|
|
+ msg.setHeaderFlag(Message::HEADERFLAG_RD);
|
|
|
+ msg.addQuestion(question_);
|
|
|
+ MessageRenderer renderer(*msgbuf_);
|
|
|
+ msg.toWire(renderer);
|
|
|
+
|
|
|
+ // Initialize the test data to be returned: tests will return a
|
|
|
+ // substring of this data. (It's convenient to have this as a member of
|
|
|
+ // the class.)
|
|
|
+ //
|
|
|
+ // We could initialize the data with a single character, but as an added
|
|
|
+ // check we'll make ssre that it has some structure.
|
|
|
+
|
|
|
+ test_data_.clear();
|
|
|
+ test_data_.reserve(MAX_SIZE);
|
|
|
+ while (test_data_.size() < MAX_SIZE) {
|
|
|
+ test_data_ += "A message to be returned to the client that has "
|
|
|
+ "some sort of structure.";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// \brief UDP Response handler (the "remote UDP DNS server")
|
|
|
+ ///
|
|
|
+ /// When IOFetch is sending data, this response handler emulates the remote
|
|
|
+ /// DNS server. It checks that the data sent by the IOFetch object is what
|
|
|
+ /// was expected to have been sent, then sends back a known buffer of data.
|
|
|
+ ///
|
|
|
+ /// \param remote Endpoint to which to send the answer
|
|
|
+ /// \param socket Socket to use to send the answer
|
|
|
+ /// \param ec ASIO error code, completion code of asynchronous I/O issued
|
|
|
+ /// by the "server" to receive data.
|
|
|
+ /// \param length Amount of data received.
|
|
|
+ void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
|
|
|
+ error_code ec = error_code(), size_t length = 0) {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "udpReceiveHandler(): error = " << ec.value() <<
|
|
|
+ ", length = " << length << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The QID in the incoming data is random so set it to 0 for the
|
|
|
+ // data comparison check. (It is set to 0 in the buffer containing
|
|
|
+ // the expected data.)
|
|
|
+ receive_buffer_[0] = receive_buffer_[1] = 0;
|
|
|
+
|
|
|
+ // Check that length of the received data and the expected data are
|
|
|
+ // identical, then check that the data is identical as well.
|
|
|
+ EXPECT_EQ(msgbuf_->getLength(), length);
|
|
|
+ EXPECT_TRUE(equal(receive_buffer_, (receive_buffer_ + length - 1),
|
|
|
+ static_cast<const uint8_t*>(msgbuf_->getData())));
|
|
|
+
|
|
|
+ // Return a message back to the IOFetch object.
|
|
|
+ socket->send_to(asio::buffer(return_data_.c_str(), return_data_.size()),
|
|
|
+ *remote);
|
|
|
+ if (debug_) {
|
|
|
+ cout << "udpReceiveHandler(): returned " << return_data_.size() <<
|
|
|
+ " bytes to the client" << endl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// \brief Completion Handler for accepting TCP data
|
|
|
+ ///
|
|
|
+ /// Called when the remote system connects to the "server". It issues
|
|
|
+ /// an asynchronous read on the socket to read data.
|
|
|
+ ///
|
|
|
+ /// \param socket Socket on which data will be received
|
|
|
+ /// \param ec Boost error code, value should be zero.
|
|
|
+ void tcpAcceptHandler(tcp::socket* socket, error_code ec = error_code())
|
|
|
+ {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpAcceptHandler(): error = " << ec.value() << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Expect that the accept completed without a problem.
|
|
|
+ EXPECT_EQ(0, ec.value());
|
|
|
+
|
|
|
+ // Work out the maximum size of data we can send over it when we
|
|
|
+ // respond, then subtract 1kB or so for safety.
|
|
|
+ tcp::socket::send_buffer_size send_size;
|
|
|
+ socket->get_option(send_size);
|
|
|
+ if (send_size.value() < (2 * 1024)) {
|
|
|
+ FAIL() << "TCP send size is less than 2kB";
|
|
|
+ } else {
|
|
|
+ tcp_send_size_ = send_size.value() - 1024;
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpacceptHandler(): will use send size = " << tcp_send_size_ << endl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Initiate a read on the socket.
|
|
|
+ cumulative_ = 0;
|
|
|
+ socket->async_receive(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
|
|
|
+ boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// \brief Completion handler for receiving TCP data
|
|
|
+ ///
|
|
|
+ /// When IOFetch is sending data, this response handler emulates the remote
|
|
|
+ /// DNS server. It that all the data sent by the IOFetch object has been
|
|
|
+ /// received, issuing another read if not. If the data is complete, it is
|
|
|
+ /// compared to what is expected and a reply sent back to the IOFetch.
|
|
|
+ ///
|
|
|
+ /// \param socket Socket to use to send the answer
|
|
|
+ /// \param ec ASIO error code, completion code of asynchronous I/O issued
|
|
|
+ /// by the "server" to receive data.
|
|
|
+ /// \param length Amount of data received.
|
|
|
+ void tcpReceiveHandler(tcp::socket* socket, error_code ec = error_code(),
|
|
|
+ size_t length = 0)
|
|
|
+ {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpReceiveHandler(): error = " << ec.value() <<
|
|
|
+ ", length = " << length << endl;
|
|
|
+ }
|
|
|
+ // Expect that the receive completed without a problem.
|
|
|
+ EXPECT_EQ(0, ec.value());
|
|
|
+
|
|
|
+ // If we haven't received all the data, issue another read.
|
|
|
+ cumulative_ += length;
|
|
|
+ bool complete = false;
|
|
|
+ if (cumulative_ > 2) {
|
|
|
+ uint16_t dns_length = readUint16(receive_buffer_);
|
|
|
+ complete = ((dns_length + 2) == cumulative_);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!complete) {
|
|
|
+ socket->async_receive(asio::buffer((receive_buffer_ + cumulative_),
|
|
|
+ (sizeof(receive_buffer_) - cumulative_)),
|
|
|
+ boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check that length of the DNS message received is that expected, then
|
|
|
+ // compare buffers, zeroing the QID in the received buffer to match
|
|
|
+ // that set in our expected question. Note that due to the length
|
|
|
+ // field the QID in the received buffer is in the third and fourth
|
|
|
+ // bytes.
|
|
|
+ EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
|
|
|
+ receive_buffer_[2] = receive_buffer_[3] = 0;
|
|
|
+ EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
|
|
|
+ static_cast<const uint8_t*>(msgbuf_->getData())));
|
|
|
+
|
|
|
+ // ... and return a message back. This has to be preceded by a two-byte
|
|
|
+ // count field.
|
|
|
+ send_buffer_.clear();
|
|
|
+ send_buffer_.push_back(0);
|
|
|
+ send_buffer_.push_back(0);
|
|
|
+ writeUint16(return_data_.size(), &send_buffer_[0]);
|
|
|
+ copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
|
|
|
+
|
|
|
+ // Send the data. This is done in multiple writes with a delay between
|
|
|
+ // each to check that the reassembly of TCP packets from fragments works.
|
|
|
+ send_cumulative_ = 0;
|
|
|
+ tcpSendData(socket);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// \brief Sent Data Over TCP
|
|
|
+ ///
|
|
|
+ /// Send the TCP data back to the IOFetch object. The data is sent in
|
|
|
+ /// three chunks - two of 16 bytes and the remainder, with a 250ms gap
|
|
|
+ /// between each. (Amounts of data smaller than one 32 bytes are sent in
|
|
|
+ /// one or two packets.)
|
|
|
+ ///
|
|
|
+ /// \param socket Socket over which send should take place
|
|
|
+ void tcpSendData(tcp::socket* socket) {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpSendData()" << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Decide what to send based on the cumulative count. At most we'll do
|
|
|
+ // two chunks of 16 bytes (with a 250ms gap between) and then the
|
|
|
+ // remainder.
|
|
|
+ uint8_t* send_ptr = &send_buffer_[send_cumulative_];
|
|
|
+ // Pointer to data to send
|
|
|
+ size_t amount = 16; // Amount of data to send
|
|
|
+ if (send_cumulative_ < (2 * amount)) {
|
|
|
+
|
|
|
+ // First or second time through, send at most 16 bytes
|
|
|
+ amount = min(amount, (send_buffer_.size() - send_cumulative_));
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // For all subsequent times, send the remainder, maximised to
|
|
|
+ // whatever we have chosen for the maximum send size.
|
|
|
+ amount = min(tcp_send_size_,
|
|
|
+ (send_buffer_.size() - send_cumulative_));
|
|
|
+ }
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpSendData(): sending " << amount << " bytes" << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // ... and send it. The amount sent is also passed as the first
|
|
|
+ // argument of the send callback, as a check.
|
|
|
+ socket->async_send(asio::buffer(send_ptr, amount),
|
|
|
+ boost::bind(&IOFetchTest::tcpSendHandler, this,
|
|
|
+ amount, socket, _1, _2));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// \brief Completion Handler for Sending TCP data
|
|
|
+ ///
|
|
|
+ /// Called when the asynchronous send of data back to the IOFetch object
|
|
|
+ /// by the TCP "server" in this class has completed. (This send has to
|
|
|
+ /// be asynchronous because control needs to return to the caller in order
|
|
|
+ /// for the IOService "run()" method to be called to run the handlers.)
|
|
|
+ ///
|
|
|
+ /// If not all the data has been sent, a short delay is instigated (during
|
|
|
+ /// which control returns to the IOService). This should force the queued
|
|
|
+ /// data to actually be sent and the IOFetch receive handler to be triggered.
|
|
|
+ /// In this way, the ability of IOFetch to handle fragmented TCP packets
|
|
|
+ /// should be checked.
|
|
|
+ ///
|
|
|
+ /// \param expected Number of bytes that were expected to have been sent.
|
|
|
+ /// \param socket Socket over which the send took place. Only used to
|
|
|
+ /// pass back to the send method.
|
|
|
+ /// \param ec Boost error code, value should be zero.
|
|
|
+ /// \param length Number of bytes sent.
|
|
|
+ void tcpSendHandler(size_t expected, tcp::socket* socket,
|
|
|
+ error_code ec = error_code(), size_t length = 0)
|
|
|
+ {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpSendHandler(): error = " << ec.value() <<
|
|
|
+ ", length = " << length << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ EXPECT_EQ(0, ec.value()); // Expect no error
|
|
|
+ EXPECT_EQ(expected, length); // And that amount sent is as expected
|
|
|
+
|
|
|
+ // Do we need to send more?
|
|
|
+ send_cumulative_ += length;
|
|
|
+ if (send_cumulative_ < send_buffer_.size()) {
|
|
|
+
|
|
|
+ // Yes - set up a timer: the callback handler for the timer is
|
|
|
+ // tcpSendData, which will then send the next chunk. We pass the
|
|
|
+ // socket over which data should be sent as an argument to that
|
|
|
+ // function.
|
|
|
+ timer_.expires_from_now(boost::posix_time::milliseconds(SEND_INTERVAL));
|
|
|
+ timer_.async_wait(boost::bind(&IOFetchTest::tcpSendData, this,
|
|
|
+ socket));
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/// \brief Fetch completion callback
|
|
|
///
|
|
|
/// This is the callback's operator() method which is called when the fetch
|
|
|
- /// is complete. Check that the data received is the wire format of the
|
|
|
- /// question, then send back an arbitrary response.
|
|
|
+ /// is complete. It checks that the data received is the wire format of the
|
|
|
+ /// data sent back by the server.
|
|
|
+ ///
|
|
|
+ /// \param result Result indicated by the callback
|
|
|
void operator()(IOFetch::Result result) {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "operator()(): result = " << result << endl;
|
|
|
+ }
|
|
|
+
|
|
|
EXPECT_EQ(expected_, result); // Check correct result returned
|
|
|
EXPECT_FALSE(run_); // Check it is run only once
|
|
|
run_ = true; // Note success
|
|
|
- service_.stop(); // ... and exit run loop
|
|
|
- }
|
|
|
|
|
|
- /// \brief Response handler, pretending to be remote DNS server
|
|
|
- ///
|
|
|
- /// This checks that the data sent is what we expected to receive, and
|
|
|
- /// sends back a test answer.
|
|
|
- void respond(udp::endpoint* remote, udp::socket* socket,
|
|
|
- asio::error_code ec = asio::error_code(), size_t length = 0) {
|
|
|
+ // If the expected result for SUCCESS, then this should have been called
|
|
|
+ // when one of the "servers" in this class has sent back return_data_.
|
|
|
+ // Check the data is as expected/
|
|
|
+ if (expected_ == IOFetch::SUCCESS) {
|
|
|
+ EXPECT_EQ(return_data_.size(), result_buff_->getLength());
|
|
|
|
|
|
- // Construct the data buffer for question we expect to receive.
|
|
|
- OutputBuffer msgbuf(512);
|
|
|
- Message msg(Message::RENDER);
|
|
|
- msg.setQid(0);
|
|
|
- msg.setOpcode(Opcode::QUERY());
|
|
|
- msg.setRcode(Rcode::NOERROR());
|
|
|
- msg.setHeaderFlag(Message::HEADERFLAG_RD);
|
|
|
- msg.addQuestion(question_);
|
|
|
- MessageRenderer renderer(msgbuf);
|
|
|
- msg.toWire(renderer);
|
|
|
+ const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
|
|
|
+ EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
|
|
|
+ }
|
|
|
|
|
|
- // The QID in the incoming data is random so set it to 0 for the
|
|
|
- // data comparison check. (It was set to 0 when the buffer containing
|
|
|
- // the expected data was constructed above.)
|
|
|
- server_buff_[0] = 0;
|
|
|
- server_buff_[1] = 0;
|
|
|
+ // ... and cause the run loop to exit.
|
|
|
+ service_.stop();
|
|
|
+ }
|
|
|
|
|
|
- // Check that lengths are identical.
|
|
|
- EXPECT_EQ(msgbuf.getLength(), length);
|
|
|
- EXPECT_TRUE(memcmp(msgbuf.getData(), &server_buff_[0], length) == 0);
|
|
|
+ // The next set of methods are the tests themselves. A number of the TCP
|
|
|
+ // and UDP tests are very similar.
|
|
|
|
|
|
- // ... and return a message back.
|
|
|
- socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA), *remote);
|
|
|
+ /// \brief Check for stop()
|
|
|
+ ///
|
|
|
+ /// Test that when we run the query and stop it after it was run, it returns
|
|
|
+ /// "stopped" correctly. (That is why stop() is posted to the service_ as
|
|
|
+ /// well instead of calling it.)
|
|
|
+ ///
|
|
|
+ /// \param protocol Test protocol
|
|
|
+ /// \param fetch Fetch object being tested
|
|
|
+ void stopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
|
|
|
+ protocol_ = protocol;
|
|
|
+ expected_ = IOFetch::STOPPED;
|
|
|
+
|
|
|
+ // Post the query
|
|
|
+ service_.get_io_service().post(fetch);
|
|
|
+
|
|
|
+ // Post query_.stop() (yes, the boost::bind thing is just
|
|
|
+ // query_.stop()).
|
|
|
+ service_.get_io_service().post(
|
|
|
+ boost::bind(&IOFetch::stop, fetch, IOFetch::STOPPED));
|
|
|
+
|
|
|
+ // Run both of them. run() returns when everything in the I/O service
|
|
|
+ // queue has completed.
|
|
|
+ service_.run();
|
|
|
+ EXPECT_TRUE(run_);
|
|
|
}
|
|
|
-};
|
|
|
|
|
|
+ /// \brief Premature stop test
|
|
|
+ ///
|
|
|
+ /// Test that when we queue the query to service_ and call stop() before it
|
|
|
+ /// gets executed, it acts sanely as well (eg. has the same result as
|
|
|
+ /// running stop() after - calls the callback).
|
|
|
+ ///
|
|
|
+ /// \param protocol Test protocol
|
|
|
+ /// \param fetch Fetch object being tested
|
|
|
+ void prematureStopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
|
|
|
+ protocol_ = protocol;
|
|
|
+ expected_ = IOFetch::STOPPED;
|
|
|
+
|
|
|
+ // Stop before it is started
|
|
|
+ fetch.stop();
|
|
|
+ service_.get_io_service().post(fetch);
|
|
|
+
|
|
|
+ service_.run();
|
|
|
+ EXPECT_TRUE(run_);
|
|
|
+ }
|
|
|
|
|
|
-/// Test that when we run the query and stop it after it was run,
|
|
|
-/// it returns "stopped" correctly.
|
|
|
-///
|
|
|
-/// That is why stop() is posted to the service_ as well instead
|
|
|
-/// of calling it.
|
|
|
-TEST_F(IOFetchTest, UdpStop) {
|
|
|
- expected_ = IOFetch::STOPPED;
|
|
|
+ /// \brief Timeout test
|
|
|
+ ///
|
|
|
+ /// Test that fetch times out when no answer arrives.
|
|
|
+ ///
|
|
|
+ /// \param protocol Test protocol
|
|
|
+ /// \param fetch Fetch object being tested
|
|
|
+ void timeoutTest(IOFetch::Protocol protocol, IOFetch& fetch) {
|
|
|
+ protocol_ = protocol;
|
|
|
+ expected_ = IOFetch::TIME_OUT;
|
|
|
+
|
|
|
+ service_.get_io_service().post(fetch);
|
|
|
+ service_.run();
|
|
|
+ EXPECT_TRUE(run_);
|
|
|
+ }
|
|
|
|
|
|
- // Post the query
|
|
|
- service_.get_io_service().post(udp_fetch_);
|
|
|
+ /// \brief Send/Receive Test
|
|
|
+ ///
|
|
|
+ /// Send a query to the server then receives a response.
|
|
|
+ ///
|
|
|
+ /// \param Test data to return to client
|
|
|
+ void tcpSendReturnTest(const std::string& return_data) {
|
|
|
+ if (debug_) {
|
|
|
+ cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
|
|
|
+ }
|
|
|
+ return_data_ = return_data;
|
|
|
+ protocol_ = IOFetch::TCP;
|
|
|
+ expected_ = IOFetch::SUCCESS;
|
|
|
+
|
|
|
+ // Socket into which the connection will be accepted.
|
|
|
+ tcp::socket socket(service_.get_io_service());
|
|
|
+
|
|
|
+ // Acceptor object - called when the connection is made, the handler
|
|
|
+ // will initiate a read on the socket.
|
|
|
+ tcp::acceptor acceptor(service_.get_io_service(),
|
|
|
+ tcp::endpoint(tcp::v4(), TEST_PORT));
|
|
|
+ acceptor.async_accept(socket,
|
|
|
+ boost::bind(&IOFetchTest::tcpAcceptHandler, this, &socket, _1));
|
|
|
+
|
|
|
+ // Post the TCP fetch object to send the query and receive the response.
|
|
|
+ service_.get_io_service().post(tcp_fetch_);
|
|
|
+
|
|
|
+ // ... and execute all the callbacks. This exits when the fetch
|
|
|
+ // completes.
|
|
|
+ service_.run();
|
|
|
+ EXPECT_TRUE(run_); // Make sure the callback did execute
|
|
|
+
|
|
|
+ // Tidy up
|
|
|
+ socket.close();
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
- // Post query_.stop() (yes, the boost::bind thing is just
|
|
|
- // query_.stop()).
|
|
|
- service_.get_io_service().post(
|
|
|
- boost::bind(&IOFetch::stop, udp_fetch_, IOFetch::STOPPED));
|
|
|
+// Check the protocol
|
|
|
+TEST_F(IOFetchTest, Protocol) {
|
|
|
+ EXPECT_EQ(IOFetch::UDP, udp_fetch_.getProtocol());
|
|
|
+ EXPECT_EQ(IOFetch::TCP, tcp_fetch_.getProtocol());
|
|
|
+}
|
|
|
|
|
|
- // Run both of them. run() returns when everything in the I/O service
|
|
|
- // queue has completed.
|
|
|
- service_.run();
|
|
|
- EXPECT_TRUE(run_);
|
|
|
+// UDP Stop test - see IOFetchTest::stopTest() header.
|
|
|
+TEST_F(IOFetchTest, UdpStop) {
|
|
|
+ stopTest(IOFetch::UDP, udp_fetch_);
|
|
|
}
|
|
|
|
|
|
-// Test that when we queue the query to service_ and call stop() before it gets
|
|
|
-// executed, it acts sanely as well (eg. has the same result as running stop()
|
|
|
-// after - calls the callback).
|
|
|
+// UDP premature stop test - see IOFetchTest::prematureStopTest() header.
|
|
|
TEST_F(IOFetchTest, UdpPrematureStop) {
|
|
|
- expected_ = IOFetch::STOPPED;
|
|
|
-
|
|
|
- // Stop before it is started
|
|
|
- udp_fetch_.stop();
|
|
|
- service_.get_io_service().post(udp_fetch_);
|
|
|
-
|
|
|
- service_.run();
|
|
|
- EXPECT_TRUE(run_);
|
|
|
+ prematureStopTest(IOFetch::UDP, udp_fetch_);
|
|
|
}
|
|
|
|
|
|
-// Test that it will timeout when no answer arrives.
|
|
|
+// UDP premature stop test - see IOFetchTest::timeoutTest() header.
|
|
|
TEST_F(IOFetchTest, UdpTimeout) {
|
|
|
- expected_ = IOFetch::TIME_OUT;
|
|
|
-
|
|
|
- service_.get_io_service().post(udp_fetch_);
|
|
|
- service_.run();
|
|
|
- EXPECT_TRUE(run_);
|
|
|
+ timeoutTest(IOFetch::UDP, udp_fetch_);
|
|
|
}
|
|
|
|
|
|
-// Test that it will succeed when we fake an answer and stores the same data we
|
|
|
-// send. This is done through a real socket on the loopback address.
|
|
|
-TEST_F(IOFetchTest, UdpReceive) {
|
|
|
+// UDP SendReceive test. Set up a UDP server then ports a UDP fetch object.
|
|
|
+// This will send question_ to the server and receive the answer back from it.
|
|
|
+TEST_F(IOFetchTest, UdpSendReceive) {
|
|
|
+ protocol_ = IOFetch::UDP;
|
|
|
expected_ = IOFetch::SUCCESS;
|
|
|
|
|
|
+ // Set up the server.
|
|
|
udp::socket socket(service_.get_io_service(), udp::v4());
|
|
|
socket.set_option(socket_base::reuse_address(true));
|
|
|
socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
|
|
|
+ return_data_ = "Message returned to the client";
|
|
|
|
|
|
udp::endpoint remote;
|
|
|
- socket.async_receive_from(asio::buffer(server_buff_),
|
|
|
+ socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
|
|
|
remote,
|
|
|
- boost::bind(&IOFetchTest::respond, this, &remote, &socket, _1, _2));
|
|
|
+ boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
|
|
|
+ _1, _2));
|
|
|
service_.get_io_service().post(udp_fetch_);
|
|
|
+ if (debug_) {
|
|
|
+ cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
|
|
|
+ endl;
|
|
|
+ }
|
|
|
service_.run();
|
|
|
|
|
|
socket.close();
|
|
|
|
|
|
- EXPECT_TRUE(run_);
|
|
|
- ASSERT_EQ(sizeof TEST_DATA, buff_->getLength());
|
|
|
- EXPECT_EQ(0, memcmp(TEST_DATA, buff_->getData(), sizeof TEST_DATA));
|
|
|
+ EXPECT_TRUE(run_);;
|
|
|
+}
|
|
|
+
|
|
|
+// Do the same tests for TCP transport
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpStop) {
|
|
|
+ stopTest(IOFetch::TCP, tcp_fetch_);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpPrematureStop) {
|
|
|
+ prematureStopTest(IOFetch::TCP, tcp_fetch_);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpTimeout) {
|
|
|
+ timeoutTest(IOFetch::TCP, tcp_fetch_);
|
|
|
+}
|
|
|
+
|
|
|
+// Test with values at or near 0, then at or near the chunk size (16 and 32
|
|
|
+// bytes, the sizes of the first two packets) then up to 65535. These are done
|
|
|
+// in separate tests because in practice a new IOFetch is created for each
|
|
|
+// query/response exchange and we don't want to confuse matters in the test
|
|
|
+// by running the test with an IOFetch that has already done one exchange.
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive0) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 0));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive1) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 1));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive15) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 15));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive16) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 16));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive17) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 17));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive31) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 31));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive32) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 32));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive33) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 33));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive4096) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 4096));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive8192) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 8192));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive16384) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 16384));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive32768) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 32768));
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(IOFetchTest, TcpSendReceive65535) {
|
|
|
+ tcpSendReturnTest(test_data_.substr(0, 65535));
|
|
|
}
|
|
|
|
|
|
} // namespace asiolink
|