Commit aa1b9c12 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Test write timeout on context

parent 300b3d32
...@@ -129,9 +129,22 @@ int redis_context_flush(redis_context *ctx) { ...@@ -129,9 +129,22 @@ int redis_context_flush(redis_context *ctx) {
while (!drained) { while (!drained) {
rv = redis_handle_write_from_buffer(&ctx->handle, &drained); rv = redis_handle_write_from_buffer(&ctx->handle, &drained);
if (rv == REDIS_OK) {
continue;
}
/* Wait for the socket to become writable on EAGAIN */
if (rv == REDIS_ESYS && errno == EAGAIN) {
rv = redis_handle_wait_writable(&ctx->handle);
if (rv != REDIS_OK) { if (rv != REDIS_OK) {
return rv; return rv;
} }
continue;
}
return rv;
} }
return REDIS_OK; return REDIS_OK;
......
include ../Makefile.common include ../Makefile.common
TESTS=test-format test-parser test-object test-handle test-context TESTS=test-format test-parser test-object test-handle test-context
OBJ=spawn.o net-helper.o
all: $(TESTS) all: $(TESTS)
../$(STLIBNAME): ../$(STLIBNAME):
cd .. && $(MAKE) $(STLIBNAME) cd .. && $(MAKE) $(STLIBNAME)
test-%: test-%.o ../$(STLIBNAME) test-%: test-%.o $(OBJ) ../$(STLIBNAME)
$(CC) -o $@ $(REAL_LDFLAGS) $^ $(CC) -o $@ $(REAL_LDFLAGS) $^
.c.o: .c.o:
......
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include "net-helper.h"
void accept_and_ignore(void *ptr) {
accept_and_ignore_args *args = ptr;
int fd, rv;
fd = socket(args->address.sa_family, SOCK_STREAM, 0);
if (fd == -1) {
fprintf(stderr, "socket: %s\n", strerror(errno));
exit(1);
}
rv = bind(fd, &args->address.sa_addr.addr, args->address.sa_addrlen);
if (rv == -1) {
fprintf(stderr, "bind: %s\n", strerror(errno));
exit(1);
}
rv = listen(fd, 128);
if (rv == -1) {
fprintf(stderr, "listen: %s\n", strerror(errno));
exit(1);
}
while (1) {
redis_address remote;
remote.sa_addrlen = sizeof(remote.sa_addr);
rv = accept(fd, &remote.sa_addr.addr, &remote.sa_addrlen);
if (rv == -1) {
fprintf(stderr, "accept: %s\n", strerror(errno));
exit(1);
}
/* Ignore the new connection */
}
}
#ifndef TEST_NET_HELPER
#define TEST_NET_HELPER 1
#include "../handle.h"
typedef struct accept_and_ignore_args_s accept_and_ignore_args;
struct accept_and_ignore_args_s {
redis_address address;
};
void accept_and_ignore(void *ptr);
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <assert.h>
#include "spawn.h"
int pidc = 0;
pid_t *pidv = NULL;
void spawn_init(void) {
assert(pidc == 0);
assert(pidv == NULL);
}
void spawn_destroy(void) {
pid_t pid;
int i;
int rv;
/* Kill children */
for (i = 0; i < pidc; i++) {
int status;
pid = waitpid(pidv[i], &status, WNOHANG);
if (pid) {
continue;
}
rv = kill(pidv[i], SIGKILL);
if (rv == -1) {
fprintf(stderr, "kill: %s\n", strerror(errno));
exit(1);
}
pid = waitpid(pidv[i], &status, 0);
assert(pid == pidv[i]);
}
free(pidv);
pidc = 0;
pidv = NULL;
}
void spawn(spawn_function *fn, void *ptr) {
pid_t pid;
pid = fork();
if (pid) {
pidv = realloc(pidv, sizeof(pid_t) * (pidc + 1));
pidv[pidc++] = pid;
} else {
fn(ptr);
exit(0);
}
/* Give the fork some time to boot... */
usleep(1000);
}
#ifndef TEST_SPAWN_H
#define TEST_SPAWN_H 1
typedef void (spawn_function)(void *);
void spawn_init(void);
void spawn_destroy(void);
void spawn(spawn_function *fn, void *ptr);
#endif
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "../context.h" #include "../context.h"
#include "../object.h" #include "../object.h"
#include "test-helper.h" #include "test-helper.h"
#include "net-helper.h"
#define SETUP_CONNECT() \ #define SETUP_CONNECT() \
redis_context c; \ redis_context c; \
...@@ -32,6 +33,7 @@ TEST(connect_in_refused) { ...@@ -32,6 +33,7 @@ TEST(connect_in_refused) {
SETUP_CONNECT(); SETUP_CONNECT();
struct sockaddr_in sa; struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET; sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port() + 1); sa.sin_port = htons(redis_port() + 1);
assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1); assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1);
...@@ -50,6 +52,7 @@ TEST(connect_in6_refused) { ...@@ -50,6 +52,7 @@ TEST(connect_in6_refused) {
SETUP_CONNECT(); SETUP_CONNECT();
struct sockaddr_in6 sa; struct sockaddr_in6 sa;
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6; sa.sin6_family = AF_INET6;
sa.sin6_port = htons(redis_port() + 1); sa.sin6_port = htons(redis_port() + 1);
assert(inet_pton(AF_INET6, "::1", &sa.sin6_addr) == 1); assert(inet_pton(AF_INET6, "::1", &sa.sin6_addr) == 1);
...@@ -68,6 +71,7 @@ TEST(connect_un_noent) { ...@@ -68,6 +71,7 @@ TEST(connect_un_noent) {
SETUP_CONNECT(); SETUP_CONNECT();
struct sockaddr_un sa; struct sockaddr_un sa;
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_LOCAL; sa.sun_family = AF_LOCAL;
strcpy((char*)&sa.sun_path, "/tmp/idontexist.sock"); strcpy((char*)&sa.sun_path, "/tmp/idontexist.sock");
...@@ -85,6 +89,7 @@ TEST(connect_timeout) { ...@@ -85,6 +89,7 @@ TEST(connect_timeout) {
SETUP_CONNECT(); SETUP_CONNECT();
struct sockaddr_in sa; struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET; sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port()); sa.sin_port = htons(redis_port());
assert(inet_pton(AF_INET, "10.255.255.254", &sa.sin_addr) == 1); assert(inet_pton(AF_INET, "10.255.255.254", &sa.sin_addr) == 1);
...@@ -108,6 +113,7 @@ TEST(connect_success) { ...@@ -108,6 +113,7 @@ TEST(connect_success) {
SETUP_CONNECT(); SETUP_CONNECT();
struct sockaddr_in sa; struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET; sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port()); sa.sin_port = htons(redis_port());
assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1); assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1);
...@@ -269,6 +275,42 @@ TEST(call_command_argv) { ...@@ -269,6 +275,42 @@ TEST(call_command_argv) {
redis_context_destroy(&c); redis_context_destroy(&c);
} }
TEST(flush_against_full_kernel_buffer) {
SETUP_CONNECT();
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port() + 1);
assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1);
accept_and_ignore_args args;
args.address.sa_family = sa.sin_family;
args.address.sa_addrlen = sizeof(sa);
args.address.sa_addr.in = sa;
spawn(accept_and_ignore, &args);
rv = redis_context_connect_in(&c, sa);
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_int(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
}
int main(void) { int main(void) {
test_connect_in_refused(); test_connect_in_refused();
test_connect_in6_refused(); test_connect_in6_refused();
...@@ -283,4 +325,5 @@ int main(void) { ...@@ -283,4 +325,5 @@ int main(void) {
test_write_command_argv(); test_write_command_argv();
test_call_command(); test_call_command();
test_call_command_argv(); test_call_command_argv();
test_flush_against_full_kernel_buffer();
} }
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <netdb.h> #include <netdb.h>
/* local */ /* local */
#include "spawn.h"
#include "../handle.h" #include "../handle.h"
/******************************************************************************/ /******************************************************************************/
...@@ -103,7 +104,9 @@ static const char *current_test = NULL; ...@@ -103,7 +104,9 @@ static const char *current_test = NULL;
static void test__##name(void); \ static void test__##name(void); \
static void test_##name(void) { \ static void test_##name(void) { \
current_test = #name; \ current_test = #name; \
spawn_init(); \
test__##name(); \ test__##name(); \
spawn_destroy(); \
printf("%s: PASSED\n", current_test); \ printf("%s: PASSED\n", current_test); \
} \ } \
static void test__##name(void) static void test__##name(void)
......
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