Browse Source

[1593] Replace macros in test code with temmplates/overloaded functions

Stephen Morris 13 years ago
parent
commit
aa39c499c1
1 changed files with 127 additions and 78 deletions
  1. 127 78
      src/bin/sockcreator/tests/sockcreator_tests.cc

+ 127 - 78
src/bin/sockcreator/tests/sockcreator_tests.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  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
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <iostream>
+
 #include "../sockcreator.h"
 
 #include <util/unittests/fork.h>
@@ -29,96 +31,143 @@ using namespace isc::socket_creator;
 using namespace isc::util::unittests;
 using namespace isc::util::io;
 
+// The tests check both TCP and UDP sockets on IPv4 and IPv6.
+//
+// Essentially we need to check all four combinations of TCP/UDP and IPv4/IPv6.
+// The different address families (IPv4/IPv6) require different structures to
+// hold the address information, and so some common code is in the form of
+// templates (or overloads), parameterised on the structure type.
+//
+// The protocol is determined by an integer (SOCK_STREAM or SOCK_DGRAM) so
+// cannot be templated in the same way.  Relevant check functions are
+// selected manually.
+
 namespace {
 
-/*
- * Generic version of the creation of socket test. It just tries to
- * create the socket and checks the result is not negative (eg.
- * it is valid descriptor) and that it can listen.
- *
- * This is a macro so ASSERT_* does abort the TEST, not just the
- * function inside.
- */
-#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
-    ADDR_SET, CHECK_SOCK) \
-    do { \
-        /*
-         * This should create an address that binds on all interfaces
-         * and lets the OS choose a free port.
-         */ \
-        struct ADDR_TYPE addr; \
-        memset(&addr, 0, sizeof addr); \
-        ADDR_SET(addr); \
-        addr.FAMILY_FIELD = ADDR_FAMILY; \
-        struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
-            static_cast<void *>(&addr)); \
-        \
-        int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
-        /* Provide even nice error message. */ \
-        ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
-            #SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
-            << socket << " and error " << strerror(errno); \
-        CHECK_SOCK(ADDR_TYPE, socket); \
-        int on; \
-        socklen_t len(sizeof(on)); \
-        EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &on, &len));\
-        EXPECT_NE(0, on); \
-        if (ADDR_FAMILY == AF_INET6) { \
-            EXPECT_EQ(0, getsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &on, \
-                                    &len)); \
-            EXPECT_NE(0, on); \
-        } \
-        EXPECT_EQ(0, close(socket)); \
-    } while (0)
-
-// Just helper macros
-#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
-#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_loopback; } while (0)
-// If the get_sock returned something useful, listen must work
-#define TCP_CHECK(UNUSED, SOCKET) do { \
-        EXPECT_EQ(0, listen(SOCKET, 1)); \
-    } while (0)
-// More complicated with UDP, so we send a packet to ourselfs and se if it
-// arrives
-#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
-        struct ADDR_TYPE addr; \
-        memset(&addr, 0, sizeof addr); \
-        struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
-            static_cast<void *>(&addr)); \
-        \
-        socklen_t len = sizeof addr; \
-        ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
-        ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)) << \
-            "Send failed with error " << strerror(errno) << " on socket " << \
-            SOCKET; \
-        char buffer[5]; \
-        ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)) << \
-            "Recv failed with error " << strerror(errno) << " on socket " << \
-            SOCKET; \
-        EXPECT_STREQ("test", buffer); \
-    } while (0)
+// Set IP-version-specific fields.
 
-/*
- * Several tests to ensure we can create the sockets.
- */
+void
+setAddressFamilyFields(sockaddr_in* address) {
+    address->sin_family = AF_INET;
+    address->sin_addr.s_addr = INADDR_ANY;
+}
+
+void
+setAddressFamilyFields(sockaddr_in6* address) {
+    address->sin6_family = AF_INET6;
+    address->sin6_addr = in6addr_loopback;
+}
+
+// Socket has been opened, peform a check on it.  The sole argument is the
+// socket descriptor.  The TCP check is the same regardless of the address
+// family.  The UDP check requires that the socket address be obtained so
+// is parameterised on the type of structure required to hold the address.
+
+void
+tcpCheck(const int socknum) {
+    // Sufficient to be able to listen on the socket.
+    EXPECT_EQ(0, listen(socknum, 1));
+}
+
+template <typename ADDRTYPE>
+void
+udpCheck(const int socknum) {
+    // UDP testing is more complicated than TCP: send a packet to ourselves and
+    // see if it arrives.
+
+    // Get details of the socket so that we can use it as the target of the
+    // sendto().
+    ADDRTYPE addr;
+    memset(&addr, 0, sizeof(addr));
+    sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
+    socklen_t len = sizeof(addr);
+    ASSERT_EQ(0, getsockname(socknum, addr_ptr, &len));
+
+    // Send the packet to ourselves and check we receive it.
+    ASSERT_EQ(5, sendto(socknum, "test", 5, 0, addr_ptr, sizeof(addr))) <<
+        "Send failed with error " << strerror(errno) << " on socket " <<
+        socknum;
+    char buffer[5];
+    ASSERT_EQ(5, recv(socknum, buffer, 5, 0)) <<
+        "Recv failed with error " << strerror(errno) << " on socket " <<
+        socknum;
+    EXPECT_STREQ("test", buffer);
+}
+
+// The check function (tcpCheck/udpCheck) is passed as a parameter to the test
+// code, so provide a conveniet typedef.
+typedef void (*socket_check_t)(const int);
+
+// Address-family-specific scoket checks.
+//
+// The first argument is used to select the overloaded check function.
+// The other argument is the socket descriptor number.
+
+// IPv4 check
+void addressFamilySpecificCheck(const sockaddr_in*, const int) {
+};
+
+// IPv6 check
+void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum) {
+    int options;
+    socklen_t len = sizeof(options);
+    EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options, &len));
+    EXPECT_NE(0, options);
+};
+
+
+// Generic version of the socket test.  It creates the socket and checks that
+// it is a valid descriptor.  The family-specific check functions are called
+// to check that the socket is valid.  The function is parameterised according
+// to the structure used to hold the address.
+//
+// Arguments:
+// socket_type Type of socket to create (SOCK_DGRAM or SOCK_STREAM)
+// socket_check Associated check function - udpCheck() or tcpCheck()
+template <typename ADDRTYPE>
+void testAnyCreate(int socket_type, socket_check_t socket_check) {
+
+    // Create the socket.
+    ADDRTYPE addr;
+    memset(&addr, 0, sizeof(addr));
+    setAddressFamilyFields(&addr);
+    sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
+    int socket = get_sock(socket_type, addr_ptr, sizeof(addr));
+    ASSERT_GE(socket, 0) << "Couldn't create socket: failed with " <<
+        "return code " << socket << " and error " << strerror(errno);
+
+    // Perform socket-type-specific testing.
+    socket_check(socket);
+
+    // Do address-family-independent
+    int options;
+    socklen_t len = sizeof(options);
+    EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &options, &len));
+    EXPECT_NE(0, options);
+
+    // ...and the address-family specific tests.
+    addressFamilySpecificCheck(&addr, socket);
+
+    // Tidy up and exit.
+    EXPECT_EQ(0, close(socket));
+}
+
+
+// Several tests to ensure we can create the sockets.
 TEST(get_sock, udp4_create) {
-    TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
-        UDP_CHECK);
+    testAnyCreate<sockaddr_in>(SOCK_DGRAM, udpCheck<sockaddr_in>);
 }
 
 TEST(get_sock, tcp4_create) {
-    TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
-        TCP_CHECK);
+    testAnyCreate<sockaddr_in>(SOCK_STREAM, tcpCheck);
 }
 
 TEST(get_sock, udp6_create) {
-    TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
-        IN6ADDR_SET, UDP_CHECK);
+    testAnyCreate<sockaddr_in6>(SOCK_DGRAM, udpCheck<sockaddr_in6>);
 }
 
 TEST(get_sock, tcp6_create) {
-    TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
-        IN6ADDR_SET, TCP_CHECK);
+    testAnyCreate<sockaddr_in6>(SOCK_STREAM, tcpCheck);
 }
 
 /*