Commit 48134262 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

added tests for specific addresses and "-4/-6" cases.

the tests identified bugs in my previous refactoring, which were fixed.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac221b@2474 e5f2f494-b856-4b98-b285-d166d9295462
parent 8f5c79ba
......@@ -453,7 +453,7 @@ private:
class IOServiceImpl {
public:
IOServiceImpl(AuthSrv* auth_server, const char& port,
const ip::address& v4addr, const ip::address& v6addr);
const ip::address* v4addr, const ip::address* v6addr);
asio::io_service io_service_;
AuthSrv* auth_server_;
......@@ -469,8 +469,8 @@ public:
};
IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char& port,
const ip::address& v4addr,
const ip::address& v6addr) :
const ip::address* const v4addr,
const ip::address* const v6addr) :
auth_server_(auth_server),
udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
......@@ -485,17 +485,17 @@ IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char& port,
}
try {
if (v4addr.is_v4()) {
if (v4addr != NULL) {
udp4_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
v4addr, portnum));
*v4addr, portnum));
tcp4_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
v4addr, portnum));
*v4addr, portnum));
}
if (v6addr.is_v6()) {
if (v6addr != NULL) {
udp6_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
v6addr, portnum));
*v6addr, portnum));
tcp6_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
v6addr, portnum));
*v6addr, portnum));
}
} catch (const asio::system_error& err) {
// We need to catch and convert any ASIO level exceptions.
......@@ -518,19 +518,19 @@ IOService::IOService(AuthSrv* auth_server, const char& port,
}
impl_ = new IOServiceImpl(auth_server, port,
addr.is_v4() ? addr : ip::address::address(),
addr.is_v6() ? addr : ip::address::address());
addr.is_v4() ? &addr : NULL,
addr.is_v6() ? &addr : NULL);
}
IOService::IOService(AuthSrv* auth_server, const char& port,
const bool use_ipv4, const bool use_ipv6) :
impl_(NULL)
{
const ip::address v4addr = use_ipv4 ? ip::address(ip::address_v4::any()) :
ip::address::address();
const ip::address v6addr = use_ipv6 ? ip::address(ip::address_v6::any()) :
ip::address::address();
impl_ = new IOServiceImpl(auth_server, port, v4addr, v6addr);
const ip::address v4addr_any = ip::address(ip::address_v4::any());
const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
const ip::address v6addr_any = ip::address(ip::address_v6::any());
const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
impl_ = new IOServiceImpl(auth_server, port, v4addrp, v6addrp);
}
IOService::~IOService() {
......
......@@ -380,6 +380,9 @@ class IOService {
///
/// \name Constructors and Destructor
///
/// These are currently very specific to the authoritative server
/// implementation.
///
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private, making this class non-copyable.
//@{
......@@ -387,9 +390,15 @@ private:
IOService(const IOService& source);
IOService& operator=(const IOService& source);
public:
/// \brief The constructor. Currently very specific to the authoritative
/// server implementation.
IOService(AuthSrv* auth_server, const char& address, const char& port);
/// \brief The constructor with a specific IP address and port on which
/// the services listen on.
IOService(AuthSrv* auth_server, const char& port, const char& address);
/// \brief The constructor with a specific port on which the services
/// listen on.
///
/// It effectively listens on "any" IPv4 and/or IPv6 addresses.
/// IPv4/IPv6 services will be available if and only if \c use_ipv4
/// or \c use_ipv6 is \c true, respectively.
IOService(AuthSrv* auth_server, const char& port,
const bool use_ipv4, const bool use_ipv6);
/// \brief The destructor.
......
......@@ -156,6 +156,18 @@ resolveAddress(const int family, const int sock_type, const int protocol) {
return (res);
}
// This fixture is a framework for various types of network operations
// using the ASIO interfaces. Each test case creates an IOService object,
// opens a local "client" socket for testing, sends data via the local socket
// to the service that would run in the IOService object.
// A mock callback function (an ASIOCallBack object) is registered with the
// IOService object, so the test code should be able to examine the data
// receives on the server side. It then checks the received data matches
// expected parameters.
// If initialization parameters of the IOService should be modified, the test
// case can do it using the setIOService() method.
// Note: the set of tests in ASIOLinkTest use actual network services and may
// involve undesirable side effect such as blocking.
class ASIOLinkTest : public ::testing::Test {
protected:
ASIOLinkTest();
......@@ -166,6 +178,7 @@ protected:
if (sock_ != -1) {
close(sock_);
}
delete io_service_;
}
void sendUDP(const int family) {
res_ = resolveAddress(family, SOCK_DGRAM, IPPROTO_UDP);
......@@ -179,7 +192,7 @@ protected:
if (cc != sizeof(test_data)) {
isc_throw(IOError, "unexpected sendto result: " << cc);
}
io_service_.run();
io_service_->run();
}
void sendTCP(const int family) {
res_ = resolveAddress(family, SOCK_STREAM, IPPROTO_TCP);
......@@ -195,9 +208,54 @@ protected:
if (cc != sizeof(test_data)) {
isc_throw(IOError, "unexpected sendto result: " << cc);
}
io_service_.run();
io_service_->run();
}
public:
void setIOService(const char& address) {
delete io_service_;
io_service_ = NULL;
io_service_ = new IOService(NULL, *TEST_PORT, address);
io_service_->setCallBack(ASIOCallBack(this));
}
void setIOService(const bool use_ipv4, const bool use_ipv6) {
delete io_service_;
io_service_ = NULL;
io_service_ = new IOService(NULL, *TEST_PORT, use_ipv4, use_ipv6);
io_service_->setCallBack(ASIOCallBack(this));
}
void doTest(const int family, const int protocol) {
if (protocol == IPPROTO_UDP) {
sendUDP(family);
} else {
sendTCP(family);
}
// There doesn't seem to be an effective test for the validity of
// 'native'.
// One thing we are sure is it must be different from our local socket.
EXPECT_NE(sock_, callback_native_);
EXPECT_EQ(protocol, callback_protocol_);
EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
callback_address_);
const uint8_t* expected_data =
protocol == IPPROTO_UDP ? test_data : test_data + 2;
const size_t expected_datasize =
protocol == IPPROTO_UDP ? sizeof(test_data) :
sizeof(test_data) - 2;
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
callback_data_.size(),
expected_data, expected_datasize);
}
private:
class ASIOCallBack : public std::unary_function<IOMessage, void> {
public:
ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
void operator()(const IOMessage& io_message) const {
test_obj_->callBack(io_message);
}
private:
ASIOLinkTest* test_obj_;
};
void callBack(const IOMessage& io_message) {
callback_protocol_ = io_message.getSocket().getProtocol();
callback_native_ = io_message.getSocket().getNative();
......@@ -207,10 +265,10 @@ public:
static_cast<const uint8_t*>(io_message.getData()),
static_cast<const uint8_t*>(io_message.getData()) +
io_message.getDataSize());
io_service_.stop();
io_service_->stop();
}
protected:
IOService io_service_;
IOService* io_service_;
int callback_protocol_;
int callback_native_;
string callback_address_;
......@@ -220,61 +278,74 @@ private:
struct addrinfo* res_;
};
class ASIOCallBack : public std::unary_function<IOMessage, void> {
public:
ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
void operator()(const IOMessage& io_message) const {
test_obj_->callBack(io_message);
}
private:
ASIOLinkTest* test_obj_;
};
ASIOLinkTest::ASIOLinkTest() :
io_service_(NULL, *TEST_PORT, true, true),
sock_(-1), res_(NULL)
io_service_(NULL), sock_(-1), res_(NULL)
{
io_service_.setCallBack(ASIOCallBack(this));
setIOService(true, true);
}
TEST_F(ASIOLinkTest, v6UDPSend) {
sendUDP(AF_INET6);
// There doesn't seem to be an effective test for the validity of 'native'.
// One thing we are sure is it must be different from our local socket.
EXPECT_NE(callback_native_, sock_);
EXPECT_EQ(IPPROTO_UDP, callback_protocol_);
EXPECT_EQ(TEST_IPV6_ADDR, callback_address_);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
callback_data_.size(), test_data, sizeof(test_data));
doTest(AF_INET6, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v6TCPSend) {
sendTCP(AF_INET6);
EXPECT_NE(callback_native_, sock_);
EXPECT_EQ(IPPROTO_TCP, callback_protocol_);
EXPECT_EQ(TEST_IPV6_ADDR, callback_address_);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
callback_data_.size(),
test_data + 2, sizeof(test_data) - 2);
doTest(AF_INET6, IPPROTO_TCP);
}
TEST_F(ASIOLinkTest, v4UDPSend) {
sendUDP(AF_INET);
EXPECT_NE(callback_native_, sock_);
EXPECT_EQ(IPPROTO_UDP, callback_protocol_);
EXPECT_EQ(TEST_IPV4_ADDR, callback_address_);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
callback_data_.size(), test_data, sizeof(test_data));
doTest(AF_INET, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v4TCPSend) {
sendTCP(AF_INET);
EXPECT_NE(callback_native_, sock_);
EXPECT_EQ(IPPROTO_TCP, callback_protocol_);
EXPECT_EQ(TEST_IPV4_ADDR, callback_address_);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
callback_data_.size(),
test_data + 2, sizeof(test_data) - 2);
doTest(AF_INET, IPPROTO_TCP);
}
TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
// Explicitly set a specific address to be bound to the socket.
// The subsequent test does not directly ensures the underlying socket
// is bound to the expected address, but the success of the tests should
// reasonably suggest it works as intended.
// Specifying an address also implicitly means the service runs in a
// single address-family mode. In tests using TCP we can confirm that
// by trying to make a connection and seeing a failure. In UDP, it'd be
// more complicated because we need to use a connected socket and catch
// an error on a subsequent read operation. We could do it, but for
// simplicity we only tests the easier cases for now.
setIOService(*TEST_IPV6_ADDR);
doTest(AF_INET6, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v6TCPSendSpecific) {
setIOService(*TEST_IPV6_ADDR);
doTest(AF_INET6, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET), IOError);
}
TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
setIOService(*TEST_IPV4_ADDR);
doTest(AF_INET, IPPROTO_UDP);
}
TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
setIOService(*TEST_IPV4_ADDR);
doTest(AF_INET, IPPROTO_TCP);
EXPECT_THROW(sendTCP(AF_INET6), IOError);
}
TEST_F(ASIOLinkTest, v6TCPOnly) {
// Open only IPv6 TCP socket. A subsequent attempt of establishing an
// IPv4/TCP connection should fail. See above for why we only test this
// for TCP.
setIOService(false, true);
EXPECT_THROW(sendTCP(AF_INET), IOError);
}
TEST_F(ASIOLinkTest, v4TCPOnly) {
setIOService(true, false);
EXPECT_THROW(sendTCP(AF_INET6), IOError);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment