From 7abcaebdc4c3dd11b779eaef58a7371fb82ae888 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Fri, 1 Jul 2011 09:49:58 -0700 Subject: [PATCH] Updating test framework for startup/shutdown of memcached. --- .bzrignore | 4 + libmemcached/assert.hpp | 57 ++++++ libmemcached/common.h | 12 +- libmemcached/connect.cc | 12 +- libmemcached/error.cc | 79 +++++++- libmemcached/error.h | 9 +- libmemcached/error.hpp | 9 + libmemcached/hosts.cc | 13 +- libmemcached/include.am | 1 + libmemcached/io.cc | 9 +- libmemcached/memcached.cc | 2 +- libmemcached/memcached_util.h | 1 + libmemcached/response.cc | 25 +-- libmemcached/server.cc | 75 ++++---- libmemcached/server.h | 8 +- libmemcached/server_list.cc | 82 ++++++-- libmemcached/stats.cc | 1 + libmemcached/util/include.am | 2 + libmemcached/util/pid.cc | 76 ++++++++ libmemcached/util/pid.h | 49 +++++ libmemcached/util/ping.cc | 12 +- libtest/error.h | 1 + libtest/framework.cc | 11 ++ libtest/framework.h | 10 +- libtest/include.am | 25 ++- libtest/killpid.cc | 116 +++++++++++ libtest/killpid.h | 42 ++++ libtest/memcached.cc | 352 +++++++++++++++------------------- libtest/server.cc | 99 ++++++++++ libtest/server.h | 113 +++++++++-- libtest/test.cc | 88 ++++++--- libtest/test.hpp | 6 + libtest/wait.cc | 54 ++++++ libtest/wait.h | 78 ++++++++ tests/cycle.cc | 91 +++++++++ tests/deprecated.cc | 11 +- tests/include.am | 49 +++-- tests/libmemcached_world.h | 33 +++- tests/mem_functions.cc | 24 ++- tests/start.cc | 29 --- 40 files changed, 1327 insertions(+), 443 deletions(-) create mode 100644 libmemcached/assert.hpp create mode 100644 libmemcached/util/pid.cc create mode 100644 libmemcached/util/pid.h create mode 100644 libtest/killpid.cc create mode 100644 libtest/killpid.h create mode 100644 libtest/server.cc create mode 100644 libtest/wait.cc create mode 100644 libtest/wait.h create mode 100644 tests/cycle.cc delete mode 100644 tests/start.cc diff --git a/.bzrignore b/.bzrignore index f4695169..a64e1be4 100644 --- a/.bzrignore +++ b/.bzrignore @@ -99,3 +99,7 @@ tests/testplus tests/testudp tests/var/ unittests/unittests +libtest/wait +docs/text +docs/changes +tests/cycle diff --git a/libmemcached/assert.hpp b/libmemcached/assert.hpp new file mode 100644 index 00000000..8a46784e --- /dev/null +++ b/libmemcached/assert.hpp @@ -0,0 +1,57 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * libmcachedd client library. + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include +#include + +#ifdef NDEBUG +#define assert(__expr, __mesg) ((void)0) +#else + +#define assert_msg(__expr, __mesg) \ +do \ +{ \ + if (not (__expr)) \ + { \ + fprintf(stderr, "\nAssertion \"%s\" failed for function \"%s\" likely for %s, at %s:%d\n", #__expr, __func__, (#__mesg), __FILE__, __LINE__);\ + abort(); \ + } \ +} while (0) + +#endif diff --git a/libmemcached/common.h b/libmemcached/common.h index 98072cb3..ac300def 100644 --- a/libmemcached/common.h +++ b/libmemcached/common.h @@ -154,12 +154,12 @@ LIBMEMCACHED_LOCAL memcached_return_t memcached_purge(memcached_server_write_instance_st ptr); LIBMEMCACHED_LOCAL -memcached_server_st *memcached_server_create_with(const memcached_st *memc, - memcached_server_write_instance_st host, - const char *hostname, - in_port_t port, - uint32_t weight, - memcached_connection_t type); + memcached_server_st *__server_create_with(const memcached_st *memc, + memcached_server_write_instance_st host, + const char *hostname, + in_port_t port, + uint32_t weight, + memcached_connection_t type); static inline memcached_return_t memcached_validate_key_length(size_t key_length, bool binary) diff --git a/libmemcached/connect.cc b/libmemcached/connect.cc index 5e6c77f5..43924116 100644 --- a/libmemcached/connect.cc +++ b/libmemcached/connect.cc @@ -101,11 +101,11 @@ static memcached_return_t connect_poll(memcached_server_st *ptr) int err; socklen_t len= sizeof (err); (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - ptr->cached_errno= (err == 0) ? get_socket_errno() : err; + memcached_set_errno(*ptr, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT); } else { - ptr->cached_errno= get_socket_errno(); + memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); } WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); @@ -347,7 +347,8 @@ static memcached_return_t unix_socket_connect(memcached_server_st *ptr) if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - return memcached_set_errno(*ptr, errno, NULL); + memcached_set_errno(*ptr, errno, NULL); + return MEMCACHED_CONNECTION_FAILURE; } struct sockaddr_un servAddr; @@ -374,7 +375,8 @@ static memcached_return_t unix_socket_connect(memcached_server_st *ptr) default: WATCHPOINT_ERRNO(errno); - return memcached_set_errno(*ptr, errno, MEMCACHED_AT); + memcached_set_errno(*ptr, errno, MEMCACHED_AT); + return MEMCACHED_CONNECTION_FAILURE; } } } while (0); @@ -540,7 +542,9 @@ memcached_return_t memcached_connect(memcached_server_write_instance_st ptr) memcached_return_t rc= MEMCACHED_NO_SERVERS; if (ptr->fd != INVALID_SOCKET) + { return MEMCACHED_SUCCESS; + } LIBMEMCACHED_MEMCACHED_CONNECT_START(); diff --git a/libmemcached/error.cc b/libmemcached/error.cc index 5cfb4be2..a8f42043 100644 --- a/libmemcached/error.cc +++ b/libmemcached/error.cc @@ -50,12 +50,31 @@ struct memcached_error_t char message[MAX_ERROR_LENGTH]; }; +static void _set(memcached_server_st& server, memcached_st& memc) +{ + if (server.error_messages && server.error_messages->query_id != server.root->query_id) + { + memcached_error_free(server); + } + + if (memc.error_messages == NULL) + return; + + memcached_error_t *error= (struct memcached_error_t *)libmemcached_malloc(&memc, sizeof(struct memcached_error_t)); + if (not error) // Bad business if this happens + return; + + memcpy(error, memc.error_messages, sizeof(memcached_error_t)); + error->next= server.error_messages; + server.error_messages= error; +} + static void _set(memcached_st& memc, memcached_string_t *str, memcached_return_t &rc, const char *at, int local_errno= 0) { (void)at; if (memc.error_messages && memc.error_messages->query_id != memc.query_id) { - memcached_error_free(&memc); + memcached_error_free(memc); } // For memory allocation we use our error since it is a bit more specific @@ -196,6 +215,7 @@ memcached_return_t memcached_set_error(memcached_server_st& self, memcached_retu return rc; _set(*self.root, &error_host, rc, at); + _set(self, (*self.root)); return rc; } @@ -215,6 +235,7 @@ memcached_return_t memcached_set_error(memcached_server_st& self, memcached_retu return rc; _set(*self.root, &error_host, rc, at); + _set(self, *self.root); return rc; } @@ -285,13 +306,12 @@ memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errn memcached_string_t error_host= { hostname_port_message, size }; - self.cached_errno= local_errno; // Store in the actual server - memcached_return_t rc= MEMCACHED_ERRNO; if (not self.root) return rc; _set(*self.root, &error_host, rc, at, local_errno); + _set(self, (*self.root)); return rc; } @@ -307,13 +327,12 @@ memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errn memcached_string_t error_host= { hostname_port_message, size }; - self.cached_errno= local_errno; // Store in the actual server - memcached_return_t rc= MEMCACHED_ERRNO; if (not self.root) return rc; _set(*self.root, &error_host, rc, at, local_errno); + _set(self, (*self.root)); return rc; } @@ -360,13 +379,16 @@ static void _error_free(memcached_error_t *error) } } -void memcached_error_free(memcached_st *self) +void memcached_error_free(memcached_st& self) { - if (not self) - return; + _error_free(self.error_messages); + self.error_messages= NULL; +} - _error_free(self->error_messages); - self->error_messages= NULL; +void memcached_error_free(memcached_server_st& self) +{ + _error_free(self.error_messages); + self.error_messages= NULL; } const char *memcached_last_error_message(memcached_st *memc) @@ -417,3 +439,40 @@ int memcached_last_error_errno(memcached_st *memc) return memc->error_messages->local_errno; } + +const char *memcached_server_error(memcached_server_instance_st server) +{ + if (not server) + return memcached_strerror(server->root, MEMCACHED_INVALID_ARGUMENTS); + + if (not server->error_messages) + return memcached_strerror(server->root, MEMCACHED_SUCCESS); + + if (not server->error_messages->size) + return memcached_strerror(server->root, server->error_messages->rc); + + return server->error_messages->message; +} + + +memcached_error_t *memcached_error_copy(const memcached_server_st& server) +{ + if (not server.error_messages) + return NULL; + + memcached_error_t *error= (memcached_error_t *)libmemcached_malloc(server.root, sizeof(memcached_error_t)); + memcpy(error, server.error_messages, sizeof(memcached_error_t)); + error->next= NULL; + + return error; +} + +memcached_return_t memcached_server_error_return(memcached_server_instance_st ptr) +{ + if (ptr and ptr->error_messages) + { + return ptr->error_messages->rc; + } + + return MEMCACHED_FAILURE; +} diff --git a/libmemcached/error.h b/libmemcached/error.h index 754cffe8..99beb8ca 100644 --- a/libmemcached/error.h +++ b/libmemcached/error.h @@ -41,9 +41,6 @@ extern "C" { #endif -LIBMEMCACHED_LOCAL - void memcached_error_free(memcached_st *error); - LIBMEMCACHED_API const char *memcached_last_error_message(memcached_st *memc); @@ -56,6 +53,12 @@ LIBMEMCACHED_API LIBMEMCACHED_API int memcached_last_error_errno(memcached_st *memc); +LIBMEMCACHED_API + const char *memcached_server_error(memcached_server_instance_st ptr); + +LIBMEMCACHED_API + memcached_return_t memcached_server_error_return(memcached_server_instance_st ptr); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libmemcached/error.hpp b/libmemcached/error.hpp index 1c683fc5..55ff1bd9 100644 --- a/libmemcached/error.hpp +++ b/libmemcached/error.hpp @@ -84,4 +84,13 @@ LIBMEMCACHED_LOCAL LIBMEMCACHED_LOCAL bool memcached_has_current_error(memcached_st &memc); +LIBMEMCACHED_LOCAL +void memcached_error_free(memcached_st&); + +LIBMEMCACHED_LOCAL +void memcached_error_free(memcached_server_st&); + +LIBMEMCACHED_LOCAL +memcached_error_t *memcached_error_copy(const memcached_server_st&); + #endif diff --git a/libmemcached/hosts.cc b/libmemcached/hosts.cc index 4c419b02..db639891 100644 --- a/libmemcached/hosts.cc +++ b/libmemcached/hosts.cc @@ -344,7 +344,9 @@ static memcached_return_t update_continuum(memcached_st *ptr) memcached_return_t memcached_server_push(memcached_st *ptr, const memcached_server_list_st list) { if (not list) + { return MEMCACHED_SUCCESS; + } uint32_t count= memcached_server_list_count(list); @@ -362,8 +364,7 @@ memcached_return_t memcached_server_push(memcached_st *ptr, const memcached_serv memcached_server_write_instance_st instance; if ((ptr->flags.use_udp && list[x].type != MEMCACHED_CONNECTION_UDP) - or ((list[x].type == MEMCACHED_CONNECTION_UDP) - and ! (ptr->flags.use_udp)) ) + or ((list[x].type == MEMCACHED_CONNECTION_UDP) and not (ptr->flags.use_udp)) ) { return MEMCACHED_INVALID_HOST_PROTOCOL; } @@ -374,8 +375,8 @@ memcached_return_t memcached_server_push(memcached_st *ptr, const memcached_serv instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); WATCHPOINT_ASSERT(instance); - if (not memcached_server_create_with(ptr, instance, list[x].hostname, - list[x].port, list[x].weight, list[x].type)) + if (not __server_create_with(ptr, instance, list[x].hostname, + list[x].port, list[x].weight, list[x].type)) { return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); } @@ -453,7 +454,7 @@ memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, if (not hostname) hostname= "localhost"; - return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP); + return server_add(ptr, hostname, port, weight, hostname[0] == '/' ? MEMCACHED_CONNECTION_UNIX_SOCKET : MEMCACHED_CONNECTION_TCP); } static memcached_return_t server_add(memcached_st *ptr, const char *hostname, @@ -479,7 +480,7 @@ static memcached_return_t server_add(memcached_st *ptr, const char *hostname, /* TODO: Check return type */ memcached_server_write_instance_st instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); - if (not memcached_server_create_with(ptr, instance, hostname, port, weight, type)) + if (not __server_create_with(ptr, instance, hostname, port, weight, type)) { return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); } diff --git a/libmemcached/include.am b/libmemcached/include.am index e1bed44f..db612892 100644 --- a/libmemcached/include.am +++ b/libmemcached/include.am @@ -12,6 +12,7 @@ EXTRA_DIST+= \ libmemcached/memcached/README.txt noinst_HEADERS+= \ + libmemcached/assert.hpp \ libmemcached/byteorder.h \ libmemcached/common.h \ libmemcached/do.hpp \ diff --git a/libmemcached/io.cc b/libmemcached/io.cc index a787b0c2..f77a0973 100644 --- a/libmemcached/io.cc +++ b/libmemcached/io.cc @@ -128,11 +128,11 @@ static memcached_return_t io_wait(memcached_server_write_instance_st ptr, int err; socklen_t len= sizeof (err); (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - ptr->cached_errno= (err == 0) ? get_socket_errno() : err; + memcached_set_errno(*ptr, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT); } else { - ptr->cached_errno= get_socket_errno(); + memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); } memcached_quit_server(ptr, true); @@ -141,10 +141,9 @@ static memcached_return_t io_wait(memcached_server_write_instance_st ptr, } } - ptr->cached_errno= get_socket_errno(); memcached_quit_server(ptr, true); - return memcached_set_error(*ptr, MEMCACHED_FAILURE, MEMCACHED_AT); + return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); } memcached_return_t memcached_io_wait_for_write(memcached_server_write_instance_st ptr) @@ -711,7 +710,7 @@ static ssize_t io_flush(memcached_server_write_instance_st ptr, if (sent_length == SOCKET_ERROR) { - ptr->cached_errno= get_socket_errno(); + memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); #if 0 // @todo I should look at why we hit this bit of code hard frequently WATCHPOINT_ERRNO(get_socket_errno()); WATCHPOINT_NUMBER(get_socket_errno()); diff --git a/libmemcached/memcached.cc b/libmemcached/memcached.cc index f93f74b4..f35e7cc9 100644 --- a/libmemcached/memcached.cc +++ b/libmemcached/memcached.cc @@ -163,7 +163,7 @@ static void _free(memcached_st *ptr, bool release_st) memcached_array_free(ptr->prefix_key); ptr->prefix_key= NULL; - memcached_error_free(ptr); + memcached_error_free(*ptr); if (ptr->sasl.callbacks) { diff --git a/libmemcached/memcached_util.h b/libmemcached/memcached_util.h index 3f1185d1..c0a4620a 100644 --- a/libmemcached/memcached_util.h +++ b/libmemcached/memcached_util.h @@ -38,6 +38,7 @@ #pragma once +#include #include #include #include diff --git a/libmemcached/response.cc b/libmemcached/response.cc index 44ce840a..ce649bcb 100644 --- a/libmemcached/response.cc +++ b/libmemcached/response.cc @@ -218,7 +218,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS) { memcached_quit_server(ptr, true); - return memcached_set_error(*ptr, rrc, MEMCACHED_AT); + return memcached_set_error(*ptr, MEMCACHED_IN_PROGRESS, MEMCACHED_AT); } else if (memcached_failed(rrc)) { @@ -291,28 +291,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta while (*endptr != '\r' && *endptr != '\n') endptr++; - /* - Yes, we could make this "efficent" but to do that we would need - to maintain more state for the size of the buffer. Why waste - memory in the struct, which is important, for something that - rarely should happen? - */ - char *rel_ptr= (char *)libmemcached_realloc(ptr->root, - ptr->cached_server_error, - (size_t) (endptr - startptr + 1)); - - if (rel_ptr == NULL) - { - /* If we happened to have some memory, we just null it since we don't know the size */ - if (ptr->cached_server_error) - ptr->cached_server_error[0]= 0; - return MEMCACHED_SERVER_ERROR; - } - ptr->cached_server_error= rel_ptr; - - memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr)); - ptr->cached_server_error[endptr - startptr]= 0; - return MEMCACHED_SERVER_ERROR; + return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr)); } else if (buffer[1] == 'T') { diff --git a/libmemcached/server.cc b/libmemcached/server.cc index 52daefec..f5fe62e1 100644 --- a/libmemcached/server.cc +++ b/libmemcached/server.cc @@ -50,7 +50,6 @@ static inline void _server_init(memcached_server_st *self, memcached_st *root, self->number_of_hosts= 0; self->cursor_active= 0; self->port= port; - self->cached_errno= 0; self->fd= -1; self->io_bytes_sent= 0; self->server_failure_counter= 0; @@ -61,8 +60,8 @@ static inline void _server_init(memcached_server_st *self, memcached_st *root, self->micro_version= UINT8_MAX; self->minor_version= UINT8_MAX; self->type= type; + self->error_messages= NULL; self->read_ptr= self->read_buffer; - self->cached_server_error= NULL; self->read_buffer_length= 0; self->read_data_length= 0; self->write_buffer_offset= 0; @@ -113,10 +112,10 @@ static memcached_server_st *_server_create(memcached_server_st *self, const memc return self; } -memcached_server_st *memcached_server_create_with(const memcached_st *memc, - memcached_server_write_instance_st self, - const char *hostname, in_port_t port, - uint32_t weight, memcached_connection_t type) +memcached_server_st *__server_create_with(const memcached_st *memc, + memcached_server_write_instance_st self, + const char *hostname, in_port_t port, + uint32_t weight, memcached_connection_t type) { self= _server_create(self, memc); @@ -135,18 +134,16 @@ memcached_server_st *memcached_server_create_with(const memcached_st *memc, return self; } -void memcached_server_free(memcached_server_st *self) +void __server_free(memcached_server_st *self) { - if (not self) - return; - memcached_quit_server(self, false); - if (self->cached_server_error) - free(self->cached_server_error); - if (self->address_info) + { freeaddrinfo(self->address_info); + } + + memcached_error_free(*self); if (memcached_is_allocated(self)) { @@ -158,6 +155,20 @@ void memcached_server_free(memcached_server_st *self) } } +void memcached_server_free(memcached_server_st *self) +{ + if (not self) + return; + + if (memcached_server_list_count(self)) + { + memcached_server_list_free(self); + return; + } + + __server_free(self); +} + /* If we do not have a valid object to clone from, we toss an error. */ @@ -168,15 +179,15 @@ memcached_server_st *memcached_server_clone(memcached_server_st *destination, if (not source) return NULL; - destination= memcached_server_create_with(source->root, destination, - source->hostname, source->port, source->weight, - source->type); + destination= __server_create_with(source->root, destination, + source->hostname, source->port, source->weight, + source->type); if (not destination) { - destination->cached_errno= source->cached_errno; - - if (source->cached_server_error) - destination->cached_server_error= strdup(source->cached_server_error); + if (source->error_messages) + { + destination->error_messages= memcached_error_copy(*source); + } } return destination; @@ -272,7 +283,7 @@ void memcached_server_error_reset(memcached_server_st *self) if (not self) return; - self->cached_server_error[0]= 0; + memcached_error_free(*self); } memcached_server_instance_st memcached_server_get_last_disconnect(const memcached_st *self) @@ -284,23 +295,6 @@ memcached_server_instance_st memcached_server_get_last_disconnect(const memcache return self->last_disconnected_server; } -void memcached_server_list_free(memcached_server_list_st self) -{ - if (not self) - return; - - for (uint32_t x= 0; x < memcached_server_list_count(self); x++) - { - if (self[x].address_info) - { - freeaddrinfo(self[x].address_info); - self[x].address_info= NULL; - } - } - - libmemcached_free(self->root, self); -} - uint32_t memcached_servers_set_count(memcached_server_st *servers, uint32_t count) { WATCHPOINT_ASSERT(servers); @@ -346,11 +340,6 @@ uint32_t memcached_server_response_count(memcached_server_instance_st self) return self->cursor_active; } -const char *memcached_server_error(memcached_server_instance_st ptr) -{ - return ptr ? ptr->cached_server_error : NULL; -} - const char *memcached_server_type(memcached_server_instance_st ptr) { if (ptr) diff --git a/libmemcached/server.h b/libmemcached/server.h index 425dfb01..a45616a1 100644 --- a/libmemcached/server.h +++ b/libmemcached/server.h @@ -55,7 +55,6 @@ struct memcached_server_st { uint32_t number_of_hosts; uint32_t cursor_active; in_port_t port; - int cached_errno; memcached_socket_t fd; uint32_t io_bytes_sent; /* # bytes sent since last read */ uint32_t server_failure_counter; @@ -70,7 +69,6 @@ struct memcached_server_st { uint8_t minor_version; // ditto memcached_connection_t type; char *read_ptr; - char *cached_server_error; size_t read_buffer_length; size_t read_data_length; size_t write_buffer_offset; @@ -79,6 +77,7 @@ struct memcached_server_st { time_t next_retry; memcached_st *root; uint64_t limit_maxbytes; + struct memcached_error_t *error_messages; char read_buffer[MEMCACHED_MAX_BUFFER]; char write_buffer[MEMCACHED_MAX_BUFFER]; char hostname[NI_MAXHOST]; @@ -159,13 +158,12 @@ const char *memcached_server_name(memcached_server_instance_st self); LIBMEMCACHED_API in_port_t memcached_server_port(memcached_server_instance_st self); -LIBMEMCACHED_API -const char *memcached_server_error(memcached_server_instance_st ptr); - LIBMEMCACHED_API const char *memcached_server_type(memcached_server_instance_st ptr); +LIBMEMCACHED_LOCAL +void __server_free(memcached_server_st *); #ifdef __cplusplus } // extern "C" diff --git a/libmemcached/server_list.cc b/libmemcached/server_list.cc index 6cf6ae94..4da19963 100644 --- a/libmemcached/server_list.cc +++ b/libmemcached/server_list.cc @@ -1,16 +1,44 @@ -/* LibMemcached - * Copyright (C) 2006-2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2010 Brian Aker All rights reserved. * - * Summary: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + #include +#include memcached_server_list_st memcached_server_list_append_with_weight(memcached_server_list_st ptr, @@ -21,13 +49,23 @@ memcached_server_list_append_with_weight(memcached_server_list_st ptr, uint32_t count; memcached_server_list_st new_host_list; - if (hostname == NULL || error == NULL) - return NULL; + memcached_return_t unused; + if (error == NULL) + error= &unused; + + if (hostname == NULL) + { + hostname= "localhost"; + } if (hostname[0] == '/') + { port = 0; - else if (! port) + } + else if (not port) + { port= MEMCACHED_DEFAULT_PORT; + } /* Increment count for hosts */ count= 1; @@ -37,23 +75,23 @@ memcached_server_list_append_with_weight(memcached_server_list_st ptr, } new_host_list= (memcached_server_write_instance_st)realloc(ptr, sizeof(memcached_server_st) * count); - if (!new_host_list) + if (not new_host_list) { - ptr->cached_errno= errno; - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + *error= memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); return NULL; } /* @todo Check return type */ - if (not memcached_server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, port ? MEMCACHED_CONNECTION_TCP : MEMCACHED_CONNECTION_UNIX_SOCKET)) + if (not __server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, port ? MEMCACHED_CONNECTION_TCP : MEMCACHED_CONNECTION_UNIX_SOCKET)) { - ptr->cached_errno= errno; - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + *error= memcached_set_errno(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT); return NULL; } +#if 0 // Handset allocated since new_host_list->options.is_allocated= true; +#endif /* Backwards compatibility hack */ memcached_servers_set_count(new_host_list, count); @@ -86,3 +124,17 @@ void memcached_server_list_set(memcached_st *self, memcached_server_st *list) { self->servers= list; } + +void memcached_server_list_free(memcached_server_list_st self) +{ + if (not self) + return; + + for (uint32_t x= 0; x < memcached_server_list_count(self); x++) + { + assert_msg(not memcached_is_allocated(&self[x]), "You have called memcached_server_list_free(), but you did not pass it a valid memcached_server_list_st"); + __server_free(&self[x]); + } + + libmemcached_free(self->root, self); +} diff --git a/libmemcached/stats.cc b/libmemcached/stats.cc index 5a2013e8..6e2ef31f 100644 --- a/libmemcached/stats.cc +++ b/libmemcached/stats.cc @@ -493,6 +493,7 @@ memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_retu stat_instance= stats +x; + stat_instance->pid= -1; stat_instance->root= self; instance= memcached_server_instance_fetch(self, x); diff --git a/libmemcached/util/include.am b/libmemcached/util/include.am index 449b2c18..ea80eb8f 100644 --- a/libmemcached/util/include.am +++ b/libmemcached/util/include.am @@ -7,6 +7,7 @@ nobase_include_HEADERS+= \ libmemcached/memcached_util.h \ libmemcached/util.h \ libmemcached/util/flush.h \ + libmemcached/util/pid.h \ libmemcached/util/ping.h \ libmemcached/util/pool.h \ libmemcached/util/version.h @@ -15,6 +16,7 @@ endif libmemcached_libmemcachedutil_la_SOURCES= \ libmemcached/util/flush.cc \ + libmemcached/util/pid.cc \ libmemcached/util/ping.cc \ libmemcached/util/pool.cc \ libmemcached/util/version.cc diff --git a/libmemcached/util/pid.cc b/libmemcached/util/pid.cc new file mode 100644 index 00000000..75edd518 --- /dev/null +++ b/libmemcached/util/pid.cc @@ -0,0 +1,76 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Summary: connects to a host, and determines what its pid is + * + */ + +#include +#include + + +// Never look at the stat object directly. + + +pid_t libmemcached_util_getpid(const char *hostname, in_port_t port, memcached_return_t *ret) +{ + memcached_st *memc_ptr= memcached_create(NULL); + + pid_t pid= -1; + + memcached_return_t rc= memcached_server_add(memc_ptr, hostname, port); + if (memcached_success(rc)) + { + if (memcached_success(memcached_version(memc_ptr))) + { + memcached_stat_st *stat= memcached_stat(memc_ptr, NULL, &rc); + if (stat and stat->pid > 0) + { + pid= stat->pid; + } + + memcached_stat_free(memc_ptr, stat); + } + } + memcached_free(memc_ptr); + + if (ret) + { + *ret= rc; + } + + return pid; +} + diff --git a/libmemcached/util/pid.h b/libmemcached/util/pid.h new file mode 100644 index 00000000..f2fb7488 --- /dev/null +++ b/libmemcached/util/pid.h @@ -0,0 +1,49 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +LIBMEMCACHED_API +pid_t libmemcached_util_getpid(const char *hostname, in_port_t port, memcached_return_t *ret); + +#ifdef __cplusplus +} +#endif + diff --git a/libmemcached/util/ping.cc b/libmemcached/util/ping.cc index 37da8649..52489b95 100644 --- a/libmemcached/util/ping.cc +++ b/libmemcached/util/ping.cc @@ -51,6 +51,16 @@ bool libmemcached_util_ping(const char *hostname, in_port_t port, memcached_retu rc= memcached_version(memc_ptr); } + if (memcached_failed(rc) and rc == MEMCACHED_SOME_ERRORS) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc_ptr, 0); + + if (instance and instance->error_messages) + { + rc= memcached_server_error_return(instance); + } + } memcached_free(memc_ptr); if (ret) @@ -58,5 +68,5 @@ bool libmemcached_util_ping(const char *hostname, in_port_t port, memcached_retu *ret= rc; } - return rc == MEMCACHED_SUCCESS; + return memcached_success(rc); } diff --git a/libtest/error.h b/libtest/error.h index c4a3a702..a40868b3 100644 --- a/libtest/error.h +++ b/libtest/error.h @@ -16,3 +16,4 @@ enum test_return_t { #define test_failed(__test_return_t) ((__test_return_t) != TEST_SUCCESS) +#define test_success(__test_return_t) ((__test_return_t) == TEST_SUCCESS) diff --git a/libtest/framework.cc b/libtest/framework.cc index ff67f20b..eedbd580 100644 --- a/libtest/framework.cc +++ b/libtest/framework.cc @@ -55,3 +55,14 @@ test_return_t Framework::Item::startup(void* arg) return TEST_SUCCESS; } + +void* Framework::create(test_return_t* arg) +{ + if (_create) + { + return _create(arg); + } + + return NULL; +} + diff --git a/libtest/framework.h b/libtest/framework.h index 7a58cbee..afb9001a 100644 --- a/libtest/framework.h +++ b/libtest/framework.h @@ -19,15 +19,7 @@ struct Framework { test_callback_create_fn *_create; test_callback_fn *_destroy; - void* create(test_return_t* arg) - { - if (_create) - { - return _create(arg); - } - - return NULL; - } + void* create(test_return_t* arg); test_return_t destroy(void*); diff --git a/libtest/include.am b/libtest/include.am index a1ed6ede..0f4d910d 100644 --- a/libtest/include.am +++ b/libtest/include.am @@ -13,6 +13,7 @@ LIBUTEST_TMP = ${abs_top_builddir}/tests/var/tmp/ CLEANFILES+= \ tests/var/log/* \ + tests/var/run/* \ tests/var/tmp/* noinst_HEADERS+= \ @@ -24,16 +25,21 @@ noinst_HEADERS+= \ libtest/failed.h \ libtest/framework.h \ libtest/get.h \ + libtest/killpid.h \ libtest/runner.h \ libtest/server.h \ libtest/stats.h \ libtest/strerror.h \ libtest/test.h \ libtest/test.hpp \ - libtest/visibility.h + libtest/visibility.h \ + libtest/wait.h noinst_LTLIBRARIES+= libtest/libserver.la -libtest_libserver_la_SOURCES= libtest/memcached.cc +libtest_libserver_la_SOURCES= \ + libtest/killpid.cc \ + libtest/memcached.cc \ + libtest/server.cc noinst_LTLIBRARIES+= libtest/libtest.la libtest_libtest_la_SOURCES=\ @@ -42,7 +48,13 @@ libtest_libtest_la_SOURCES=\ libtest_libtest_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} -DBUILDING_LIBTEST libtest_libtest_la_CPPFLAGS= ${AM_CPPFLAGS} -tests/var: tests/var/log tests/var/tmp +clearn-var: + @rm -f tests/var/log/* + @rm -f tests/var/run/* + @rm -f tests/var/tmp/* + + +tests/var: tests/var/log tests/var/tmp tests/var/run clearn-var $(mkdir_p) tests/var tests/var/log: @@ -50,3 +62,10 @@ tests/var/log: tests/var/tmp: $(mkdir_p) tests/var/tmp + +tests/var/run: + $(mkdir_p) tests/var/run + +noinst_PROGRAMS+= libtest/wait + +libtest_wait_SOURCES= libtest/wait.cc diff --git a/libtest/killpid.cc b/libtest/killpid.cc new file mode 100644 index 00000000..e963f4a7 --- /dev/null +++ b/libtest/killpid.cc @@ -0,0 +1,116 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include + +#include + +bool kill_pid(pid_t pid_arg) +{ + if ((kill(pid_arg, SIGTERM) == -1)) + { + switch (errno) + { + case EPERM: + perror(__func__); + std::cerr << __func__ << " -> Does someone else have a process running locally for " << int(pid_arg) << "?" << std::endl; + return false; + + case ESRCH: + perror(__func__); + std::cerr << "Process " << int(pid_arg) << " not found." << std::endl; + return false; + + default: + case EINVAL: + perror(__func__); + return false; + } + } + + int status= 0; + pid_t pid= waitpid(pid_arg, &status, 0); + if (pid == -1) + { + switch (errno) + { + case ECHILD: + return true; + } + std::cerr << std::endl << "Error occured while waitpid(" << strerror(errno) << ") on pid " << int(pid_arg) << std::endl; + return false; + } + + if (WIFEXITED(status)) + return true; + + if (WCOREDUMP(status)) + return true; + + return false; +} + + +void kill_file(const std::string &filename) +{ + FILE *fp; + + if (filename.empty()) + return; + + if ((fp= fopen(filename.c_str(), "r"))) + { + char pid_buffer[1024]; + + char *ptr= fgets(pid_buffer, sizeof(pid_buffer), fp); + fclose(fp); + + if (ptr) + { + pid_t pid= (pid_t)atoi(pid_buffer); + if (pid != 0) + { + kill_pid(pid); + unlink(filename.c_str()); // If this happens we may be dealing with a dead server that left its pid file. + } + } + } +} diff --git a/libtest/killpid.h b/libtest/killpid.h new file mode 100644 index 00000000..d67ec94b --- /dev/null +++ b/libtest/killpid.h @@ -0,0 +1,42 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + + +bool kill_pid(pid_t pid_arg); + +void kill_file(const std::string &filename); diff --git a/libtest/memcached.cc b/libtest/memcached.cc index 8c79b17d..e7e87f26 100644 --- a/libtest/memcached.cc +++ b/libtest/memcached.cc @@ -42,25 +42,31 @@ #define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10 -#include - -#include - -#include +#include + +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include #include -#include +#include #include #include #include +#include +#include + +#include + + +#define CERR_PREFIX std::endl << __FILE__ << ":" << __LINE__ << " " static void global_sleep(void) { @@ -73,269 +79,221 @@ static void global_sleep(void) #endif } -static bool wait_for_file(const char *filename) -{ - uint32_t timeout= 6; - uint32_t waited; - uint32_t this_wait; - uint32_t retry; +#define SOCKET_FILE "/tmp/memcached.socket" - for (waited= 0, retry= 1; ; retry++, waited+= this_wait) - { - if ((! access(filename, R_OK)) || (waited >= timeout)) - { - return true; - } - - this_wait= retry * retry / 3 + 1; - sleep(this_wait); - } - - return false; -} - -static void kill_file(const char *file_buffer) +static bool cycle_server(server_st& server) { - FILE *fp; - - while ((fp= fopen(file_buffer, "r"))) + while (1) { - char pid_buffer[1024]; - - if (fgets(pid_buffer, sizeof(pid_buffer), fp) != NULL) + if (libmemcached_util_ping(server.hostname, server.port(), NULL)) { - pid_t pid= (pid_t)atoi(pid_buffer); - if (pid != 0) + // First we try to kill it, and on fail of that we flush it. + pid_t pid= libmemcached_util_getpid(server.hostname, server.port(), NULL); + + if (pid > 0 and kill_pid(pid)) { - if (kill(pid, SIGTERM) == -1) - { - remove(file_buffer); // If this happens we may be dealing with a dead server that left its pid file. - } - else - { - uint32_t counter= 3; - while ((kill(pid, 0) == 0) && --counter) - { - global_sleep(); - } - } + std::cerr << CERR_PREFIX << "Killed existing server," << server << " with pid:" << pid << std::endl; + continue; + } + else if (libmemcached_util_flush(server.hostname, server.port(), NULL)) // If we can flush it, we will just use it + { + std::cerr << CERR_PREFIX << "Found server on port " << int(server.port()) << ", flushed it!" << std::endl; + server.set_used(); + return true; + } // No idea what is wrong here, so we need to find a different port + else + { + return false; } } - global_sleep(); - - fclose(fp); + break; } + + return true; } -void server_startup(server_startup_st *construct) +bool server_startup(server_startup_st *construct) { - if ((construct->server_list= getenv("MEMCACHED_SERVERS"))) + if (getenv(((char *)"MEMCACHED_SERVERS"))) { - printf("servers %s\n", construct->server_list); + construct->server_list= getenv(((char *)"MEMCACHED_SERVERS")); + printf("servers %s\n", construct->server_list.c_str()); construct->count= 0; } else { + std::string server_config_string; + + uint32_t port_base= 0; + for (uint32_t x= 0; x < (construct->count -1); x++) { - char server_string_buffer[8096]; - char *end_ptr; - end_ptr= server_string_buffer; + server_st &server= construct->server[x]; - uint32_t port_base= 0; - for (uint32_t x= 0; x < construct->count; x++) { - int status; + char *var; + char variable_buffer[1024]; + + snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x); - snprintf(construct->pid_file[x], FILENAME_MAX, "/tmp/memcached.pidXXXXXX"); - int fd; - if ((fd= mkstemp(construct->pid_file[x])) == -1) + if ((var= getenv(variable_buffer))) { - perror("mkstemp"); - return; + server.set_port((in_port_t)atoi(var)); } - close(fd); - + else { - char *var; - char variable_buffer[1024]; - - snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x); + server.set_port(in_port_t(x + TEST_PORT_BASE + port_base)); - if ((var= getenv(variable_buffer))) - { - construct->port[x]= (in_port_t)atoi(var); - } - else + while (not cycle_server(server)) { - do { - construct->port[x]= (in_port_t)(x + TEST_PORT_BASE + port_base); - - if (libmemcached_util_ping("localhost", construct->port[x], NULL)) - { - if (libmemcached_util_flush("localhost", construct->port[x], NULL)) - { - fprintf(stderr, "Found server on port %d, flushed it!\n", (int)construct->port[x]); - construct->is_used[x]= true; - } // If we can flush it, we will just use it - else - { - fprintf(stderr, "Found server on port %d, could not flush it, so trying next port.\n", (int)construct->port[x]); - port_base++; - construct->port[x]= 0; - } - } - } while (construct->port[x] == 0); + std::cerr << CERR_PREFIX << "Found server " << server << ", could not flush it, so trying next port." << std::endl; + port_base++; + server.set_port(in_port_t(x + TEST_PORT_BASE + port_base)); } } + } + if (server.is_used()) + { + std::cerr << std::endl << "Using server at : " << server << std::endl; + } + else + { char buffer[FILENAME_MAX]; if (x == 0) { - snprintf(buffer, sizeof(buffer), "%s -d -P %s -t 1 -p %u -U %u -m 128", - MEMCACHED_BINARY, construct->pid_file[x], construct->port[x], construct->port[x]); + snprintf(buffer, sizeof(buffer), "%s -d -t 1 -p %u -U %u -m 128", + MEMCACHED_BINARY, server.port(), server.port()); } else { - snprintf(buffer, sizeof(buffer), "%s -d -P %s -t 1 -p %u -U %u", - MEMCACHED_BINARY, construct->pid_file[x], construct->port[x], construct->port[x]); + snprintf(buffer, sizeof(buffer), "%s -d -t 1 -p %u -U %u", + MEMCACHED_BINARY, server.port(), server.port()); } - if (construct->is_used[x]) + int status= system(buffer); + if (status == -1) { - fprintf(stderr, "USING SERVER: %s\n", buffer); + std::cerr << CERR_PREFIX << "Failed system(" << buffer << ")" << std::endl; + return false; } - else + fprintf(stderr, "STARTING SERVER: %s\n", buffer); + + int count= 30; + memcached_return_t rc; + while (not libmemcached_util_ping(server.hostname, server.port(), &rc) and --count) { - if (libmemcached_util_ping("localhost", construct->port[x], NULL)) - { - fprintf(stderr, "Server on port %u already exists\n", construct->port[x]); - } - else - { - status= system(buffer); - fprintf(stderr, "STARTING SERVER: %s status:%d\n", buffer, status); - } + global_sleep(); } - size_t remaining_length= sizeof(server_string_buffer) - (size_t)(end_ptr -server_string_buffer); - int count= snprintf(end_ptr, remaining_length, "--server=localhost:%u ", construct->port[x]); - - if ((size_t)count >= remaining_length or count < 0) + if (memcached_failed(rc)) { - fprintf(stderr, "server names grew to be larger then buffer allowed\n"); - abort(); + std::cerr << CERR_PREFIX << "libmemcached_util_ping() failed:" << memcached_strerror(NULL, rc) << " Connection:" << server << std::endl; + return false; } - end_ptr+= count; - } - *end_ptr= 0; - - for (uint32_t x= 0; x < construct->count; x++) - { - if (! wait_for_file(construct->pid_file[x])) + server.set_pid(libmemcached_util_getpid(server.hostname, server.port(), &rc)); + if (not server.has_pid()) { - abort(); + std::cerr << CERR_PREFIX << "libmemcached_util_getpid() failed" << memcached_strerror(NULL, rc) << " Connection: " << server << std::endl; + return false; } } - for (uint32_t x= 0; x < construct->count; x++) + server_config_string+= "--server="; + server_config_string+= server.hostname; + server_config_string+= ":"; + server_config_string+= boost::lexical_cast(server.port()); + server_config_string+= " "; + fprintf(stderr, " Port %d\n", server.port()); + } + + { + server_st &server= construct->server[construct->count -1]; + { - uint32_t counter= 3000; // Absurd, just to catch run away process + std::string socket_file; + char *var; - if (construct->is_used[x]) - continue; + server.set_hostname(SOCKET_FILE); - while (construct->pids[x] <= 0 && --counter) + if ((var= getenv("LIBMEMCACHED_SOCKET"))) + { + socket_file+= var; + } + else { - FILE *file= fopen(construct->pid_file[x], "r"); - if (file) + if (not cycle_server(server)) { - char pid_buffer[1024]; - char *found= fgets(pid_buffer, sizeof(pid_buffer), file); - - if (found) - { - construct->pids[x]= atoi(pid_buffer); - fclose(file); - - if (construct->pids[x] > 0) - break; - } - fclose(file); + std::cerr << CERR_PREFIX << "Found server " << server << ", could not flush it, so trying next port." << std::endl; + return false; } + } + } - switch (errno) - { - default: - fprintf(stderr, "Could not open pid file %s -> fopen(%s) -> %s:%d\n", construct->pid_file[x], strerror(errno), __FILE__, __LINE__); - abort(); - - case ENOENT: - case EINTR: - case EACCES: - case EINPROGRESS: - break; - - case ENOTCONN: - continue; - } + if (server.is_used()) + { + std::cerr << std::endl << "Using server at : " << server << std::endl; + } + else + { + char buffer[FILENAME_MAX]; + snprintf(buffer, sizeof(buffer), "%s -d -t 1 -s %s", MEMCACHED_BINARY, SOCKET_FILE); - // Safety 3rd, check to see if the file has gone away - if (! wait_for_file(construct->pid_file[x])) - { - abort(); - } + int status= system(buffer); + if (status == -1) + { + std::cerr << CERR_PREFIX << "Failed system(" << buffer << ")" << std::endl; + return false; } + fprintf(stderr, "STARTING SERVER: %s\n", buffer); - bool was_started= false; - if (construct->pids[x] > 0) + int count= 30; + memcached_return_t rc; + while (not libmemcached_util_ping(server.hostname, server.port(), &rc) and --count) { - counter= 30; - while (--counter) - { - if (kill(construct->pids[x], 0) == 0) - { - was_started= true; - break; - } - global_sleep(); - } + global_sleep(); } - if (was_started == false) + if (memcached_failed(rc)) { - fprintf(stderr, "Failed to open buffer %s(%d)\n", construct->pid_file[x], construct->pids[x]); - for (uint32_t y= 0; y < construct->count; y++) - { - if (construct->pids[y] > 0) - kill(construct->pids[y], SIGTERM); - } - abort(); + std::cerr << CERR_PREFIX << "libmemcached_util_ping() failed:" << memcached_strerror(NULL, rc) << " Connection:" << server << std::endl; + return false; + } + + server.set_pid(libmemcached_util_getpid(server.hostname, server.port(), &rc)); + if (not server.has_pid()) + { + std::cerr << CERR_PREFIX << "libmemcached_util_getpid() failed" << memcached_strerror(NULL, rc) << " Connection: " << server << std::endl; + return false; } } - construct->server_list= strndup(server_string_buffer, strlen(server_string_buffer) -1); + { + set_default_socket(server.hostname); + server_config_string+= "--socket=\""; + server_config_string+= server.hostname; + server_config_string+= "\" "; + } } + + server_config_string.resize(server_config_string.size() -1); // Remove final space + construct->server_list= server_config_string; } srandom((unsigned int)time(NULL)); - printf("\n"); + std::cerr << std::endl; + return true; } void server_shutdown(server_startup_st *construct) { - if (construct->server_list) + for (uint32_t x= 0; x < construct->count; x++) { - for (uint32_t x= 0; x < construct->count; x++) - { - if (construct->is_used[x]) - continue; - - kill_file(construct->pid_file[x]); - } + if (construct->server[x].is_used()) + continue; - free(construct->server_list); + construct->server[x].kill(); } } diff --git a/libtest/server.cc b/libtest/server.cc new file mode 100644 index 00000000..78c2a771 --- /dev/null +++ b/libtest/server.cc @@ -0,0 +1,99 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include + + +std::ostream& operator<<(std::ostream& output, const server_st &arg) +{ + if (arg.is_socket()) + { + output << arg.hostname; + } + else + { + output << arg.hostname << ":" << arg.port(); + } + return output; // for multiple << operators +} + +server_st::~server_st() +{ + if (not _used) + { + kill(); + } +} + +void server_st::reset_pid() +{ + pid_file[0]= 0; + _pid= -1; +} + +bool server_st::kill() +{ + if (not has_pid() and pid_file[0] == 0) + { + return true; + } + + if (has_pid()) + { + kill_pid(pid()); + if (pid_file[0]) + { + unlink(pid_file); // If this happens we may be dealing with a dead server that left its pid file. + } + reset_pid(); + + return true; + } + else if (pid_file[0]) + { + kill_file(pid_file); + reset_pid(); + + return true; + } + + return false; +} diff --git a/libtest/server.h b/libtest/server.h index 5444821d..c209881e 100644 --- a/libtest/server.h +++ b/libtest/server.h @@ -9,32 +9,115 @@ #pragma once +#include +#include +#include +#include #include -/* - Server startup and shutdown functions. -*/ -#ifdef __cplusplus -extern "C" { -#endif +#define SERVERS_TO_CREATE 5 -#include +struct server_st { +private: + bool _used; + pid_t _pid; + in_port_t _port; + char pid_file[FILENAME_MAX]; // Did we start it, or was it just sitting there? +public: -typedef struct server_startup_st server_startup_st; -#define SERVERS_TO_CREATE 5 + char hostname[NI_MAXHOST]; + + server_st() : + _used(false), + _pid(-1), + _port(0) + { + pid_file[0]= 0; + strncpy(hostname, "localhost", sizeof(hostname)); + } + + void set_port(in_port_t arg) + { + _port= arg; + } + + in_port_t port() const + { + return _port; + } + + bool has_port() const + { + return not _port == 0; + } + + void set_used() + { + _used= true; + } + + void set_pid(pid_t arg) + { + _pid= arg; + } + + pid_t pid() const + { + return _pid; + } + + bool is_used() const + { + return _used; + } + + ~server_st(); + + bool has_pid() + { + return _pid > 0; + } + + bool is_socket() const + { + return hostname[0] == '/'; + } + + void set_hostname(const char *arg) + { + strncpy(hostname, arg, sizeof(hostname)); + } + + bool kill(); + +private: + void reset_pid(); +}; + +std::ostream& operator<<(std::ostream& output, const server_st &arg); struct server_startup_st { uint8_t count; uint8_t udp; - char *server_list; - char pid_file[SERVERS_TO_CREATE][FILENAME_MAX]; - in_port_t port[SERVERS_TO_CREATE]; - int pids[SERVERS_TO_CREATE]; - bool is_used[SERVERS_TO_CREATE]; // Did we start it, or was it just sitting there? + std::string server_list; + server_st server[SERVERS_TO_CREATE]; + + server_startup_st() : + count(SERVERS_TO_CREATE), + udp(0) + { } + + ~server_startup_st() + { } }; -void server_startup(server_startup_st *construct); +#ifdef __cplusplus +extern "C" { +#endif + + +bool server_startup(server_startup_st *construct); void server_shutdown(server_startup_st *construct); #ifdef __cplusplus diff --git a/libtest/test.cc b/libtest/test.cc index ee743983..a1300a8a 100644 --- a/libtest/test.cc +++ b/libtest/test.cc @@ -29,6 +29,7 @@ #endif static in_port_t global_port= 0; +static char global_socket[1024]; in_port_t default_port() { @@ -41,6 +42,17 @@ void set_default_port(in_port_t port) global_port= port; } +const char *default_socket() +{ + assert(global_socket[0]); + return global_socket; +} + +void set_default_socket(const char *socket) +{ + strncpy(global_socket, socket, strlen(socket)); +} + static void stats_print(Stats *stats) { std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl; @@ -142,21 +154,22 @@ Framework::Framework() : int main(int argc, char *argv[]) { - Framework world; + Framework *world= new Framework(); - Stats stats; - - get_world(&world); - - if (not world.runner) + if (not world) { - world.runner= &defualt_runners; + return EXIT_FAILURE; } + Stats stats; + + get_world(world); + test_return_t error; - void *world_ptr= world.create(&error); + void *world_ptr= world->create(&error); if (test_failed(error)) { + std::cerr << "create() failed" << std::endl; return EXIT_FAILURE; } @@ -181,7 +194,7 @@ int main(int argc, char *argv[]) wildcard= argv[2]; } - for (collection_st *next= world.collections; next->name; next++) + for (collection_st *next= world->collections; next->name; next++) { test_return_t collection_rc= TEST_SUCCESS; bool failed= false; @@ -192,11 +205,11 @@ int main(int argc, char *argv[]) stats.collection_total++; - collection_rc= world.startup(world_ptr); + collection_rc= world->startup(world_ptr); if (collection_rc == TEST_SUCCESS and next->pre) { - collection_rc= world.runner->pre(next->pre, world_ptr); + collection_rc= world->runner->pre(next->pre, world_ptr); } switch (collection_rc) @@ -232,21 +245,34 @@ int main(int argc, char *argv[]) std::cerr << "\tTesting " << run->name; - world.item.startup(world_ptr); - - world.item.flush(world_ptr, run); - - world.item.pre(world_ptr); - test_return_t return_code; - { // Runner Code - gettimeofday(&start_time, NULL); - return_code= world.runner->run(run->test_fn, world_ptr); - gettimeofday(&end_time, NULL); - load_time= timedif(end_time, start_time); + if (test_success(return_code= world->item.startup(world_ptr))) + { + if (test_success(return_code= world->item.flush(world_ptr, run))) + { + // @note pre will fail is SKIPPED is returned + if (test_success(return_code= world->item.pre(world_ptr))) + { + { // Runner Code + gettimeofday(&start_time, NULL); + return_code= world->runner->run(run->test_fn, world_ptr); + gettimeofday(&end_time, NULL); + load_time= timedif(end_time, start_time); + } + } + + // @todo do something if post fails + (void)world->item.post(world_ptr); + } + else + { + std::cerr << __FILE__ << ":" << __LINE__ << " item.flush(failure)" << std::endl; + } + } + else + { + std::cerr << __FILE__ << ":" << __LINE__ << " item.startup(failure)" << std::endl; } - - world.item.post(world_ptr); stats.total++; @@ -276,15 +302,15 @@ int main(int argc, char *argv[]) std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl; - if (test_failed(world.on_error(return_code, world_ptr))) + if (test_failed(world->on_error(return_code, world_ptr))) { break; } } - if (next->post && world.runner->post) + if (next->post and world->runner->post) { - (void) world.runner->post(next->post, world_ptr); + (void) world->runner->post(next->post, world_ptr); } if (failed == 0 and skipped == 0) @@ -293,7 +319,7 @@ int main(int argc, char *argv[]) } cleanup: - world.shutdown(world_ptr); + world->shutdown(world_ptr); } if (stats.collection_failed || stats.collection_skipped) @@ -308,12 +334,14 @@ cleanup: std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl; } - if (test_failed(world.destroy(world_ptr))) + if (test_failed(world->destroy(world_ptr))) { stats.failed++; // We do this to make our exit code return EXIT_FAILURE } stats_print(&stats); - return stats.failed == 0 ? 0 : 1; + delete world; + + return stats.failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/libtest/test.hpp b/libtest/test.hpp index 5953f82c..cf0e223d 100644 --- a/libtest/test.hpp +++ b/libtest/test.hpp @@ -37,6 +37,12 @@ LIBTEST_API LIBTEST_API void set_default_port(in_port_t port); +LIBTEST_API + const char* default_socket(); + +LIBTEST_API + void set_default_socket(const char *socket); + #ifdef __cplusplus #define test_literal_param(X) (X), (static_cast((sizeof(X) - 1))) #else diff --git a/libtest/wait.cc b/libtest/wait.cc new file mode 100644 index 00000000..93265916 --- /dev/null +++ b/libtest/wait.cc @@ -0,0 +1,54 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc == 2) + { + libtest::Wait wait(argv[1]); + + if (wait.successful()) + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} diff --git a/libtest/wait.h b/libtest/wait.h new file mode 100644 index 00000000..33c4db54 --- /dev/null +++ b/libtest/wait.h @@ -0,0 +1,78 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include +#include + +namespace libtest { + +class Wait +{ +public: + + Wait(const std::string &filename, uint32_t timeout= 6) : + _successful(false) + { + uint32_t waited; + uint32_t this_wait; + uint32_t retry; + + for (waited= 0, retry= 1; ; retry++, waited+= this_wait) + { + if ((not access(filename.c_str(), R_OK)) or (waited >= timeout)) + { + _successful= true; + break; + } + + this_wait= retry * retry / 3 + 1; + sleep(this_wait); + } + } + + bool successful() const + { + return _successful; + } + +private: + bool _successful; +}; + +} // namespace libtest diff --git a/tests/cycle.cc b/tests/cycle.cc new file mode 100644 index 00000000..3b0aa241 --- /dev/null +++ b/tests/cycle.cc @@ -0,0 +1,91 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* + Test that we are cycling the servers we are creating during testing. +*/ + +#include + +#include + + +#include + +#define SERVERS_TO_CREATE 5 + +#ifndef __INTEL_COMPILER +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + +test_st ping[] ={ + {0, 0, 0} +}; + +collection_st collection[] ={ + {0, 0, 0, 0} +}; + +static server_startup_st *world_create(test_return_t *error) +{ + server_startup_st *servers= new server_startup_st(); + + server_startup(servers); + + *error= TEST_SUCCESS; + + return servers; +} + +static test_return_t world_destroy(server_startup_st *servers) +{ + server_shutdown(servers); + delete servers; + + return TEST_SUCCESS; +} + + + +void get_world(Framework *world) +{ + world->collections= collection; + + world->_create= (test_callback_create_fn*)world_create; + world->_destroy= (test_callback_fn*)world_destroy; +} + diff --git a/tests/deprecated.cc b/tests/deprecated.cc index bf0e0c6d..ab40b639 100644 --- a/tests/deprecated.cc +++ b/tests/deprecated.cc @@ -49,13 +49,16 @@ test_return_t server_list_null_test(memcached_st *ptr) (void)ptr; server_list= memcached_server_list_append_with_weight(NULL, NULL, 0, 0, NULL); - test_true(server_list == NULL); + test_true(server_list); + memcached_server_list_free(server_list); server_list= memcached_server_list_append_with_weight(NULL, "localhost", 0, 0, NULL); - test_true(server_list == NULL); + test_true(server_list); + memcached_server_list_free(server_list); server_list= memcached_server_list_append_with_weight(NULL, NULL, 0, 0, &rc); - test_true(server_list == NULL); + test_true(server_list); + memcached_server_list_free(server_list); return TEST_SUCCESS; } @@ -63,7 +66,7 @@ test_return_t server_list_null_test(memcached_st *ptr) // Look for memory leak test_return_t regression_bug_728286(memcached_st *) { - memcached_server_st *servers = memcached_servers_parse("1.2.3.4:99"); + memcached_server_st *servers= memcached_servers_parse("1.2.3.4:99"); assert(servers); memcached_server_free(servers); diff --git a/tests/include.am b/tests/include.am index fb72ea8d..e2546709 100644 --- a/tests/include.am +++ b/tests/include.am @@ -46,13 +46,21 @@ noinst_HEADERS+= \ noinst_PROGRAMS+= \ tests/atomsmasher \ + tests/cycle \ tests/hash_plus \ - tests/startservers \ tests/testapp \ tests/testhashkit \ tests/testplus \ tests/testudp +# Cycle should always run first +tests_cycle_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING) +tests_cycle_SOURCES= tests/cycle.cc +tests_cycle_DEPENDENCIES= $(TESTS_LDADDS) +tests_cycle_LDADD= $(tests_cycle_DEPENDENCIES) +check_PROGRAMS+= tests/cycle + + tests_testapp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING) tests_testapp_SOURCES= \ tests/basic.cc \ @@ -79,6 +87,7 @@ tests_testapp_LDADD= \ $(TESTS_LDADDS) \ libhashkit/libhashkit.la \ libmemcached/libmemcachedinternal.la +check_PROGRAMS+= tests/testapp tests_testplus_SOURCES= tests/plus.cpp tests_testplus_CXXFLAGS = $(AM_CXXFLAGS) $(NO_EFF_CXX) @@ -86,25 +95,23 @@ tests_testplus_DEPENDENCIES= $(TESTS_LDADDS) tests_testplus_LDADD= $(tests_testplus_DEPENDENCIES) $(LIBSASL) check_PROGRAMS+= tests/testplus -tests_atomsmasher_SOURCES= tests/atomsmasher.cc -tests_atomsmasher_SOURCES+= clients/generator.cc clients/execute.cc +tests_atomsmasher_SOURCES= \ + tests/atomsmasher.cc \ + clients/generator.cc \ + clients/execute.cc tests_atomsmasher_DEPENDENCIES= $(TESTS_LDADDS) - tests_atomsmasher_LDADD= $(tests_atomsmasher_DEPENDENCIES) $(LIBSASL) tests_testudp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING) tests_testudp_SOURCES= tests/mem_udp.cc tests_testudp_DEPENDENCIES= $(TESTS_LDADDS) - tests_testudp_LDADD= $(tests_testudp_DEPENDENCIES) $(LIBSASL) - -tests_startservers_SOURCES= tests/start.cc -tests_startservers_DEPENDENCIES= $(TESTS_LDADDS) -tests_startservers_LDADD= $(tests_startservers_DEPENDENCIES) $(LIBSASL) +check_PROGRAMS+= tests/testudp tests_testhashkit_SOURCES = tests/hashkit_functions.cc tests_testhashkit_DEPENDENCIES = libtest/libtest.la libhashkit/libhashkit.la tests_testhashkit_LDADD = $(tests_testhashkit_DEPENDENCIES) +check_PROGRAMS+= tests/testhashkit tests_hash_plus_SOURCES= tests/hash_plus.cc tests_hash_plus_CXXFLAGS= $(AM_CXXFLAGS) $(NO_EFF_CXX) @@ -114,10 +121,10 @@ check_PROGRAMS+= tests/hash_plus test: check -check-local: tests/var $(TEST_DOCS) test-mem test-hash memcapable +check-local: tests/var $(TEST_DOCS) memcapable @echo "Tests completed" -test-x: check-local test-plus test-memcat test-memcp test-memrm test-memerror test-memdump test-memflush test-memstat +test-x: tests/var test-plus test-memcat test-memcp test-memrm test-memerror test-memdump test-memflush test-memstat @echo "Tests completed" memcapable: clients/memcapable @@ -242,30 +249,35 @@ TESTPLUS_COMMAND= tests/testplus $(COLLECTION) $(SUITE) HASHPLUS_COMMAND= tests/hashplus $(COLLECTION) $(SUITE) +CYCLE_COMMAND= tests/cycle $(COLLECTION) $(SUITE) + ATOM_COMMAND= tests/atomsmasher $(COLLECTION) $(SUITE) UDP_COMMAND= tests/testudp $(COLLECTION) $(SUITE) HASH_COMMAND= tests/testhashkit $(COLLECTION) $(SUITE) -test-mem: tests/testapp +test-mem: tests/var tests/testapp $(MEM_COMMAND) -test-udp: tests/testudp +test-udp: tests/var tests/testudp $(UDP_COMMAND) -test-atom: tests/atomsmasher +test-atom: tests/var tests/atomsmasher $(ATOM_COMMAND) -test-plus: tests/testplus +test-plus: tests/var tests/testplus $(TESTPLUS_COMMAND) -test-hash: tests/testhashkit +test-hash: tests/var tests/testhashkit $(HASH_COMMAND) -test-hashplus: tests/hashplus +test-hashplus: tests/var tests/hashplus $(HASHPLUS_COMMAND) +test-cycle: tests/var tests/cycle + $(CYCLE_COMMAND) + pahole-mem: tests/testapp $(PAHOLE_COMMAND) $(MEM_COMMAND) @@ -287,6 +299,9 @@ gdb-hash: tests/testhashkit gdb-hashplus: tests/hashplus $(DEBUG_COMMAND) $(HASHPLUS_COMMAND) +gdb-cycle: tests/cycle + $(DEBUG_COMMAND) $(CYCLE_COMMAND) + gdb-memslap: clients/memslap $(DEBUG_COMMAND) $(MEMSLAP_COMMAND) diff --git a/tests/libmemcached_world.h b/tests/libmemcached_world.h index ea08db77..a6de23a0 100644 --- a/tests/libmemcached_world.h +++ b/tests/libmemcached_world.h @@ -9,9 +9,9 @@ * */ -#ifdef __cplusplus -extern "C" { -#endif +#pragma once + +#include /* The structure we use for the test system */ struct libmemcached_test_container_st @@ -23,11 +23,13 @@ struct libmemcached_test_container_st libmemcached_test_container_st() : parent(NULL), memc(NULL) - { - memset(&construct, 0, sizeof(server_startup_st)); - } + { } }; +#ifdef __cplusplus +extern "C" { +#endif + /* Prototypes for functions we will pass to test framework */ libmemcached_test_container_st *world_create(test_return_t *error); test_return_t world_test_startup(libmemcached_test_container_st *); @@ -50,7 +52,11 @@ libmemcached_test_container_st *world_create(test_return_t *error) { global_container.construct.count= SERVERS_TO_CREATE; global_container.construct.udp= 0; - server_startup(&global_container.construct); + if (not server_startup(&global_container.construct)) + { + *error= TEST_FAILURE; + return NULL; + } *error= TEST_SUCCESS; @@ -62,11 +68,12 @@ test_return_t world_container_startup(libmemcached_test_container_st *container) char buffer[BUFSIZ]; test_compare_got(MEMCACHED_SUCCESS, - libmemcached_check_configuration(container->construct.server_list, strlen(container->construct.server_list), + libmemcached_check_configuration(container->construct.server_list.c_str(), container->construct.server_list.size(), buffer, sizeof(buffer)), buffer); - container->parent= memcached(container->construct.server_list, strlen(container->construct.server_list)); + assert(not container->parent); + container->parent= memcached(container->construct.server_list.c_str(), container->construct.server_list.size()); test_true(container->parent); return TEST_SUCCESS; @@ -82,6 +89,9 @@ test_return_t world_container_shutdown(libmemcached_test_container_st *container test_return_t world_test_startup(libmemcached_test_container_st *container) { + assert(container); + assert(not container->memc); + assert(container->parent); container->memc= memcached_clone(NULL, container->parent); test_true(container->memc); @@ -90,6 +100,7 @@ test_return_t world_test_startup(libmemcached_test_container_st *container) test_return_t world_flush(libmemcached_test_container_st *container) { + assert(container->memc); memcached_flush(container->memc, 0); memcached_quit(container->memc); @@ -98,6 +109,7 @@ test_return_t world_flush(libmemcached_test_container_st *container) test_return_t world_pre_run(libmemcached_test_container_st *container) { + assert(container->memc); for (uint32_t loop= 0; loop < memcached_server_list_count(container->memc->servers); loop++) { memcached_server_instance_st instance= @@ -121,6 +133,7 @@ test_return_t world_post_run(libmemcached_test_container_st *container) test_return_t world_on_error(test_return_t test_state, libmemcached_test_container_st *container) { (void)test_state; + assert(container->memc); memcached_free(container->memc); container->memc= NULL; @@ -145,6 +158,8 @@ static test_return_t _runner_default(libmemcached_test_callback_fn func, libmemc { if (func) { + assert(container); + assert(container->memc); return func(container->memc); } else diff --git a/tests/mem_functions.cc b/tests/mem_functions.cc index d7402dbd..180f1e7e 100644 --- a/tests/mem_functions.cc +++ b/tests/mem_functions.cc @@ -4150,7 +4150,7 @@ static test_return_t check_for_1_2_3(memcached_st *memc) memcached_server_instance_by_position(memc, 0); if ((instance->major_version >= 1 && (instance->minor_version == 2 && instance->micro_version >= 4)) - || instance->minor_version > 2) + or instance->minor_version > 2) { return TEST_SUCCESS; } @@ -4160,17 +4160,17 @@ static test_return_t check_for_1_2_3(memcached_st *memc) static test_return_t pre_unix_socket(memcached_st *memc) { - memcached_return_t rc; struct stat buf; memcached_servers_reset(memc); + const char *socket_file= default_socket(); - if (stat("/tmp/memcached.socket", &buf)) - return TEST_SKIPPED; + test_skip(0, stat(socket_file, &buf)); - rc= memcached_server_add_unix_socket_with_weight(memc, "/tmp/memcached.socket", 0); + test_compare(MEMCACHED_SUCCESS, + memcached_server_add_unix_socket_with_weight(memc, socket_file, 0)); - return ( rc == MEMCACHED_SUCCESS ? TEST_SUCCESS : TEST_FAILURE ); + return TEST_SUCCESS; } static test_return_t pre_nodelay(memcached_st *memc) @@ -5556,6 +5556,9 @@ static test_return_t test_verbosity(memcached_st *memc) static test_return_t test_server_failure(memcached_st *memc) { + if (memcached_server_count(memc) < 2) + return TEST_SKIPPED; + memcached_server_instance_st instance= memcached_server_instance_by_position(memc, 0); memcached_st *local_memc= memcached_create(NULL); @@ -5591,7 +5594,12 @@ static test_return_t test_server_failure(memcached_st *memc) static test_return_t test_cull_servers(memcached_st *memc) { - uint32_t count = memcached_server_count(memc); + uint32_t count= memcached_server_count(memc); + + if (count < 2) + { + return TEST_SKIPPED; + } // Do not do this in your code, it is not supported. memc->servers[1].options.is_dead= true; @@ -6452,7 +6460,7 @@ test_st parser_tests[] ={ {"libmemcached_check_configuration_with_filename", 0, (test_callback_fn*)libmemcached_check_configuration_with_filename_test }, {"number_options", 0, (test_callback_fn*)parser_number_options_test }, {"randomly generated options", 0, (test_callback_fn*)random_statement_build_test }, - {"prefix_key", 0, (test_callback_fn*)parser_key_prefix_test }, + {"namespace", 0, (test_callback_fn*)parser_key_prefix_test }, {"server", 0, (test_callback_fn*)server_test }, {"bad server strings", 0, (test_callback_fn*)servers_bad_test }, {"server with weights", 0, (test_callback_fn*)server_with_weight_test }, diff --git a/tests/start.cc b/tests/start.cc deleted file mode 100644 index 6ea0e47d..00000000 --- a/tests/start.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include - -#include -#include -#include - -int main(void) -{ - server_startup_st construct; - - memset(&construct, 0, sizeof(server_startup_st)); - - construct.count= 4; - - server_startup(&construct); - - return EXIT_SUCCESS; -} -- 2.30.2