Commit 6b09fa3b authored by Michal Vaner's avatar Michal Vaner
Browse files

Tests for run

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/vorner-sockcreator@3148 e5f2f494-b856-4b98-b285-d166d9295462
parent 1f7ffbad
......@@ -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 @@ TEST(get_sock, fail_with_nonsense) {
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);
}
}
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