[svn] commit: r3148 - /branches/vorner-sockcreator/src/bin/sockcreator/tests/sockcreator_tests.cc

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Oct 8 18:07:58 UTC 2010


Author: vorner
Date: Fri Oct  8 18:07:54 2010
New Revision: 3148

Log:
Tests for run

Modified:
    branches/vorner-sockcreator/src/bin/sockcreator/tests/sockcreator_tests.cc

Modified: branches/vorner-sockcreator/src/bin/sockcreator/tests/sockcreator_tests.cc
==============================================================================
--- branches/vorner-sockcreator/src/bin/sockcreator/tests/sockcreator_tests.cc (original)
+++ branches/vorner-sockcreator/src/bin/sockcreator/tests/sockcreator_tests.cc Fri Oct  8 18:07:54 2010
@@ -17,8 +17,12 @@
 #include <gtest/gtest.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
 #include <netinet/in.h>
+#include <unistd.h>
 #include <cstring>
+#include <cstdlib>
 #include <cerrno>
 
 using namespace isc::socket_creator;
@@ -108,4 +112,216 @@
     ASSERT_LT(get_sock(0, NULL, 0), 0);
 }
 
-}
+/*
+ * This creates a pipe, forks and feeds the pipe with given data.
+ * Used to provide the input in non-blocking/asynchronous way.
+ */
+pid_t
+provide_input(int *read_pipe, const char *input, const size_t length) {
+    int pipes[2];
+    if(pipe(pipes)) {
+        return -1;
+    }
+    *read_pipe = pipes[0];
+    pid_t pid(fork());
+    if(pid) { // We are in the parent
+        return pid;
+    } else { // This is in the child, just puth the data there
+        close(pipes[0]);
+        if(!write_data(pipes[1], input, length)) {
+            exit(1);
+        } else {
+            close(pipes[1]);
+            exit(0);
+        }
+    }
+}
+
+/*
+ * This creates a pipe, forks and reads the pipe and compares it
+ * with given data. Used to check output of run in asynchronous way.
+ */
+pid_t
+check_output(int *write_pipe, const char *output, const size_t length) {
+    int pipes[2];
+    if(pipe(pipes)) {
+        return -1;
+    }
+    *write_pipe = pipes[1];
+    pid_t pid(fork());
+    if(pid) { // We are in parent
+        return pid;
+    } else {
+        close(pipes[1]);
+        char buffer[length + 1];
+        // Try to read one byte more to see if the output ends here
+        if(read_data(pipes[0], buffer, length + 1) != length)
+            exit(1);
+        // Normalize the return value - return 0 on equality, 1 on nonequality
+        exit(!!memcmp(buffer, output, length));
+    }
+}
+
+/*
+ * Waits for pid to terminate and checks it terminates successfully (with 0).
+ */
+bool
+process_ok(pid_t process) {
+    int status;
+    // Make sure it does terminate when the output is shorter than expected
+    /*
+     * FIXME: if the timeout is reached, this kill the whole test, not just
+     * the waitpid call. Should have signal handler. This is no show-stopper,
+     * but we might have another tests to run.
+     */
+    alarm(3);
+    if(waitpid(process, &status, 0) == -1) {
+        if(errno == EINTR)
+            kill(process, SIGTERM);
+        return false;
+    }
+    return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+}
+
+/*
+ * Helper functions to pass to run during testing.
+ */
+int
+get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t addr_len)
+{
+    int result(0);
+    int port;
+    /*
+     * We encode the type and address family into the int and return it.
+     * Lets ignore the port and address for now
+     * First bit is 1 if it is known type. Second tells if TCP or UDP.
+     * The familly is similar - third bit is known address family,
+     * the fourth is the family.
+     */
+    switch(type) {
+        case SOCK_STREAM:
+            result += 1;
+            break;
+        case SOCK_DGRAM:
+            result += 3;
+            break;
+    }
+    switch(addr->sa_family) {
+        case AF_INET:
+            result += 4;
+            port = static_cast<struct sockaddr_in *>(
+                static_cast<void *>(addr))->sin_port;
+            break;
+        case AF_INET6:
+            result += 12;
+            port = static_cast<struct sockaddr_in6 *>(
+                static_cast<void *>(addr))->sin6_port;
+            break;
+    }
+    /*
+     * The port should be 0xffff. If it's not, we change the result.
+     * The port of 0xbbbb means bind should fail and 0xcccc means
+     * socket should fail.
+     */
+    if(port != 0xffff) {
+        errno = 0;
+        if(port == 0xbbbb) {
+            return -2;
+        } else if(port == 0xcccc) {
+            return -1;
+        } else {
+            result += 16;
+        }
+    }
+    return result;
+}
+
+int
+send_fd_dummy(const int destination, const int what)
+{
+    /*
+     * Make sure it is 1 byte so we know the length. We do not use more during
+     * the test anyway.
+     */
+    char fd_data(what);
+    if(!write_data(destination, &fd_data, 1))
+        return -1;
+}
+
+/*
+ * Generic test that it works, with various inputs and outputs.
+ * It uses different functions to create the socket and send it and pass
+ * data to it and check it returns correct data back, to see if the run()
+ * parses the commands correctly.
+ */
+void run_test(const char *input_data, const size_t input_size,
+    const char *output_data, const size_t output_size,
+    bool should_succeed = true)
+{
+    // Prepare the input feeder and output checker processes
+    int input_fd, output_fd;
+    pid_t input(provide_input(&input_fd, input_data, input_size)),
+        output(check_output(&output_fd, output_data, output_size));
+    ASSERT_NE(-1, input) << "Couldn't start input feeder";
+    ASSERT_NE(-1, output) << "Couldn't start output checker";
+    // Run the body
+    int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
+    if(should_succeed) {
+        EXPECT_EQ(0, result);
+    } else {
+        EXPECT_NE(0, result);
+    }
+    // Check the subprocesses say everything is OK too
+    EXPECT_TRUE(process_ok(input));
+    EXPECT_TRUE(process_ok(output));
+}
+
+/*
+ * Check it terminates successfully when asked to.
+ */
+TEST(run, terminate) {
+    run_test("T", 1, NULL, 0);
+}
+
+/*
+ * Check it rejects incorrect input.
+ */
+TEST(run, bad_input) {
+    run_test("XXX", 3, "FI", 2, false);
+}
+
+/*
+ * Check it correctly parses queries to create sockets.
+ */
+TEST(run, sockets) {
+    run_test(
+        "SU4\xff\xff\0\0\0\0" // This has 9 bytes
+        "ST4\xff\xff\0\0\0\0" // This has 9 bytes
+        "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+        "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+        "T", 61,
+        "S\x07S\x05S\x0dS\x0f", 8);
+}
+
+/*
+ * Check if failures of get_socket are handled correctly.
+ */
+TEST(run, bad_sockets) {
+    // We need to construct the answer, but it depends on int length.
+    size_t int_len(sizeof(int));
+    size_t result_len(4 + 2 * int_len);
+    char result[result_len];
+    // Both errno parts should be 0
+    memset(result, 0, result_len);
+    // Fill the 2 control parts
+    strcpy(result, "EB");
+    strcpy(result + 2 + int_len, "ES");
+    // Run the test
+    run_test(
+        "SU4\xbb\xbb\0\0\0\0"
+        "SU4\xcc\xcc\0\0\0\0"
+        "T", 19,
+        result, result_len);
+}
+
+}




More information about the bind10-changes mailing list