From 28602fd2f5f1c758b50cd82d4545da8e6c55f5c7 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 18 Jul 2011 10:48:40 -0700 Subject: [PATCH] Merge in all of libtest updates. --- .bzrignore | 16 +- .hgignore | 87 ----- Makefile.am | 2 +- configure.ac | 6 + libmemcached/server.cc | 8 +- libmemcached/server.h | 8 +- libtest/callbacks.h | 5 +- libtest/cmdline.cc | 64 ++++ libtest/cmdline.h | 39 +++ libtest/common.h | 4 +- libtest/failed.cc | 38 ++- libtest/framework.cc | 56 ++-- libtest/framework.h | 76 +++-- libtest/gearmand.cc | 222 +++++++++++++ libtest/gearmand.h | 43 +++ libtest/include.am | 66 +++- libtest/killpid.cc | 33 +- libtest/memcached.cc | 306 ++++++++---------- libtest/memcached.h | 48 +++ libtest/runner.cc | 75 +++++ libtest/runner.h | 16 +- libtest/server.cc | 591 ++++++++++++++++++++++++++++++---- libtest/server.h | 164 ++++++---- libtest/signal.cc | 186 +++++++++++ libtest/signal.h | 56 ++++ libtest/stream.h | 46 ++- libtest/test.cc | 184 ++++------- libtest/test.h | 83 ++++- libtest/test.hpp | 8 +- libtest/unittest.cc | 132 +++++++- libtest/wait.h | 12 +- m4/libgearmand.m4 | 16 + m4/pandora_have_libgearman.m4 | 41 --- support/libmemcached.spec.in | 2 +- tests/atomsmasher.cc | 18 +- tests/basic.cc | 3 +- tests/cycle.cc | 35 +- tests/debug.cc | 181 +++++++++++ tests/debug.h | 46 +++ tests/deprecated.cc | 3 +- tests/error_conditions.cc | 3 +- tests/hash_plus.cc | 6 +- tests/hashkit_functions.cc | 34 +- tests/include.am | 23 +- tests/libmemcached_world.h | 173 +++++----- tests/mem_functions.cc | 407 ++++++++++------------- tests/mem_udp.cc | 9 +- tests/namespace.cc | 17 +- tests/parser.cc | 33 +- tests/plus.cpp | 61 +++- tests/pool.cc | 5 +- tests/print.cc | 8 +- tests/print.h | 2 +- tests/replication.cc | 115 ++++--- tests/replication.h | 2 +- tests/string.cc | 2 +- tests/string.h | 2 +- tests/virtual_buckets.cc | 5 +- 58 files changed, 2759 insertions(+), 1173 deletions(-) delete mode 100644 .hgignore create mode 100644 libtest/cmdline.cc create mode 100644 libtest/cmdline.h create mode 100644 libtest/gearmand.cc create mode 100644 libtest/gearmand.h create mode 100644 libtest/memcached.h create mode 100644 libtest/runner.cc create mode 100644 libtest/signal.cc create mode 100644 libtest/signal.h create mode 100644 m4/libgearmand.m4 delete mode 100644 m4/pandora_have_libgearman.m4 create mode 100644 tests/debug.cc create mode 100644 tests/debug.h diff --git a/.bzrignore b/.bzrignore index 8f970c35..875ee548 100644 --- a/.bzrignore +++ b/.bzrignore @@ -18,6 +18,10 @@ */Makefile.in *TAGS .deps +.hg/ +.hgsub +.hgignore +.hgsubstate INSTALL Makefile Makefile.in @@ -52,12 +56,14 @@ config/top.h configure docs/*.[13] docs/*.html +docs/changes docs/conf.py docs/doctest/ docs/doctrees/ docs/html/ docs/linkcheck/ docs/man/* +docs/text example/memcached_light libhashkit/configure.h libmemcached-*.tar.gz @@ -72,6 +78,10 @@ libmemcached/configure.h libmemcached/dtrace_probes.h libmemcached/generated_probes.h libmemcached/memcached_configure.h +libtest/.hg/ +libtest/.hgignore +libtest/unittest +libtest/wait libtool libtool.m4 ltoptions.m4 @@ -88,6 +98,7 @@ support/libmemcached.pc support/libmemcached.spec tags tests/atomsmasher +tests/cycle tests/hash_plus tests/hashplus tests/memplus @@ -99,8 +110,3 @@ tests/testplus tests/testudp tests/var/ unittests/unittests -libtest/wait -docs/text -docs/changes -tests/cycle -libtest/unittest diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 531b2e2a..00000000 --- a/.hgignore +++ /dev/null @@ -1,87 +0,0 @@ -# Exact paths to config junk -^INSTALL$ -^aclocal.m4$ -^configure$ -^config/(config.guess|config.sub|depcomp|install-sh|ltmain.sh|missing)$ -^config.(log|status)$ -^libtool$ -^autom4te.cache$ -^libmemcached/(libmemcached_config.h(.in)?|stamp-h1)$ -^libmemcached/libmemcached_config.h.in$ -^stamp-h1$ -^lib/libmemcachedPlus.la$ -^tests/testplus$ - -# Don't bother with TAGS files -TAGS -clients/TAGS -clients/memdump -libmemcached/TAGS -libmemcachedutil/TAGS -tests/TAGS - - - -# Build artifacts -^libmemcached/libmemcached.la$ -^libmemcachedutil/libmemcachedutil.la$ -^clients/mem(cat|cp|flush|rm|slap|stat|error)$ -^tests/testapp$ -.(deps|libs)/*$ -.cmp$ -autom4te.cache/*$ -.(gz|cmp|tar|rpm|srpm)$ -support/libmemcached.pc$ -support/libmemcached.spec$ -support/libmemcached-fc.spec$ -docs/pod2htmd.tmp -docs/pod2htmi.tmp -libmemcached/memcachedplus.loT -tests/atomsmasher -tests/startservers -tests/stopservers -tests/udptest -^libmemcached/dtrace_probes.h$ -^tests/core$ - -Makefile(.in)?$ -.(deps|libs)$ -\.l?o$ - -# Generated man files -\.1$ -\.3$ - -# Merged files -\.orig$ - -#HTML files -docs/libmemcached/default.css -docs/libmemcached/memcached.html -docs/libmemcached/memcached_auto.html -docs/libmemcached/memcached_behavior.html -docs/libmemcached/memcached_callback.html -docs/libmemcached/memcached_create.html -docs/libmemcached/memcached_delete.html -docs/libmemcached/memcached_examples.html -docs/libmemcached/memcached_flush.html -docs/libmemcached/memcached_get.html -docs/libmemcached/memcached_quit.html -docs/libmemcached/memcached_result_st.html -docs/libmemcached/memcached_server_st.html -docs/libmemcached/memcached_servers.html -docs/libmemcached/memcached_set.html -docs/libmemcached/memcached_stats.html -docs/libmemcached/memcached_strerror.html -docs/libmemcached/memcached_verbosity.html -docs/libmemcached/memcached_version.html -docs/libmemcached/memcat.html -docs/libmemcached/memcp.html -docs/libmemcached/memerror.html -docs/libmemcached/memflush.html -docs/libmemcached/memrm.html -docs/libmemcached/memslap.html -docs/libmemcached/memstat.html - -# Backup files created by emacs -.*~$ diff --git a/Makefile.am b/Makefile.am index 82b19a4d..30aa1337 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,7 +60,7 @@ fedora: @cp ~/rpmbuild/RPMS/x86_64/libmemcached-$(VERSION)*.rpm . @cp ~/rpmbuild/SRPMS/libmemcached-$(VERSION)*.rpm . -generic: +generic: support/libmemcached.spec @rm -f ~/rpmbuild/RPMS/x86_64/libmemcached-$(VERSION)*.rpm @rm -f ~/rpmbuild/SRPMS/libmemcached-$(VERSION)*.rpm @cp libmemcached-$(VERSION).tar.gz ~/rpmbuild/SOURCES/ diff --git a/configure.ac b/configure.ac index c1498462..e0558a52 100644 --- a/configure.ac +++ b/configure.ac @@ -11,6 +11,7 @@ AC_PREREQ(2.59) AC_INIT([libmemcached],[0.51],[http://libmemcached.org/]) AC_CONFIG_SRCDIR([libmemcached/memcached.cc]) AC_CONFIG_AUX_DIR(config) +AC_CONFIG_MACRO_DIR(m4) PANDORA_CANONICAL_TARGET(no-vc-changelog) AC_CHECK_PROGS([YACC], ['bison'], [:]) @@ -38,6 +39,10 @@ AC_SUBST(MEMCACHED_LIBRARY_VERSION) HASHKIT_LIBRARY_VERSION=1:0:0 AC_SUBST(HASHKIT_LIBRARY_VERSION) +AC_DEFINE([HAVE_LIBMEMCACHED], [ 1 ], [dummy rule for libtest]) +AC_SUBST(HAVE_LIBMEMCACHED, 1) +AM_CONDITIONAL(HAVE_LIBMEMCACHED, true) + AH_TOP([ #ifndef CONFIG_H #define CONFIG_H @@ -122,6 +127,7 @@ ENABLE_DEPRECATED PANDORA_HAVE_LIBINNODB PANDORA_PRINT_CALLSTACK PANDORA_HAVE_SASL +WITH_LIBGEARMAN dnl The sasl functions should only be visible if we build with sasl support AS_IF([test "x$ac_cv_sasl" = "xyes"], diff --git a/libmemcached/server.cc b/libmemcached/server.cc index f5fe62e1..955e1b30 100644 --- a/libmemcached/server.cc +++ b/libmemcached/server.cc @@ -313,7 +313,7 @@ uint32_t memcached_server_count(const memcached_st *self) return self->number_of_hosts; } -const char *memcached_server_name(memcached_server_instance_st self) +const char *memcached_server_name(const memcached_server_instance_st self) { WATCHPOINT_ASSERT(self); if (not self) @@ -322,7 +322,7 @@ const char *memcached_server_name(memcached_server_instance_st self) return self->hostname; } -in_port_t memcached_server_port(memcached_server_instance_st self) +in_port_t memcached_server_port(const memcached_server_instance_st self) { WATCHPOINT_ASSERT(self); if (not self) @@ -331,7 +331,7 @@ in_port_t memcached_server_port(memcached_server_instance_st self) return self->port; } -uint32_t memcached_server_response_count(memcached_server_instance_st self) +uint32_t memcached_server_response_count(const memcached_server_instance_st self) { WATCHPOINT_ASSERT(self); if (not self) @@ -340,7 +340,7 @@ uint32_t memcached_server_response_count(memcached_server_instance_st self) return self->cursor_active; } -const char *memcached_server_type(memcached_server_instance_st ptr) +const char *memcached_server_type(const memcached_server_instance_st ptr) { if (ptr) { diff --git a/libmemcached/server.h b/libmemcached/server.h index a45616a1..fec1d48c 100644 --- a/libmemcached/server.h +++ b/libmemcached/server.h @@ -150,16 +150,16 @@ memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, const cha Operations on Single Servers. */ LIBMEMCACHED_API -uint32_t memcached_server_response_count(memcached_server_instance_st self); +uint32_t memcached_server_response_count(const memcached_server_instance_st self); LIBMEMCACHED_API -const char *memcached_server_name(memcached_server_instance_st self); +const char *memcached_server_name(const memcached_server_instance_st self); LIBMEMCACHED_API -in_port_t memcached_server_port(memcached_server_instance_st self); +in_port_t memcached_server_port(const memcached_server_instance_st self); LIBMEMCACHED_API -const char *memcached_server_type(memcached_server_instance_st ptr); +const char *memcached_server_type(const memcached_server_instance_st ptr); LIBMEMCACHED_LOCAL diff --git a/libtest/callbacks.h b/libtest/callbacks.h index ea7ae182..ea587bd9 100644 --- a/libtest/callbacks.h +++ b/libtest/callbacks.h @@ -10,10 +10,11 @@ extern "C" { #endif -typedef void* (test_callback_create_fn)(enum test_return_t*); +typedef void* (test_callback_create_fn)(libtest::server_startup_st&, test_return_t&); +typedef bool test_callback_destroy_fn(void *); typedef enum test_return_t (test_callback_fn)(void *); typedef enum test_return_t (test_callback_runner_fn)(test_callback_fn*, void *); -typedef enum test_return_t (test_callback_error_fn)(const enum test_return_t, void *); +typedef enum test_return_t (test_callback_error_fn)(const test_return_t, void *); #ifdef __cplusplus } diff --git a/libtest/cmdline.cc b/libtest/cmdline.cc new file mode 100644 index 00000000..2b67a47d --- /dev/null +++ b/libtest/cmdline.cc @@ -0,0 +1,64 @@ +/* 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 + +bool exec_cmdline(const std::string& executable, const char *args[]) +{ + std::stringstream arg_buffer; + + arg_buffer << "./libtool --mode=execute "; + + arg_buffer << executable; + for (const char **ptr= args; *ptr; ++ptr) + { + arg_buffer << " " << *ptr; + } + + arg_buffer << " > /dev/null 2>&1"; + if (system(arg_buffer.str().c_str()) == -1) + { + return false; + } + + return true; +} diff --git a/libtest/cmdline.h b/libtest/cmdline.h new file mode 100644 index 00000000..582cb4a5 --- /dev/null +++ b/libtest/cmdline.h @@ -0,0 +1,39 @@ +/* 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 exec_cmdline(const std::string& executable, const char *args[]); diff --git a/libtest/common.h b/libtest/common.h index 3874dae5..c59981aa 100644 --- a/libtest/common.h +++ b/libtest/common.h @@ -49,7 +49,7 @@ #include #include +#include +#include #include - -using namespace libtest; diff --git a/libtest/failed.cc b/libtest/failed.cc index 5ed8020a..f0f0e652 100644 --- a/libtest/failed.cc +++ b/libtest/failed.cc @@ -43,32 +43,38 @@ #include #include -struct failed_test_names_st -{ - failed_test_names_st(const char *collection_arg, const char *test_arg) : +namespace libtest { + +struct failed_st { + failed_st(const std::string collection_arg, const std::string test_arg) : collection(collection_arg), test(test_arg) - { - } + { } std::string collection; std::string test; }; -typedef std::vector Failures; - -static Failures failures; +typedef std::vector Failures; -void push_failed_test(const char *collection, const char *test) +class Failed { - failures.push_back(failed_test_names_st(collection, test)); -} +private: + Failures failures; -void print_failed_test(void) -{ - for (Failures::iterator iter= failures.begin(); iter != failures.end(); iter++) +public: + void push(const char *collection, const char *test) { - Error << "\t" << (*iter).collection << " " << (*iter).test; + failures.push_back(failed_st(collection, test)); } -} + void print_failed_test(void) + { + for (Failures::iterator iter= failures.begin(); iter != failures.end(); iter++) + { + Error << "\t" << (*iter).collection << " " << (*iter).test; + } + } +}; + +} // namespace libtest diff --git a/libtest/framework.cc b/libtest/framework.cc index dc9bddd9..baff4b8a 100644 --- a/libtest/framework.cc +++ b/libtest/framework.cc @@ -40,22 +40,6 @@ using namespace libtest; -static test_return_t _runner_default(test_callback_fn func, void *p) -{ - if (func) - { - return func(p); - } - - return TEST_SUCCESS; -} - -static Runner defualt_runners= { - _runner_default, - _runner_default, - _runner_default -}; - static test_return_t _default_callback(void *p) { (void)p; @@ -63,6 +47,8 @@ static test_return_t _default_callback(void *p) return TEST_SUCCESS; } +static Runner defualt_runners; + Framework::Framework() : collections(NULL), _create(NULL), @@ -70,20 +56,39 @@ Framework::Framework() : collection_startup(_default_callback), collection_shutdown(_default_callback), _on_error(NULL), - runner(&defualt_runners), + _runner(NULL), _creators_ptr(NULL) { } Framework::~Framework() { - if (_destroy) + if (_destroy and _destroy(_creators_ptr)) { - if (test_failed(_destroy(_creators_ptr))) - { - Error << "Failure in _destroy(), some resources may not have been cleaned up."; - } + Error << "Failure in _destroy(), some resources may not have been cleaned up."; } + + _servers.shutdown(); +} + +test_return_t Framework::Item::pre(void *arg) +{ + if (pre_run) + { + return pre_run(arg); + } + + return TEST_SUCCESS; +} + +test_return_t Framework::Item::post(void *arg) +{ + if (post_run) + { + return post_run(arg); + } + + return TEST_SUCCESS; } test_return_t Framework::Item::flush(void* arg, test_st* run) @@ -126,12 +131,17 @@ test_return_t Framework::Item::startup(void* arg) return TEST_SUCCESS; } +libtest::Runner *Framework::runner() +{ + return _runner ? _runner : &defualt_runners; +} + void* Framework::create(test_return_t& arg) { arg= TEST_SUCCESS; if (_create) { - return _creators_ptr= _create(&arg); + return _creators_ptr= _create(_servers, arg); } return NULL; diff --git a/libtest/framework.h b/libtest/framework.h index 1c9a1c1d..b504c5c1 100644 --- a/libtest/framework.h +++ b/libtest/framework.h @@ -43,23 +43,30 @@ get_world() in order to fill this structure. */ -struct Framework { +class Framework { +public: collection_st *collections; /* These methods are called outside of any collection call. */ test_callback_create_fn *_create; - test_callback_fn *_destroy; - - void* create(test_return_t& arg); + test_callback_destroy_fn *_destroy; /* This is called a the beginning of any collection run. */ test_callback_fn *collection_startup; - test_return_t startup(void*); - /* This is called a the end of any collection run. */ test_callback_fn *collection_shutdown; + void set_collection_shutdown(test_callback_error_fn *arg) + { + _on_error= arg; + } + +public: + void* create(test_return_t& arg); + + test_return_t startup(void*); + test_return_t shutdown(void* arg) { if (collection_shutdown) @@ -74,7 +81,8 @@ struct Framework { These are run before/after the test. If implemented. Their execution is not controlled by the test. */ - struct Item { + class Item { + public: /* This is called a the beginning of any run. */ test_callback_fn *_startup; @@ -86,12 +94,15 @@ struct Framework { */ test_callback_fn *_flush; + private: /* Run before and after the runnner is executed. */ test_callback_fn *pre_run; test_callback_fn *post_run; + public: + Item() : _startup(NULL), _flush(NULL), @@ -99,38 +110,35 @@ struct Framework { post_run(NULL) { } - test_return_t flush(void* arg, test_st* run); - - void set_pre(test_callback_fn *arg) + void set_startup(test_callback_fn *arg) { - pre_run= arg; + _startup= arg; } - void set_post(test_callback_fn *arg) + void set_collection(test_callback_fn *arg) { - pre_run= arg; + _flush= arg; } - test_return_t pre(void *arg) + void set_flush(test_callback_fn *arg) { - if (pre_run) - { - return pre_run(arg); - } - - return TEST_SUCCESS; + _flush= arg; } - test_return_t post(void *arg) + void set_pre(test_callback_fn *arg) { - if (post_run) - { - return post_run(arg); - } + pre_run= arg; + } - return TEST_SUCCESS; + void set_post(test_callback_fn *arg) + { + pre_run= arg; } + test_return_t pre(void *arg); + test_return_t flush(void* arg, test_st* run); + test_return_t post(void *arg); + } item; /** @@ -138,13 +146,26 @@ struct Framework { */ test_callback_error_fn *_on_error; + void set_on_error(test_callback_error_fn *arg) + { + _on_error= arg; + } + test_return_t on_error(const enum test_return_t, void *); /** Runner represents the callers for the tests. If not implemented we will use a set of default implementations. */ - Runner *runner; + libtest::Runner *_runner; + + void set_runner(libtest::Runner *arg) + { + _runner= arg; + } + + libtest::Runner *runner(); + Framework(); @@ -154,5 +175,6 @@ struct Framework { private: Framework& operator=(const Framework&); + libtest::server_startup_st _servers; void *_creators_ptr; }; diff --git a/libtest/gearmand.cc b/libtest/gearmand.cc new file mode 100644 index 00000000..c729d8ed --- /dev/null +++ b/libtest/gearmand.cc @@ -0,0 +1,222 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * libtest + * + * 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 "util/instance.h" +#include "util/operation.h" + +using namespace gearman_util; +using namespace libtest; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifndef __INTEL_COMPILER +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +class GetPid : public Instance::Finish +{ +private: + pid_t _pid; + +public: + GetPid() : + _pid(-1) + { } + + pid_t pid() + { + return _pid; + } + + + bool call(const bool success, const std::string &response) + { + _pid= -1; + + if (success and response.size()) + { + _pid= atoi(response.c_str()); + } + + if (_pid < 1) + { + _pid= -1; + return true; + } + + return false; + } +}; + +using namespace libtest; + +class Gearmand : public Server +{ +private: +public: + Gearmand(const std::string& host_arg, in_port_t port_arg) : + Server(host_arg, port_arg) + { } + + pid_t get_pid(bool error_is_ok) + { + GetPid *get_instance_pid; + Instance instance(hostname(), port()); + instance.set_finish(get_instance_pid= new GetPid); + + instance.push(new Operation(test_literal_param("getpid\r\n"), true)); + + if (not instance.run() and not error_is_ok) + { + Error << "Failed to obtain pid of server"; + } + + _pid= get_instance_pid->pid(); + + return _pid; + } + + bool ping() + { + gearman_client_st *client= gearman_client_create(NULL); + if (not client) + { + Error << "Could not allocate memory for gearman_client_create()"; + return false; + } + gearman_client_set_timeout(client, 1000); + + if (gearman_success(gearman_client_add_server(client, hostname().c_str(), port()))) + { + gearman_return_t rc= gearman_client_echo(client, gearman_literal_param("This is my echo test")); + + if (gearman_success(rc)) + { + gearman_client_free(client); + return true; + } + } + + gearman_client_free(client); + + return false;; + } + + const char *name() + { + return "gearmand"; + }; + + const char *executable() + { + return GEARMAND_BINARY; + } + + const char *pid_file_option() + { + return "--pid-file="; + } + + const char *daemon_file_option() + { + return "--daemon"; + } + + const char *log_file_option() + { + return "-vvvvv --log-file="; + } + + const char *port_option() + { + return "--port="; + } + + bool is_libtool() + { + return true; + } + + bool build(int argc, const char *argv[]); +}; + + +#include + +bool Gearmand::build(int argc, const char *argv[]) +{ + std::stringstream arg_buffer; + + if (getuid() == 0 or geteuid() == 0) + { + arg_buffer << " -u root "; + } + + for (int x= 1 ; x < argc ; x++) + { + arg_buffer << " " << argv[x] << " "; + } + + set_extra_args(arg_buffer.str()); + + return true; +} + +namespace libtest { + +Server *build_gearmand(const char *hostname, in_port_t try_port) +{ + return new Gearmand(hostname, try_port); +} + +} diff --git a/libtest/gearmand.h b/libtest/gearmand.h new file mode 100644 index 00000000..6d311ead --- /dev/null +++ b/libtest/gearmand.h @@ -0,0 +1,43 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * libtest + * + * 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 + +namespace libtest { + +Server *build_gearmand(const char *hostname, in_port_t try_port); + +} diff --git a/libtest/include.am b/libtest/include.am index 398037ba..ebc9adf7 100644 --- a/libtest/include.am +++ b/libtest/include.am @@ -7,11 +7,14 @@ # # included from Top Level Makefile.am # All paths should be given relative to the root -# +# LIBUTEST_TMP = ${abs_top_builddir}/tests/var/tmp/ VALGRIND_COMMAND= $(LIBTOOL) --mode=execute valgrind --error-exitcode=1 --leak-check=yes --show-reachable=yes --track-fds=yes --malloc-fill=A5 --free-fill=DE +HELGRIND_COMMAND= $(LIBTOOL) --mode=execute valgrind --tool=helgrind --read-var-info=yes --error-exitcode=1 +DRD_COMMAND= $(LIBTOOL) --mode=execute valgrind --tool=drd +GDB_COMMAND= $(LIBTOOL) --mode=execute gdb CLEANFILES+= \ tests/var/log/* \ @@ -20,16 +23,20 @@ CLEANFILES+= \ noinst_HEADERS+= \ libtest/callbacks.h \ + libtest/cmdline.h \ libtest/collection.h \ libtest/common.h \ libtest/core.h \ libtest/error.h \ libtest/failed.h \ libtest/framework.h \ + libtest/gearmand.h \ libtest/get.h \ libtest/killpid.h \ + libtest/memcached.h \ libtest/runner.h \ libtest/server.h \ + libtest/signal.h \ libtest/stats.h \ libtest/strerror.h \ libtest/test.h \ @@ -37,18 +44,32 @@ noinst_HEADERS+= \ libtest/visibility.h \ libtest/wait.h -noinst_LTLIBRARIES+= libtest/libserver.la -libtest_libserver_la_SOURCES= \ - libtest/killpid.cc \ - libtest/memcached.cc \ - libtest/server.cc - noinst_LTLIBRARIES+= libtest/libtest.la -libtest_libtest_la_SOURCES=\ - libtest/framework.cc \ - libtest/test.cc +libtest_libtest_la_SOURCES= \ + libtest/cmdline.cc \ + libtest/framework.cc \ + libtest/killpid.cc \ + libtest/runner.cc \ + libtest/server.cc \ + libtest/signal.cc \ + libtest/test.cc + libtest_libtest_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} -DBUILDING_LIBTEST libtest_libtest_la_CXXFLAGS= ${AM_CXXFLAGS} ${NO_CONVERSION} -DBUILDING_LIBTEST +libtest_libtest_la_LIBADD= + +LIBTEST_LDADD= libtest/libtest.la + +if HAVE_LIBMEMCACHED +LIBTEST_LDADD+= $(libmemcached_LIBS) -lmemcachedutil +libtest_libtest_la_SOURCES+= libtest/memcached.cc +endif + +if HAVE_LIBGEARMAN +LIBTEST_LDADD+= libgearman/libgearman.la +libtest_libtest_la_SOURCES+= libtest/gearmand.cc +libtest_libtest_la_SOURCES+= util/instance.cc +endif clearn-var: @rm -f tests/var/log/* @@ -68,12 +89,29 @@ tests/var/tmp: tests/var/run: $(mkdir_p) tests/var/run + +libtest_unittest_CFLAGS= libtest_unittest_LDADD= \ - libtest/libtest.la -libtest_unittest_SOURCES= \ - libtest/unittest.cc -noinst_PROGRAMS+= libtest/unittest + ${LIBTEST_LDADD} \ + libtest/libtest.la +libtest_unittest_SOURCES= libtest/unittest.cc check_PROGRAMS+= libtest/unittest +noinst_PROGRAMS+= libtest/unittest +test-unittest: libtest/unittest + @libtest/unittest + +valgrind-unittest: libtest/unittest + @$(VALGRIND_COMMAND) libtest/unittest + +gdb-unittest: libtest/unittest + @$(GDB_COMMAND) libtest/unittest + +helgrind-unittest: libtest/unittest + @$(HELGRIND_COMMAND) libtest/unittest + +drd-unittest: libtest/unittest + @$(DRD_COMMAND) libtest/unittest libtest_wait_SOURCES= libtest/wait.cc noinst_PROGRAMS+= libtest/wait + diff --git a/libtest/killpid.cc b/libtest/killpid.cc index bacec8f1..bc9a5594 100644 --- a/libtest/killpid.cc +++ b/libtest/killpid.cc @@ -39,6 +39,11 @@ #include #include #include +#include +#include +#include +#include + #include #include @@ -47,38 +52,42 @@ using namespace libtest; bool kill_pid(pid_t pid_arg) { - if (pid_arg <= 1) + assert(pid_arg > 0); + if (pid_arg < 1) + { + Error << "Invalid pid:" << pid_arg; return false; + } if ((::kill(pid_arg, SIGTERM) == -1)) { switch (errno) { case EPERM: - perror(__func__); - Error << __func__ << " -> Does someone else have a process running locally for " << int(pid_arg) << "?"; + Error << "Does someone else have a process running locally for " << int(pid_arg) << "?"; return false; case ESRCH: - perror(__func__); Error << "Process " << int(pid_arg) << " not found."; return false; default: case EINVAL: - perror(__func__); + Error << "kill() " << strerror(errno); return false; } } int status= 0; - pid_t pid= waitpid(pid_arg, &status, 0); - if (pid == -1) + if (waitpid(pid_arg, &status, 0) == -1) { switch (errno) { + // Just means that the server has already gone away case ECHILD: - return true; + { + return true; + } } Error << "Error occured while waitpid(" << strerror(errno) << ") on pid " << int(pid_arg); @@ -86,13 +95,7 @@ bool kill_pid(pid_t pid_arg) return false; } - if (WIFEXITED(status)) - return true; - - if (WCOREDUMP(status)) - return true; - - return false; + return true; } diff --git a/libtest/memcached.cc b/libtest/memcached.cc index 6c019f9d..e3d90947 100644 --- a/libtest/memcached.cc +++ b/libtest/memcached.cc @@ -1,9 +1,8 @@ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: * - * Libmemcached library + * libtest * * 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 @@ -35,221 +34,188 @@ * */ +#include -/* - Startup, and shutdown the memcached servers. -*/ - -#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10 +#include +#include -#include +using namespace libtest; +#include +#include #include #include #include -#include -#include +#include #include -#include +#include +#include #include -#include - -#include -#include #include -#include #include -#define SOCKET_FILE "/tmp/memcached.socket" +#include -static pid_t __getpid(server_st& server) -{ - memcached_return_t rc; - pid_t pid= libmemcached_util_getpid(server.hostname(), server.port(), &rc); - return pid; -} +#ifndef __INTEL_COMPILER +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif -static bool __ping(server_st& server) -{ - memcached_return_t rc; - bool ret= libmemcached_util_ping(server.hostname(), server.port(), &rc); - return ret; -} +using namespace libtest; -static bool cycle_server(server_st *server) +class Memcached : public Server { - while (1) +public: + Memcached(const std::string& host_arg, const in_port_t port_arg, const bool is_socket_arg) : + Server(host_arg, port_arg, is_socket_arg) + { } + + pid_t get_pid(bool error_is_ok) { - if (libmemcached_util_ping(server->hostname(), server->port(), NULL)) + // Memcached is slow to start, so we need to do this + if (not pid_file().empty()) { - // 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); + Wait wait(pid_file(), 0); - if (pid > 0 and kill_pid(pid)) + if (not wait.successful()) { - Error << "Killed existing server," << *server << " with pid:" << pid; - continue; + Error << "Pidfile was not found:" << pid_file(); + return -1; } - else if (libmemcached_util_flush(server->hostname(), server->port(), NULL)) // If we can flush it, we will just use it - { - Error << "Found server on port " << int(server->port()) << ", flushed it!"; - server->set_used(); - return true; - } // No idea what is wrong here, so we need to find a different port - else + } + + memcached_return_t rc; + if (has_socket()) + { + _pid= libmemcached_util_getpid(socket().c_str(), port(), &rc); + } + else + { + _pid= libmemcached_util_getpid(hostname().c_str(), port(), &rc); + } + + if ((memcached_failed(rc) or _pid < 1) and not error_is_ok) + { + Error << "libmemcached_util_getpid(" << memcached_strerror(NULL, rc) << ") pid: " << _pid << " for:" << *this; + } + + return _pid; + } + + bool ping() + { + // Memcached is slow to start, so we need to do this + if (not pid_file().empty()) + { + Wait wait(pid_file(), 0); + + if (not wait.successful()) { - return false; + Error << "Pidfile was not found:" << pid_file(); + return -1; } } - break; + memcached_return_t rc; + bool ret; + if (has_socket()) + { + ret= libmemcached_util_ping(socket().c_str(), 0, &rc); + } + else + { + ret= libmemcached_util_ping(hostname().c_str(), port(), &rc); + } + + if (memcached_failed(rc) or not ret) + { + Error << "libmemcached_util_ping(" << memcached_strerror(NULL, rc) << ")"; + } + return ret; } - return true; -} + const char *name() + { + return "memcached"; + }; -bool server_startup(server_startup_st *construct) -{ - Logn(); + const char *executable() + { + return MEMCACHED_BINARY; + } - if (getenv(((char *)"MEMCACHED_SERVERS"))) + const char *pid_file_option() { - construct->server_list= getenv(((char *)"MEMCACHED_SERVERS")); - Log << "MEMCACHED_SERVERS " << construct->server_list; - construct->count= 0; + return "-P "; } - else + + const char *socket_file_option() const { - std::string server_config_string; + return "-s "; + } - uint32_t port_base= 0; - for (uint32_t x= 0; x < uint32_t(construct->count -1); x++) - { - server_st *server= NULL; + const char *daemon_file_option() + { + return "-d"; + } - { - char *var; - char variable_buffer[1024]; - - snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x); - - if ((var= getenv(variable_buffer))) - { - server= new server_st((in_port_t)atoi(var), __getpid, __ping); - } - else - { - server= new server_st(in_port_t(x +TEST_PORT_BASE +port_base), __getpid, __ping); - - while (not cycle_server(server)) - { - Error << "Found server " << *server << ", could not flush it, so trying next port."; - port_base++; - server->set_port(in_port_t(x +TEST_PORT_BASE +port_base)); - } - } - } + const char *log_file_option() + { + return NULL; + } - if (server->is_used()) - { - Log << "Using server at : " << server; - } - else - { - char buffer[FILENAME_MAX]; - if (x == 0) - { - 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 -t 1 -p %u -U %u", - MEMCACHED_BINARY, server->port(), server->port()); - } - server->set_command(buffer); - - if (not server->start()) - { - Error << "Failed system(" << buffer << ")"; - delete server; - return false; - } - Log << "STARTING SERVER: " << buffer << " pid:" << server->pid(); - } - construct->push_server(server); + const char *port_option() + { + return "-p "; + } - if (x == 0) - { - assert(server->has_port()); - set_default_port(server->port()); - } + bool is_libtool() + { + return false; + } - char port_str[NI_MAXSERV]; - snprintf(port_str, sizeof(port_str), "%u", int(server->port())); + // Memcached's pidfile is broken + bool broken_pid_file() + { + return true; + } - server_config_string+= "--server="; - server_config_string+= server->hostname(); - server_config_string+= ":"; - server_config_string+= port_str; - server_config_string+= " "; - } + bool build(int argc, const char *argv[]); +}; - // Socket - { - std::string socket_file(SOCKET_FILE); - char *var; +#include - if ((var= getenv("LIBMEMCACHED_SOCKET"))) - { - socket_file= var; - } +bool Memcached::build(int argc, const char *argv[]) +{ + std::stringstream arg_buffer; - server_st *server= new server_st(SOCKET_FILE, __getpid, __ping); + if (getuid() == 0 or geteuid() == 0) + { + arg_buffer << " -u root "; + } - if (not cycle_server(server)) - { - Error << "Found server " << server << ", could not flush it, failing since socket file is not available."; - return false; - } + for (int x= 1 ; x < argc ; x++) + { + arg_buffer << " " << argv[x] << " "; + } - if (server->is_used()) - { - Log << "Using server at : " << *server; - } - else - { - char buffer[FILENAME_MAX]; - snprintf(buffer, sizeof(buffer), "%s -d -t 1 -s %s", MEMCACHED_BINARY, SOCKET_FILE); - server->set_command(buffer); - - if (not server->start()) - { - Error << "Failed system(" << buffer << ")"; - delete server; - return false; - } - Log << "STARTING SERVER: " << buffer << " pid:" << server->pid(); - } - set_default_socket(server->hostname()); - construct->push_server(server); + set_extra_args(arg_buffer.str()); - { - server_config_string+= "--socket=\""; - server_config_string+= server->hostname(); - server_config_string+= "\" "; - } - } + return true; +} - server_config_string.resize(server_config_string.size() -1); // Remove final space - construct->server_list= server_config_string; - } +namespace libtest { - Logn(); +Server *build_memcached(const std::string& hostname, const in_port_t try_port) +{ + return new Memcached(hostname, try_port, false); +} - srandom((unsigned int)time(NULL)); +Server *build_memcached_socket(const std::string& hostname, const in_port_t try_port) +{ + return new Memcached(hostname, try_port, true); +} - return true; } + diff --git a/libtest/memcached.h b/libtest/memcached.h new file mode 100644 index 00000000..5620400a --- /dev/null +++ b/libtest/memcached.h @@ -0,0 +1,48 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * libtest + * + * 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 + +namespace libtest { + +LIBTEST_API +Server *build_memcached(const std::string& hostname, const in_port_t try_port); + +LIBTEST_API +Server *build_memcached_socket(const std::string& socket_file, const in_port_t try_port); + +} + diff --git a/libtest/runner.cc b/libtest/runner.cc new file mode 100644 index 00000000..808fb13c --- /dev/null +++ b/libtest/runner.cc @@ -0,0 +1,75 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest, libtest + * + * 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 + +namespace libtest { + +Runner::Runner() +{ +} + +test_return_t Runner::run(test_callback_fn* func, void *object) +{ + if (func) + { + return func(object); + } + + return TEST_SUCCESS; +} + +test_return_t Runner::pre(test_callback_fn* func, void *object) +{ + if (func) + { + return func(object); + } + + return TEST_SUCCESS; +} + +test_return_t Runner::post(test_callback_fn* func, void *object) +{ + if (func) + { + return func(object); + } + + return TEST_SUCCESS; +} + +} // namespace libtest diff --git a/libtest/runner.h b/libtest/runner.h index 8678c4c0..8eb5480a 100644 --- a/libtest/runner.h +++ b/libtest/runner.h @@ -7,13 +7,21 @@ #pragma once +namespace libtest { + /** Structure which houses the actual callers for the test cases contained in the collections. */ -struct Runner { - test_callback_runner_fn *pre; - test_callback_runner_fn *run; - test_callback_runner_fn *post; +class Runner { +public: + virtual test_return_t run(test_callback_fn* func, void *object); + virtual test_return_t pre(test_callback_fn* func, void *object); + virtual test_return_t post(test_callback_fn* func, void *object); + + Runner(); + + virtual ~Runner() { } }; +} // namespace Runner diff --git a/libtest/server.cc b/libtest/server.cc index f82aa299..ed7e87f1 100644 --- a/libtest/server.cc +++ b/libtest/server.cc @@ -1,6 +1,6 @@ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: * - * Libmemcached library + * Libtest library * * Copyright (C) 2011 Data Differential, http://datadifferential.com/ * @@ -34,16 +34,39 @@ * */ -#include -#include -#include +#include + #include +#include +#include +#include + +#include +#include +#include + +// trim from end +static inline std::string &rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} #include +#include #include +#ifdef HAVE_LIBGEARMAN +#include +#endif + +#ifdef HAVE_LIBMEMCACHED +#include +#endif + +namespace libtest { -std::ostream& operator<<(std::ostream& output, const server_st &arg) +std::ostream& operator<<(std::ostream& output, const Server &arg) { if (arg.is_socket()) { @@ -53,153 +76,589 @@ std::ostream& operator<<(std::ostream& output, const server_st &arg) { output << arg.hostname() << ":" << arg.port(); } + + if (arg.has_pid()) + { + output << " Pid:" << arg.pid(); + } + + if (arg.has_socket()) + { + output << " Socket:" << arg.socket(); + } + + if (not arg.running().empty()) + { + output << " Exec:" << arg.running(); + } + + return output; // for multiple << operators } -static void global_sleep(void) +void Server::nap(void) { - static struct timespec global_sleep_value= { 0, 50000 }; - #ifdef WIN32 sleep(1); #else + struct timespec global_sleep_value= { 0, 50000 }; nanosleep(&global_sleep_value, NULL); #endif } -server_st::server_st(in_port_t port_arg, test_server_getpid *get_pid_arg, test_server_ping *ping_arg) : - _used(false), +Server::Server(const std::string& host_arg, const in_port_t port_arg, bool is_socket_arg) : + _is_socket(is_socket_arg), _pid(-1), _port(port_arg), - __get_pid(get_pid_arg), - __ping(ping_arg), - _hostname("localhost") + _hostname(host_arg) { - pid_file[0]= 0; } -server_st::server_st(const std::string &socket_file, test_server_getpid *get_pid_arg, test_server_ping *ping_arg) : - _used(false), - _pid(-1), - _port(0), - __get_pid(get_pid_arg), - __ping(ping_arg), - _hostname(socket_file) +Server::~Server() { - pid_file[0]= 0; + if (has_pid() and not kill()) + { + Error << "Unable to kill:" << *this; + } } +std::string server_startup_st::option_string() const +{ + std::string temp= server_list; + rtrim(temp); + return temp; +} -server_st::~server_st() +// If the server exists, kill it +bool Server::cycle() { - if (has_pid()) + uint32_t limit= 3; + + // Try to ping, and kill the server #limit number of times + pid_t current_pid; + while (--limit and (current_pid= get_pid()) != -1) { - kill(); + if (kill()) + { + Log << "Killed existing server," << *this << " with pid:" << current_pid; + nap(); + continue; + } + } + + // For whatever reason we could not kill it, and we reached limit + if (limit == 0) + { + Error << "Reached limit, could not kill server pid:" << current_pid; + return false; + } + + return true; +} + +// Grab a one off command +bool Server::command(std::string& command_arg) +{ + rebuild_base_command(); + + command_arg+= _base_command; + + if (args(command_arg)) + { + return true; } + + return false; } -bool server_st::start() +bool Server::start() { - assert(not _command.empty()); + // If we find that we already have a pid then kill it. + if (has_pid() and not kill()) + { + Error << "Could not kill() existing server during start()"; + return false; + } assert(not has_pid()); - if (has_pid()) + _running.clear(); + if (not command(_running)) + { + Error << "Could not build command()"; return false; + } - if (system(_command.c_str()) == -1) + if (system(_running.c_str()) == -1) + { + Error << "system() failed:" << strerror(errno); + _running.clear(); return false; + } - int count= 30; + if (pid_file_option() and not pid_file().empty()) + { + Wait wait(pid_file()); + + if (not wait.successful()) + { + Error << "Unable to open pidfile: " << pid_file(); + } + } + + int count= 5; while (not ping() and --count) { - global_sleep(); + nap(); } if (count == 0) { + Error << "Failed to ping() server once started:" << *this; + _running.clear(); return false; } - _pid= get_pid(); + // A failing get_pid() at this point is considered an error + _pid= get_pid(false); return has_pid(); } -void server_st::reset_pid() +void Server::reset_pid() { - pid_file[0]= 0; + _running.clear(); + _pid_file.clear(); _pid= -1; } -pid_t server_st::pid() +pid_t Server::pid() +{ + return _pid; +} + +bool Server::set_socket_file() { - if (not has_pid()) + char file_buffer[FILENAME_MAX]; + file_buffer[0]= 0; + + if (broken_pid_file()) + { + snprintf(file_buffer, sizeof(file_buffer), "/tmp/%s.socketXXXXXX", name()); + } + else { - _pid= get_pid(); + snprintf(file_buffer, sizeof(file_buffer), "tests/var/run/%s.socketXXXXXX", name()); } - return _pid; + int fd; + if ((fd= mkstemp(file_buffer)) == -1) + { + perror(file_buffer); + return false; + } + close(fd); + unlink(file_buffer); + + _socket= file_buffer; + + return true; } +bool Server::set_pid_file() +{ + char file_buffer[FILENAME_MAX]; + file_buffer[0]= 0; + + if (broken_pid_file()) + { + snprintf(file_buffer, sizeof(file_buffer), "/tmp/%s.pidXXXXXX", name()); + } + else + { + snprintf(file_buffer, sizeof(file_buffer), "tests/var/run/%s.pidXXXXXX", name()); + } + + int fd; + if ((fd= mkstemp(file_buffer)) == -1) + { + perror(file_buffer); + return false; + } + close(fd); + unlink(file_buffer); + + _pid_file= file_buffer; -bool server_st::kill() + return true; +} + +bool Server::set_log_file() { - if (is_used()) + char file_buffer[FILENAME_MAX]; + file_buffer[0]= 0; + + snprintf(file_buffer, sizeof(file_buffer), "tests/var/log/%s.logXXXXXX", name()); + int fd; + if ((fd= mkstemp(file_buffer)) == -1) + { + perror(file_buffer); return false; + } + close(fd); + + _log_file= file_buffer; + + return true; +} - if ((_pid= get_pid())) +void Server::rebuild_base_command() +{ + _base_command.clear(); + if (is_libtool()) { - 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(); + _base_command+= "./libtool --mode=execute "; + } - return true; + if (is_debug()) + { + _base_command+= "gdb "; + } + else if (is_valgrind()) + { + _base_command+= "valgrind --log-file=tests/var/tmp/valgrind.out --leak-check=full --show-reachable=yes "; + } + + _base_command+= executable(); +} + +void Server::set_extra_args(const std::string &arg) +{ + _extra_args= arg; +} + +bool Server::args(std::string& options) +{ + std::stringstream arg_buffer; + + // Set a log file if it was requested (and we can) + if (getenv("LIBTEST_LOG") and log_file_option()) + { + if (not set_log_file()) + return false; + + arg_buffer << " " << log_file_option() << _log_file; + } + + // Update pid_file + if (pid_file_option()) + { + if (not set_pid_file()) + return false; + + arg_buffer << " " << pid_file_option() << pid_file(); + } + + assert(daemon_file_option()); + if (daemon_file_option()) + { + arg_buffer << " " << daemon_file_option(); + } + + if (_is_socket and socket_file_option()) + { + if (not set_socket_file()) + return false; + + arg_buffer << " " << socket_file_option() << "\"" << _socket << "\""; } -#if 0 - else if (pid_file[0]) + + assert(port_option()); + if (port_option() and _port > 0) + { + arg_buffer << " " << port_option() << _port; + } + + options+= arg_buffer.str(); + + if (not _extra_args.empty()) + options+= _extra_args; + + return true; +} + +bool Server::is_debug() const +{ + return bool(getenv("LIBTEST_MANUAL_GDB")); +} + +bool Server::is_valgrind() const +{ + return bool(getenv("LIBTEST_MANUAL_VALGRIND")); +} + +bool Server::kill() +{ + if (has_pid() and kill_pid(_pid)) // If we kill it, reset { - kill_file(pid_file); + if (broken_pid_file() and not pid_file().empty()) + { + unlink(pid_file().c_str()); + } + reset_pid(); return true; } -#endif return false; } -void server_startup_st::push_server(server_st *arg) +void server_startup_st::push_server(Server *arg) { servers.push_back(arg); + + char port_str[NI_MAXSERV]; + snprintf(port_str, sizeof(port_str), "%u", int(arg->port())); + + std::string server_config_string; + if (arg->has_socket()) + { + server_config_string+= "--socket="; + server_config_string+= '"'; + server_config_string+= arg->socket(); + server_config_string+= '"'; + server_config_string+= " "; + } + else + { + server_config_string+= "--server="; + server_config_string+= arg->hostname(); + server_config_string+= ":"; + server_config_string+= port_str; + server_config_string+= " "; + } + + server_list+= server_config_string; + } -void server_startup_st::shutdown() +Server* server_startup_st::pop_server() { - for (std::vector::iterator iter= servers.begin(); iter != servers.end(); iter++) - { - if ((*iter)->is_used()) - continue; + Server *tmp= servers.back(); + servers.pop_back(); + return tmp; +} - (*iter)->kill(); +void server_startup_st::shutdown(bool remove) +{ + if (remove) + { + for (std::vector::iterator iter= servers.begin(); iter != servers.end(); iter++) + { + delete *iter; + } + servers.clear(); + } + else + { + for (std::vector::iterator iter= servers.begin(); iter != servers.end(); iter++) + { + if ((*iter)->has_pid() and not (*iter)->kill()) + { + Error << "Unable to kill:" << *(*iter); + } + } } } server_startup_st::~server_startup_st() { - for (std::vector::iterator iter= servers.begin(); iter != servers.end(); iter++) + shutdown(true); +} + +bool server_startup_st::is_debug() const +{ + return bool(getenv("LIBTEST_MANUAL_GDB")); +} + +bool server_startup_st::is_valgrind() const +{ + return bool(getenv("LIBTEST_MANUAL_VALGRIND")); +} + +bool server_startup(server_startup_st& construct, const std::string& server_type, in_port_t try_port, int argc, const char *argv[]) +{ + Logn(); + + // Look to see if we are being provided ports to use + { + char variable_buffer[1024]; + snprintf(variable_buffer, sizeof(variable_buffer), "LIBTEST_PORT_%lu", (unsigned long)construct.count()); + + char *var; + if ((var= getenv(variable_buffer))) + { + in_port_t tmp= in_port_t(atoi(var)); + + if (tmp > 0) + try_port= tmp; + } + } + + Server *server= NULL; + if (0) + { } + else if (server_type.compare("gearmand") == 0) + { +#ifdef GEARMAND_BINARY + #ifdef HAVE_LIBGEARMAN + server= build_gearmand("localhost", try_port); + #else + Error << "Libgearman was not found"; + #endif +#else + Error << "No gearmand binary is available"; +#endif + } + else if (server_type.compare("memcached") == 0) + { +#ifdef MEMCACHED_BINARY +#ifdef HAVE_LIBMEMCACHED + server= build_memcached("localhost", try_port); +#else + Error << "Libmemcached was not found"; +#endif +#else + Error << "No memcached binary is available"; +#endif + } + else + { + Error << "Failed to start " << server_type << ", no support was found to be compiled in for it."; + } + + if (server == NULL) + { + Error << "Failure occured while creating server: " << server_type; + return false; + } + + /* + We will now cycle the server we have created. + */ + if (not server->cycle()) + { + Error << "Could not start up server " << *server; + delete server; + return false; + } + + server->build(argc, argv); + + if (construct.is_debug()) + { + Log << "Pausing for startup, hit return when ready."; + std::string gdb_command= server->base_command(); + std::string options; + Log << "run " << server->args(options); + getchar(); + } + else if (not server->start()) + { + Error << "Failed to start " << *server; + delete server; + return false; + } + else + { + Log << "STARTING SERVER(pid:" << server->pid() << "): " << server->running(); + } + + construct.push_server(server); + + if (default_port() == 0) { - delete *iter; + assert(server->has_port()); + set_default_port(server->port()); } - servers.clear(); + + Logn(); + + return true; } -void server_shutdown(server_startup_st *construct) +bool server_startup_st::start_socket_server(const std::string& server_type, const in_port_t try_port, int argc, const char *argv[]) { - if (not construct) - return; + Logn(); - construct->shutdown(); + Server *server= NULL; + if (0) + { } + else if (server_type.compare("gearmand") == 0) + { + Error << "Socket files are not supported for gearmand yet"; + } + else if (server_type.compare("memcached") == 0) + { +#ifdef MEMCACHED_BINARY +#ifdef HAVE_LIBMEMCACHED + server= build_memcached_socket("localhost", try_port); +#else + Error << "Libmemcached was not found"; +#endif +#else + Error << "No memcached binary is available"; +#endif + } + else + { + Error << "Failed to start " << server_type << ", no support was found to be compiled in for it."; + } + + if (server == NULL) + { + Error << "Failure occured while creating server: " << server_type; + return false; + } + + /* + We will now cycle the server we have created. + */ + if (not server->cycle()) + { + Error << "Could not start up server " << *server; + delete server; + return false; + } + + server->build(argc, argv); + + if (is_debug()) + { + Log << "Pausing for startup, hit return when ready."; + std::string gdb_command= server->base_command(); + std::string options; + Log << "run " << server->args(options); + getchar(); + } + else if (not server->start()) + { + Error << "Failed to start " << *server; + delete server; + return false; + } + else + { + Log << "STARTING SERVER(pid:" << server->pid() << "): " << server->running(); + } + + push_server(server); + + set_default_socket(server->socket().c_str()); + + Logn(); + + return true; } + +} // namespace libtest diff --git a/libtest/server.h b/libtest/server.h index 00f29cd8..f13cad07 100644 --- a/libtest/server.h +++ b/libtest/server.h @@ -9,6 +9,8 @@ #pragma once +#include +#include #include #include #include @@ -16,64 +18,84 @@ #include #include -#define SERVERS_TO_CREATE 5 +namespace libtest { -struct server_st; - -typedef pid_t (test_server_getpid)(server_st &); -typedef bool (test_server_ping)(server_st &); - -struct server_st { +struct Server { private: - bool _used; + bool _is_socket; + std::string _socket; + std::string _pid_file; + std::string _log_file; + std::string _base_command; // executable command which include libtool, valgrind, gdb, etc + std::string _running; // Current string being used for system() + +protected: pid_t _pid; in_port_t _port; - char pid_file[FILENAME_MAX]; // Did we start it, or was it just sitting there? - std::string _command; - test_server_getpid *__get_pid; - test_server_ping *__ping; std::string _hostname; + std::string _extra_args; public: - server_st(in_port_t port_arg, test_server_getpid *, test_server_ping *); + Server(const std::string& hostname, const in_port_t port_arg, const bool is_socket_arg= false); + + virtual ~Server(); - server_st(const std::string &socket_file, test_server_getpid *, test_server_ping *); + virtual const char *name()= 0; + virtual const char *executable()= 0; + virtual const char *port_option()= 0; + virtual const char *pid_file_option()= 0; + virtual const char *daemon_file_option()= 0; + virtual const char *log_file_option()= 0; + virtual bool is_libtool()= 0; - void set_methods(test_server_getpid *get_pid_arg, test_server_ping *ping_arg) + virtual const char *socket_file_option() const { - __get_pid= get_pid_arg; - __ping= ping_arg; + return NULL; } - const char *hostname() const + virtual bool broken_pid_file() { - if (_hostname.empty()) - return ""; + return false; + } - return _hostname.c_str(); + const std::string& pid_file() const + { + return _pid_file; } - bool ping() + const std::string& base_command() const { - if (__ping) - return __ping(*this); + return _base_command; + } - return false; + const std::string& log_file() const + { + return _log_file; } - pid_t get_pid() + const std::string& hostname() const { - if (__get_pid) - return _pid= __get_pid(*this); + return _hostname; + } - return -1; + const std::string& socket() const + { + return _socket; } - void set_port(in_port_t arg) + bool has_socket() const { - _port= arg; + return _is_socket; } + bool cycle(); + + virtual bool ping()= 0; + + virtual pid_t get_pid(bool error_is_ok= true)= 0; + + virtual bool build(int argc, const char *argv[])= 0; + in_port_t port() const { return _port; @@ -84,26 +106,26 @@ public: return (_port != 0); } - void set_command(const char *arg) + // Reset a server if another process has killed the server + void reset() { - _command= arg; + _pid= -1; + _pid_file.clear(); + _log_file.clear(); } - void set_used() - { - _used= true; - } + void set_extra_args(const std::string &arg); + + bool args(std::string& options); pid_t pid(); - bool is_used() const + pid_t pid() const { - return _used; + return _pid; } - ~server_st(); - - bool has_pid() + bool has_pid() const { return (_pid > 1); } @@ -113,41 +135,67 @@ public: return _hostname[0] == '/'; } + const std::string running() const + { + return _running; + } + + std::string log_and_pid(); + bool kill(); bool start(); + bool command(std::string& command_arg); + +protected: + void nap(); private: + bool is_valgrind() const; + bool is_debug() const; + bool set_log_file(); + bool set_pid_file(); + bool set_socket_file(); + void rebuild_base_command(); void reset_pid(); }; -std::ostream& operator<<(std::ostream& output, const server_st &arg); +std::ostream& operator<<(std::ostream& output, const libtest::Server &arg); -struct server_startup_st +class server_startup_st { - uint32_t count; - uint8_t udp; +private: std::string server_list; - std::vector servers; + +public: + + uint8_t udp; + std::vector servers; server_startup_st() : - count(SERVERS_TO_CREATE), udp(0) { } - void shutdown(); - void push_server(server_st *); + bool start_socket_server(const std::string& server_type, const in_port_t try_port, int argc, const char *argv[]); + + std::string option_string() const; + + size_t count() const + { + return servers.size(); + } + + bool is_debug() const; + bool is_valgrind() const; + + void shutdown(bool remove= false); + void push_server(Server *); + Server *pop_server(); ~server_startup_st(); }; -#ifdef __cplusplus -extern "C" { -#endif +bool server_startup(server_startup_st&, const std::string&, in_port_t try_port, int argc, const char *argv[]); +} // namespace libtest -bool server_startup(server_startup_st *construct); -void server_shutdown(server_startup_st *construct); -#ifdef __cplusplus -} -#endif diff --git a/libtest/signal.cc b/libtest/signal.cc new file mode 100644 index 00000000..c5ec42ba --- /dev/null +++ b/libtest/signal.cc @@ -0,0 +1,186 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest, libtest + * + * 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 + +using namespace libtest; + +struct context_st { + sigset_t set; + sem_t lock; + + context_st() + { + sigemptyset(&set); + sigaddset(&set, SIGABRT); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGUSR2); + + sem_init(&lock, 0, 0); + } + + void test() + { + assert(sigismember(&set, SIGABRT)); + assert(sigismember(&set, SIGINT)); + assert(sigismember(&set, SIGUSR2)); + } + + int wait(int& sig) + { + return sigwait(&set, &sig); + } + + ~context_st() + { + sem_destroy(&lock); + } +}; + +static volatile shutdown_t __shutdown; +static pthread_mutex_t shutdown_mutex; +static pthread_t thread; + +bool is_shutdown() +{ + bool ret; + pthread_mutex_lock(&shutdown_mutex); + ret= bool(__shutdown != SHUTDOWN_RUNNING); + pthread_mutex_unlock(&shutdown_mutex); + + return ret; +} + +void set_shutdown(shutdown_t arg) +{ + pthread_mutex_lock(&shutdown_mutex); + __shutdown= arg; + pthread_mutex_unlock(&shutdown_mutex); + + if (arg == SHUTDOWN_GRACEFUL) + { + pthread_kill(thread, SIGUSR2); + + void *retval; + pthread_join(thread, &retval); + } +} + +shutdown_t get_shutdown() +{ + shutdown_t local; + pthread_mutex_lock(&shutdown_mutex); + local= __shutdown; + pthread_mutex_unlock(&shutdown_mutex); + + return local; +} + +extern "C" { + +static void *sig_thread(void *arg) +{ + context_st *context= (context_st*)arg; + assert(context); + + context->test(); + sem_post(&context->lock); + + while (get_shutdown() == SHUTDOWN_RUNNING) + { + int sig; + + if (context->wait(sig) == -1) + { + Error << "sigwait() returned errno:" << strerror(errno); + continue; + } + + switch (sig) + { + case SIGABRT: + case SIGINT: + Error << "Signal handling thread got signal " << strsignal(sig); + set_shutdown(SHUTDOWN_FORCED); + break; + + // Signal thread is being told that a graceful shutdown is occuring + case SIGUSR2: + break; + + default: + Error << "Signal handling thread got unexpected signal " << strsignal(sig); + break; + } + } + + delete context; + + return NULL; +} + +} + +void setup_signals() +{ + pthread_mutex_init(&shutdown_mutex, NULL); + set_shutdown(SHUTDOWN_RUNNING); + + context_st *context= new context_st; + + assert(context); + + int error; + if ((error= pthread_sigmask(SIG_BLOCK, &context->set, NULL)) != 0) + { + Error << "pthread_sigmask() died during pthread_sigmask(" << strerror(error) << ")"; + exit(EXIT_FAILURE); + } + + if ((error= pthread_create(&thread, NULL, &sig_thread, (void *) &context->set)) != 0) + { + Error << "pthread_create() died during pthread_create(" << strerror(error) << ")"; + exit(EXIT_FAILURE); + } + + sem_wait(&context->lock); +} diff --git a/libtest/signal.h b/libtest/signal.h new file mode 100644 index 00000000..beb28b22 --- /dev/null +++ b/libtest/signal.h @@ -0,0 +1,56 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * uTest, libtest + * + * 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 + +enum shutdown_t { + SHUTDOWN_RUNNING, + SHUTDOWN_GRACEFUL, + SHUTDOWN_FORCED +}; + +LIBTEST_INTERNAL_API +bool is_shutdown(); + +LIBTEST_INTERNAL_API +shutdown_t get_shutdown(); + +LIBTEST_INTERNAL_API +void set_shutdown(shutdown_t arg); + +LIBTEST_INTERNAL_API +void setup_signals(void); diff --git a/libtest/stream.h b/libtest/stream.h index 558b2790..0b503a04 100644 --- a/libtest/stream.h +++ b/libtest/stream.h @@ -51,6 +51,20 @@ template class cerr { private: + public: + typedef std::basic_ostringstream stream_buffer; + + public: + void operator()(const stream_buffer &s) + { + std::cerr << s.str() << std::endl; + } + }; + +template + class make_cerr { + private: + public: typedef std::basic_ostringstream stream_buffer; @@ -85,7 +99,7 @@ template public: void operator()(const stream_buffer &s) { - std::cerr << s.str() << std::endl; + std::cerr<< s.str() << std::endl; } }; @@ -95,6 +109,7 @@ template