hiredis/test/test-context.c
2012-08-14 09:28:51 -07:00

333 lines
9.2 KiB
C

#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "context.h"
#include "net-helper.h"
#include "object.h"
#include "test-helper.h"
#define SETUP_CONNECT() \
redis_context c; \
int rv; \
rv = redis_context_init(&c); \
assert_equal_return(rv, REDIS_OK); \
rv = redis_context_set_timeout(&c, 10000); \
assert_equal_return(rv, REDIS_OK);
TEST(connect_in_refused) {
SETUP_CONNECT();
redis_address addr = redis_address_in("127.0.0.1", redis_port() + 1);
rv = redis_context_connect_in(&c, addr.sa_addr.in);
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ECONNREFUSED);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_in6_refused) {
SETUP_CONNECT();
redis_address addr = redis_address_in6("::1", redis_port() + 1);
rv = redis_context_connect_in6(&c, addr.sa_addr.in6);
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ECONNREFUSED);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_un_noent) {
SETUP_CONNECT();
redis_address addr = redis_address_un("/tmp/idontexist.sock");
rv = redis_context_connect_un(&c, addr.sa_addr.un);
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ENOENT);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_timeout) {
SETUP_CONNECT();
redis_address addr = redis_address_in("10.255.255.254", redis_port());
long long t1, t2;
t1 = usec();
rv = redis_context_connect_in(&c, addr.sa_addr.in);
t2 = usec();
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
assert((t2 - t1) < 15000); /* 5ms of slack should be enough */
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_success) {
SETUP_CONNECT();
redis_address addr = redis_address_in("127.0.0.1", redis_port());
rv = redis_context_connect_in(&c, addr.sa_addr.in);
assert_equal_return(rv, REDIS_OK);
/* Address should be set when connection was made */
assert_equal_int(c.address.sa_family, AF_INET);
redis_context_destroy(&c);
}
TEST(connect_gai_unknown_host) {
SETUP_CONNECT();
rv = redis_context_connect_gai(&c, "idontexist.foo", redis_port());
assert_equal_return(rv, REDIS_EGAI);
/* Don't care about the specific error for now. */
assert(1);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_gai_timeout) {
SETUP_CONNECT();
long long t1, t2;
t1 = usec();
rv = redis_context_connect_gai(&c, "10.255.255.254", redis_port());
t2 = usec();
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
assert((t2 - t1) < 15000); /* 5ms of slack should be enough */
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_gai_success) {
SETUP_CONNECT();
rv = redis_context_connect_gai(&c, "localhost", redis_port());
assert_equal_return(rv, REDIS_OK);
/* Address should be set when connection was made */
assert_equal_int(c.address.sa_family, AF_INET);
redis_context_destroy(&c);
}
#define SETUP_CONNECTED() \
SETUP_CONNECT(); \
rv = redis_context_connect_gai(&c, "localhost", redis_port()); \
assert_equal_return(rv, REDIS_OK);
static redis_object *read_from_context(redis_context *ctx) {
redis_protocol *reply;
int rv;
rv = redis_context_read(ctx, &reply);
assert_equal_return(rv, REDIS_OK);
return (redis_object*)reply->data;
}
static void compare_ok_status(redis_object *obj) {
assert_equal_int(obj->type, REDIS_STATUS);
assert_equal_string(obj->str, "OK");
assert_equal_int(obj->len, 2);
}
static void compare_string(redis_object *obj, const char *str, size_t len) {
assert_equal_int(obj->type, REDIS_STRING);
assert_equal_string(obj->str, str);
assert_equal_int(obj->len, len);
}
TEST(write_command) {
SETUP_CONNECTED();
rv = redis_context_write_command(&c, "set foo bar");
assert_equal_return(rv, REDIS_OK);
compare_ok_status(read_from_context(&c));
/* Check that the command was executed as intended */
rv = redis_context_write_command(&c, "get foo");
assert_equal_return(rv, REDIS_OK);
compare_string(read_from_context(&c), "bar", 3);
redis_context_destroy(&c);
}
TEST(write_command_argv) {
SETUP_CONNECTED();
int argc1 = 3;
const char *argv1[] = { "set", "foo", "bar" };
size_t argvlen1[] = { 3, 3, 3 };
rv = redis_context_write_command_argv(&c, argc1, argv1, argvlen1);
assert_equal_return(rv, REDIS_OK);
compare_ok_status(read_from_context(&c));
/* Check that the command was executed as intended */
int argc2 = 2;
const char *argv2[] = { "get", argv1[1] };
size_t argvlen2[] = { 3, argvlen1[1] };
rv = redis_context_write_command_argv(&c, argc2, argv2, argvlen2);
assert_equal_return(rv, REDIS_OK);
compare_string(read_from_context(&c), argv1[2], argvlen1[2]);
redis_context_destroy(&c);
}
TEST(call_command) {
SETUP_CONNECTED();
redis_protocol *reply;
rv = redis_context_call_command(&c, &reply, "set foo bar");
assert_equal_return(rv, REDIS_OK);
compare_ok_status(reply->data);
/* Check that the command was executed as intended */
rv = redis_context_call_command(&c, &reply, "get foo");
assert_equal_return(rv, REDIS_OK);
compare_string(reply->data, "bar", 3);
redis_context_destroy(&c);
}
TEST(call_command_argv) {
SETUP_CONNECTED();
redis_protocol *reply;
int argc1 = 3;
const char *argv1[] = { "set", "foo", "bar" };
size_t argvlen1[] = { 3, 3, 3 };
rv = redis_context_call_command_argv(&c, &reply, argc1, argv1, argvlen1);
assert_equal_return(rv, REDIS_OK);
compare_ok_status(reply->data);
/* Check that the command was executed as intended */
int argc2 = 2;
const char *argv2[] = { "get", argv1[1] };
size_t argvlen2[] = { 3, argvlen1[1] };
rv = redis_context_call_command_argv(&c, &reply, argc2, argv2, argvlen2);
assert_equal_return(rv, REDIS_OK);
compare_string(reply->data, argv1[2], argvlen1[2]);
redis_context_destroy(&c);
}
void run_server_ignore_connection(int fd, void *data) {
((void) fd);
((void) data);
}
TEST(flush_against_full_kernel_buffer) {
SETUP_CONNECT();
redis_address addr = redis_address_in("127.0.0.1", redis_port() + 1);
run_server_args args;
args.address = addr;
args.fn.ptr = run_server_ignore_connection;
spawn(run_server, &args);
rv = redis_context_connect_in(&c, addr.sa_addr.in);
assert_equal_return(rv, REDIS_OK);
/* Now write and flush until error */
while (1) {
rv = redis_context_write_command(&c, "ping");
assert_equal_return(rv, REDIS_OK);
rv = redis_context_flush(&c);
if (rv != REDIS_OK) {
break;
}
}
/* When the write buffer cannot be flushed, the operation should time out
* instead of directly returning EAGAIN to the caller */
assert_equal_return(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
}
void run_server_close_after_accept(int fd, void *data) {
((void) data);
close(fd);
}
TEST(read_against_closed_connection) {
SETUP_CONNECT();
redis_address addr = redis_address_in("127.0.0.1", redis_port() + 1);
run_server_args args;
args.address = addr;
args.fn.ptr = run_server_close_after_accept;
spawn(run_server, &args);
rv = redis_context_connect_in(&c, addr.sa_addr.in);
assert_equal_return(rv, REDIS_OK);
redis_protocol *reply = NULL;
/* Read should immediately EOF */
rv = redis_context_read(&c, &reply);
assert_equal_return(rv, REDIS_EEOF);
/* ... and should be idempotent... */
rv = redis_context_read(&c, &reply);
assert_equal_return(rv, REDIS_EEOF);
redis_context_destroy(&c);
}
int main(void) {
test_connect_in_refused();
test_connect_in6_refused();
test_connect_un_noent();
test_connect_timeout();
test_connect_success();
test_connect_gai_unknown_host();
test_connect_gai_timeout();
test_connect_gai_success();
test_write_command();
test_write_command_argv();
test_call_command();
test_call_command_argv();
test_flush_against_full_kernel_buffer();
test_read_against_closed_connection();
}