From: Brian Aker Date: Sat, 5 Mar 2011 20:39:12 +0000 (-0800) Subject: Move testing framework out to its own library. X-Git-Tag: 0.51~26 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=349ca737f30ff0b6c3c71034f0930660663fa360;p=m6w6%2Flibmemcached Move testing framework out to its own library. --- diff --git a/.bzrignore b/.bzrignore index 0ccaac97..87578c4b 100644 --- a/.bzrignore +++ b/.bzrignore @@ -186,3 +186,4 @@ tests/testplus tests/testudp unittests/unittests config/top.h +libmemcached/generated_probes.h diff --git a/COPYING b/COPYING index c18e2ae8..efbc4a88 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,7 @@ Software License Agreement (BSD License) -Copyright (c) 2007, TangentOrg (Brian Aker) +Copyright (c) 2011, Data Differential (http://datadifferential.com/) +Copyright (c) 2007-2010, TangentOrg (Brian Aker) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Makefile.am b/Makefile.am index 7b866d0c..31585120 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ test-docs: (cd docs && $(MAKE) test-docs) include libmemcached/include.am include clients/include.am +include libtest/include.am include libhashkit/include.am include unittests/include.am include tests/include.am diff --git a/libtest/include.am b/libtest/include.am new file mode 100644 index 00000000..d6d9b18a --- /dev/null +++ b/libtest/include.am @@ -0,0 +1,20 @@ +# vim:ft=automake +# Copyright (C) 2011 Data Differential (http://datadifferential.com/) +# All rights reserved. +# +# Use and distribution licensed under the BSD license. See +# the COPYING file in the parent directory for full text. +# +# included from Top Level Makefile.am +# All paths should be given relative to the root + +noinst_HEADERS+= \ + libtest/server.h \ + libtest/test.h + +noinst_LTLIBRARIES+= libtest/libserver.la +libtest_libserver_la_SOURCES= libtest/server.c + +noinst_LTLIBRARIES+= libtest/libtest.la +libtest_libtest_la_SOURCES= libtest/test.c + diff --git a/libtest/server.c b/libtest/server.c new file mode 100644 index 00000000..4349a668 --- /dev/null +++ b/libtest/server.c @@ -0,0 +1,246 @@ +/* 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: + * + */ + +/* + Startup, and shutdown the memcached servers. +*/ + +#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10 + +#define PID_FILE_BASE "/tmp/%ulibmemcached_memc.pid" + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static struct timespec global_sleep_value= { .tv_sec= 0, .tv_nsec= 50000 }; + +static void global_sleep(void) +{ +#ifdef WIN32 + sleep(1); +#else + nanosleep(&global_sleep_value, NULL); +#endif +} + +static void kill_file(const char *file_buffer) +{ + FILE *fp= fopen(file_buffer, "r"); + + while ((fp= fopen(file_buffer, "r"))) + { + char pid_buffer[1024]; + + if (fgets(pid_buffer, sizeof(pid_buffer), fp) != NULL) + { + pid_t pid= (pid_t)atoi(pid_buffer); + if (pid != 0) + { + 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(); + } + } + } + } + + global_sleep(); + + fclose(fp); + } +} + +void server_startup(server_startup_st *construct) +{ + if ((construct->server_list= getenv("MEMCACHED_SERVERS"))) + { + printf("servers %s\n", construct->server_list); + construct->servers= memcached_servers_parse(construct->server_list); + construct->server_list= NULL; + construct->count= 0; + } + else + { + { + char server_string_buffer[8096]; + char *end_ptr; + end_ptr= server_string_buffer; + + for (uint32_t x= 0; x < construct->count; x++) + { + int status; + in_port_t port; + + { + char *var; + char variable_buffer[1024]; + + snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x); + + if ((var= getenv(variable_buffer))) + { + port= (in_port_t)atoi(var); + } + else + { + port= (in_port_t)(x + TEST_PORT_BASE); + } + } + + char buffer[PATH_MAX]; + snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x); + kill_file(buffer); + + if (x == 0) + { + snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u -m 128", + MEMCACHED_BINARY, x, port, port); + } + else + { + snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u", + MEMCACHED_BINARY, x, port, port); + } + if (libmemcached_util_ping("localhost", port, NULL)) + { + fprintf(stderr, "Server on port %u already exists\n", port); + } + else + { + status= system(buffer); + fprintf(stderr, "STARTING SERVER: %s status:%d\n", buffer, status); + } + int count; + size_t remaining_length= sizeof(server_string_buffer) - (size_t)(end_ptr -server_string_buffer); + count= snprintf(end_ptr, remaining_length, "localhost:%u,", port); + + if ((size_t)count >= remaining_length || count < 0) + { + fprintf(stderr, "server names grew to be larger then buffer allowed\n"); + abort(); + } + end_ptr+= count; + } + *end_ptr= 0; + + + int *pids= calloc(construct->count, sizeof(int)); + for (uint32_t x= 0; x < construct->count; x++) + { + char buffer[PATH_MAX]; /* Nothing special for number */ + + snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x); + + uint32_t counter= 3000; // Absurd, just to catch run away process + while (pids[x] <= 0 && --counter) + { + FILE *file= fopen(buffer, "r"); + if (file) + { + char pid_buffer[1024]; + char *found= fgets(pid_buffer, sizeof(pid_buffer), file); + + if (found) + { + pids[x]= atoi(pid_buffer); + fclose(file); + + if (pids[x] > 0) + break; + } + fclose(file); + } + global_sleep(); + } + + bool was_started= false; + if (pids[x] > 0) + { + counter= 30; + while (--counter) + { + if (kill(pids[x], 0) == 0) + { + was_started= true; + break; + } + global_sleep(); + } + } + + if (was_started == false) + { + fprintf(stderr, "Failed to open buffer %s(%d)\n", buffer, pids[x]); + for (uint32_t y= 0; y < construct->count; y++) + { + if (pids[y] > 0) + kill(pids[y], SIGTERM); + } + abort(); + } + } + free(pids); + + construct->server_list= strdup(server_string_buffer); + } + printf("servers %s\n", construct->server_list); + construct->servers= memcached_servers_parse(construct->server_list); + } + + assert(construct->servers); + + srandom((unsigned int)time(NULL)); + + for (uint32_t x= 0; x < memcached_server_list_count(construct->servers); x++) + { + printf("\t%s : %d\n", memcached_server_name(&construct->servers[x]), memcached_server_port(&construct->servers[x])); + assert(construct->servers[x].fd == -1); + assert(construct->servers[x].cursor_active == 0); + } + + printf("\n"); +} + +void server_shutdown(server_startup_st *construct) +{ + if (construct->server_list) + { + for (uint32_t x= 0; x < construct->count; x++) + { + char file_buffer[PATH_MAX]; /* Nothing special for number */ + snprintf(file_buffer, sizeof(file_buffer), PID_FILE_BASE, x); + kill_file(file_buffer); + } + + free(construct->server_list); + } +} diff --git a/libtest/server.h b/libtest/server.h new file mode 100644 index 00000000..0aef86f9 --- /dev/null +++ b/libtest/server.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + Server startup and shutdown functions. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct server_startup_st server_startup_st; + +struct server_startup_st +{ + uint8_t count; + uint8_t udp; + memcached_server_st *servers; + char *server_list; +}; + +void server_startup(server_startup_st *construct); +void server_shutdown(server_startup_st *construct); + +#ifdef __cplusplus +} +#endif diff --git a/libtest/test.c b/libtest/test.c new file mode 100644 index 00000000..009c4be5 --- /dev/null +++ b/libtest/test.c @@ -0,0 +1,371 @@ +/* uTest + * 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. + */ + +/* + Sample test application. +*/ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static void world_stats_print(world_stats_st *stats) +{ + fputc('\n', stderr); + fprintf(stderr, "Total Collections\t\t\t\t%u\n", stats->collection_total); + fprintf(stderr, "\tFailed Collections\t\t\t%u\n", stats->collection_failed); + fprintf(stderr, "\tSkipped Collections\t\t\t%u\n", stats->collection_skipped); + fprintf(stderr, "\tSucceeded Collections\t\t%u\n", stats->collection_success); + fputc('\n', stderr); + fprintf(stderr, "Total\t\t\t\t%u\n", stats->total); + fprintf(stderr, "\tFailed\t\t\t%u\n", stats->failed); + fprintf(stderr, "\tSkipped\t\t\t%u\n", stats->skipped); + fprintf(stderr, "\tSucceeded\t\t%u\n", stats->success); +} + +long int timedif(struct timeval a, struct timeval b) +{ + long us, s; + + us = (int)(a.tv_usec - b.tv_usec); + us /= 1000; + s = (int)(a.tv_sec - b.tv_sec); + s *= 1000; + return s + us; +} + +const char *test_strerror(test_return_t code) +{ + switch (code) { + case TEST_SUCCESS: + return "ok"; + case TEST_FAILURE: + return "failed"; + case TEST_MEMORY_ALLOCATION_FAILURE: + return "memory allocation"; + case TEST_SKIPPED: + return "skipped"; + case TEST_MAXIMUM_RETURN: + default: + fprintf(stderr, "Unknown return value\n"); + abort(); + } +} + +void create_core(void) +{ + if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL) + { + pid_t pid= fork(); + + if (pid == 0) + { + abort(); + } + else + { + while (waitpid(pid, NULL, 0) != pid) + { + ; + } + } + } +} + + +static test_return_t _runner_default(test_callback_fn func, void *p) +{ + if (func) + { + return func(p); + } + else + { + return TEST_SUCCESS; + } +} + +static world_runner_st defualt_runners= { + _runner_default, + _runner_default, + _runner_default +}; + +static test_return_t _default_callback(void *p) +{ + (void)p; + + return TEST_SUCCESS; +} + +static inline void set_default_fn(test_callback_fn *fn) +{ + if (*fn == NULL) + { + *fn= _default_callback; + } +} + +static collection_st *init_world(world_st *world) +{ + if (! world->runner) + { + world->runner= &defualt_runners; + } + + set_default_fn(&world->collection.startup); + set_default_fn(&world->collection.shutdown); + + return world->collections; +} + + +int main(int argc, char *argv[]) +{ + test_return_t return_code; + unsigned int x; + char *collection_to_run= NULL; + char *wildcard= NULL; + world_st world; + collection_st *collection; + collection_st *next; + void *world_ptr; + + world_stats_st stats; + +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + if (sasl_client_init(NULL) != SASL_OK) + { + fprintf(stderr, "Failed to initialize sasl library!\n"); + return 1; + } +#endif + + memset(&stats, 0, sizeof(stats)); + memset(&world, 0, sizeof(world)); + get_world(&world); + + collection= init_world(&world); + + if (world.create) + { + test_return_t error; + world_ptr= world.create(&error); + if (error != TEST_SUCCESS) + exit(1); + } + else + { + world_ptr= NULL; + } + + if (argc > 1) + collection_to_run= argv[1]; + + if (argc == 3) + wildcard= argv[2]; + + for (next= collection; next->name; next++) + { + test_return_t collection_rc= TEST_SUCCESS; + test_st *run; + bool failed= false; + bool skipped= false; + + run= next->tests; + if (collection_to_run && fnmatch(collection_to_run, next->name, 0)) + continue; + + stats.collection_total++; + + collection_rc= world.collection.startup(world_ptr); + + if (collection_rc != TEST_SUCCESS) + goto skip_pre; + + if (next->pre) + { + collection_rc= world.runner->pre(next->pre, world_ptr); + } + +skip_pre: + switch (collection_rc) + { + case TEST_SUCCESS: + fprintf(stderr, "\n%s\n\n", next->name); + break; + case TEST_FAILURE: + fprintf(stderr, "\n%s [ failed ]\n\n", next->name); + stats.collection_failed++; + goto cleanup; + case TEST_SKIPPED: + fprintf(stderr, "\n%s [ skipping ]\n\n", next->name); + stats.collection_skipped++; + goto cleanup; + case TEST_MEMORY_ALLOCATION_FAILURE: + case TEST_MAXIMUM_RETURN: + default: + assert(0); + break; + } + + + for (x= 0; run->name; run++) + { + struct timeval start_time, end_time; + long int load_time= 0; + + if (wildcard && fnmatch(wildcard, run->name, 0)) + continue; + + fprintf(stderr, "Testing %s", run->name); + + if (world.test.startup) + { + world.test.startup(world_ptr); + } + + if (run->requires_flush && world.test.flush) + { + world.test.flush(world_ptr); + } + + if (world.test.pre_run) + { + world.test.pre_run(world_ptr); + } + + + // Runner code + { +#if 0 + if (next->pre && world.runner->pre) + { + return_code= world.runner->pre(next->pre, world_ptr); + + if (return_code != TEST_SUCCESS) + { + goto error; + } + } +#endif + + 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 0 + if (next->post && world.runner->post) + { + (void) world.runner->post(next->post, world_ptr); + } +#endif + } + + if (world.test.post_run) + { + world.test.post_run(world_ptr); + } + + stats.total++; + + fprintf(stderr, "\t\t\t\t\t"); + + switch (return_code) + { + case TEST_SUCCESS: + fprintf(stderr, "%ld.%03ld ", load_time / 1000, load_time % 1000); + stats.success++; + break; + case TEST_FAILURE: + stats.failed++; + failed= true; + break; + case TEST_SKIPPED: + stats.skipped++; + skipped= true; + break; + case TEST_MEMORY_ALLOCATION_FAILURE: + fprintf(stderr, "Exhausted memory, quitting\n"); + abort(); + case TEST_MAXIMUM_RETURN: + default: + assert(0); // Coding error. + break; + } + + fprintf(stderr, "[ %s ]\n", test_strerror(return_code)); + + if (world.test.on_error) + { + test_return_t rc; + rc= world.test.on_error(return_code, world_ptr); + + if (rc != TEST_SUCCESS) + break; + } + } + + if (next->post && world.runner->post) + { + (void) world.runner->post(next->post, world_ptr); + } + + if (! failed && ! skipped) + { + stats.collection_success++; + } +cleanup: + + world.collection.shutdown(world_ptr); + } + + if (stats.collection_failed || stats.collection_skipped) + { + fprintf(stderr, "Some test failures and/or skipped test occurred.\n\n"); + } + else + { + fprintf(stderr, "All tests completed successfully\n\n"); + } + + if (world.destroy) + { + test_return_t error; + error= world.destroy(world_ptr); + + if (error != TEST_SUCCESS) + { + fprintf(stderr, "Failure during shutdown.\n"); + stats.failed++; // We do this to make our exit code return 1 + } + } + + world_stats_print(&stats); + +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + sasl_done(); +#endif + + return stats.failed == 0 ? 0 : 1; +} diff --git a/libtest/test.h b/libtest/test.h new file mode 100644 index 00000000..cc0fd78a --- /dev/null +++ b/libtest/test.h @@ -0,0 +1,205 @@ +/* uTest + * 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. + */ + +/* + Structures for generic tests. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#if !defined(__cplusplus) +# include +#endif + +typedef struct world_st world_st; +typedef struct collection_st collection_st; +typedef struct test_st test_st; + +typedef enum { + TEST_SUCCESS= 0, /* Backwards compatibility */ + TEST_FAILURE, + TEST_MEMORY_ALLOCATION_FAILURE, + TEST_SKIPPED, + TEST_MAXIMUM_RETURN /* Always add new error code before */ +} test_return_t; + +typedef void *(*test_callback_create_fn)(test_return_t *error); +typedef test_return_t (*test_callback_fn)(void *); +typedef test_return_t (*test_callback_runner_fn)(test_callback_fn, void *); +typedef test_return_t (*test_callback_error_fn)(test_return_t, void *); + +/* Help function for use with gettimeofday() */ +long int timedif(struct timeval a, struct timeval b); + +/** + A structure describing the test case. +*/ +struct test_st { + const char *name; + bool requires_flush; + test_callback_fn test_fn; +}; + + +/** + A structure which describes a collection of test cases. +*/ +struct collection_st { + const char *name; + test_callback_fn pre; + test_callback_fn post; + test_st *tests; +}; + + +/** + Structure which houses the actual callers for the test cases contained in + the collections. +*/ +typedef struct { + test_callback_runner_fn pre; + test_callback_runner_fn run; + test_callback_runner_fn post; +} world_runner_st; + + +/** + world_st is the structure which is passed to the test implementation to be filled. + This must be implemented in order for the test framework to load the tests. We call + get_world() in order to fill this structure. +*/ + +struct world_st { + collection_st *collections; + + /* These methods are called outside of any collection call. */ + test_callback_create_fn create; + test_callback_fn destroy; + + struct { + /* This is called a the beginning of any test run. */ + test_callback_fn startup; + + /* This called on a test if the test requires a flush call (the bool is from test_st) */ + test_callback_fn flush; + + /** + These are run before/after the test. If implemented. Their execution is not controlled + by the test. + */ + test_callback_fn pre_run; + test_callback_fn post_run; + + /** + If an error occurs during the test, this is called. + */ + test_callback_error_fn on_error; + } test; + + struct { + /* This is called a the beginning of any collection run. */ + test_callback_fn startup; + + /* This is called at the end of any collection run. */ + test_callback_fn shutdown; + } collection; + + + /** + Runner represents the callers for the tests. If not implemented we will use + a set of default implementations. + */ + world_runner_st *runner; +}; + + + +/** + @note world_stats_st is a simple structure for tracking test successes. +*/ +typedef struct { + uint32_t collection_success; + uint32_t collection_skipped; + uint32_t collection_failed; + uint32_t collection_total; + uint32_t success; + uint32_t skipped; + uint32_t failed; + uint32_t total; +} world_stats_st; + +/* How we make all of this work :) */ +void get_world(world_st *world); + +void create_core(void); + +/** + @note Friendly print function for errors. +*/ +const char *test_strerror(test_return_t code); + +#define test_fail(A) \ +do \ +{ \ + if (1) { \ + fprintf(stderr, "\nFailed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + +#define test_true(A) \ +do \ +{ \ + if (! (A)) { \ + fprintf(stderr, "\nAssertion failed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + +#define test_true_got(A,B) \ +do \ +{ \ + if (! (A)) { \ + fprintf(stderr, "\nAssertion failed at %s:%d: \"%s\" received \"%s\"\n", __FILE__, __LINE__, #A, (B));\ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + +#define test_false(A) \ +do \ +{ \ + if ((A)) { \ + fprintf(stderr, "\nAssertion failed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + +#define test_strcmp(A,B) \ +do \ +{ \ + if (strcmp((A), (B))) \ + { \ + fprintf(stderr, "\n%s:%d: %s -> %s\n", __FILE__, __LINE__, (A), (B)); \ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + +#ifdef __cplusplus +} +#endif diff --git a/tests/atomsmasher.c b/tests/atomsmasher.c index fac6986c..5b4bcbe2 100644 --- a/tests/atomsmasher.c +++ b/tests/atomsmasher.c @@ -26,11 +26,11 @@ #include #include #include -#include "server.h" #include "../clients/generator.h" #include "../clients/execute.h" -#include "test.h" +#include +#include /* Number of items generated for tests */ #define GLOBAL_COUNT 100000 diff --git a/tests/hash_plus.cc b/tests/hash_plus.cc index 1d0b9e83..c148d3ef 100644 --- a/tests/hash_plus.cc +++ b/tests/hash_plus.cc @@ -1,7 +1,8 @@ /* C++ to libhashkit */ -#include "test.h" +#include + #include #include #include diff --git a/tests/hashkit_functions.c b/tests/hashkit_functions.c index deb5436a..0f2c42a9 100644 --- a/tests/hashkit_functions.c +++ b/tests/hashkit_functions.c @@ -15,7 +15,7 @@ #include -#include "test.h" +#include #include "hash_results.h" diff --git a/tests/include.am b/tests/include.am index b90a5ef7..f940caf8 100644 --- a/tests/include.am +++ b/tests/include.am @@ -1,8 +1,17 @@ # vim:ft=automake +# Copyright (C) 2011 Data Differential +# All rights reserved. +# +# Use and distribution licensed under the BSD license. See +# the COPYING file in the parent directory for full text. +# # included from Top Level Makefile.am # All paths should be given relative to the root -TESTS_LDADDS = libmemcached/libmemcached.la +TESTS_LDADDS= \ + libmemcached/libmemcached.la \ + libtest/libserver.la \ + libtest/libtest.la VALGRIND_COMMAND= $(LIBTOOL) --mode=execute valgrind --leak-check=yes --show-reachable=yes --track-fds=yes @@ -22,9 +31,7 @@ noinst_HEADERS+= \ tests/hash_results.h \ tests/ketama_test_cases.h \ tests/ketama_test_cases_spy.h \ - tests/libmemcached_world.h \ - tests/server.h \ - tests/test.h + tests/libmemcached_world.h noinst_PROGRAMS+= \ tests/atomsmasher \ @@ -36,55 +43,44 @@ noinst_PROGRAMS+= \ tests/testplus \ tests/testudp -noinst_LTLIBRARIES+= tests/libserver.la -tests_libserver_la_SOURCES= tests/server.c - -noinst_LTLIBRARIES+= tests/libtest.la -tests_libtest_la_SOURCES= tests/test.c - tests_testapp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING) tests_testapp_SOURCES= tests/mem_functions.c tests_testapp_DEPENDENCIES= \ $(BUILT_SOURCES) \ clients/libgenexec.la \ - tests/libserver.la \ - tests/libtest.la \ libmemcached/libmemcachedinternal.la \ $(TESTS_LDADDS) + tests_testapp_LDADD= clients/libgenexec.la \ - tests/libserver.la \ - tests/libtest.la \ libmemcached/libmemcachedinternal.la \ $(TESTS_LDADDS) $(LIBSASL) tests_testplus_SOURCES= tests/plus.cpp tests_testplus_CXXFLAGS = $(AM_CXXFLAGS) $(NO_EFF_CXX) -tests_testplus_DEPENDENCIES= tests/libtest.la tests/libserver.la $(TESTS_LDADDS) +tests_testplus_DEPENDENCIES= $(TESTS_LDADDS) tests_testplus_LDADD= $(tests_testplus_DEPENDENCIES) $(LIBSASL) tests_atomsmasher_SOURCES= tests/atomsmasher.c tests_atomsmasher_DEPENDENCIES= \ clients/libgenexec.la \ - tests/libserver.la \ - tests/libtest.la \ $(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.c tests_testudp_DEPENDENCIES= \ clients/libgenexec.la \ - tests/libserver.la \ - tests/libtest.la \ $(TESTS_LDADDS) + tests_testudp_LDADD= $(tests_testudp_DEPENDENCIES) $(LIBSASL) tests_startservers_SOURCES= tests/start.c -tests_startservers_DEPENDENCIES= tests/libserver.la $(TESTS_LDADDS) +tests_startservers_DEPENDENCIES= $(TESTS_LDADDS) tests_startservers_LDADD= $(tests_startservers_DEPENDENCIES) $(LIBSASL) tests_testhashkit_SOURCES = tests/hashkit_functions.c -tests_testhashkit_DEPENDENCIES = tests/libtest.la libhashkit/libhashkit.la +tests_testhashkit_DEPENDENCIES = libtest/libtest.la libhashkit/libhashkit.la tests_testhashkit_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL) tests_hashplus_SOURCES = tests/hash_plus.cc @@ -94,7 +90,7 @@ tests_hashplus_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL) tests_memplus_SOURCES = tests/mem_plus.cc tests_memplus_CXXFLAGS = $(AM_CXXFLAGS) $(NO_EFF_CXX) -tests_memplus_DEPENDENCIES = tests/libtest.la tests/libserver.la libmemcached/libmemcached.la +tests_memplus_DEPENDENCIES = $(TESTS_LDADDS) tests_memplus_LDADD = $(tests_memplus_DEPENDENCIES) $(LIBSASL) test: check diff --git a/tests/mem_functions.c b/tests/mem_functions.c index 871256cf..36c5717e 100644 --- a/tests/mem_functions.c +++ b/tests/mem_functions.c @@ -26,13 +26,14 @@ #include "libmemcached/common.h" -#include "server.h" +#include + #include "clients/generator.h" #include "clients/execute.h" #define SMALL_STRING_LEN 1024 -#include "test.h" +#include #ifdef HAVE_LIBMEMCACHEDUTIL diff --git a/tests/mem_plus.cc b/tests/mem_plus.cc index d62ae723..d9a76afb 100644 --- a/tests/mem_plus.cc +++ b/tests/mem_plus.cc @@ -1,7 +1,8 @@ /* C++ to libmemcit */ -#include "test.h" +#include + #include #include #include diff --git a/tests/mem_udp.c b/tests/mem_udp.c index 76aab981..01314dbd 100644 --- a/tests/mem_udp.c +++ b/tests/mem_udp.c @@ -26,9 +26,8 @@ #include #include -#include "server.h" - -#include "test.h" +#include +#include #define SERVERS_TO_CREATE 5 diff --git a/tests/plus.cpp b/tests/plus.cpp index 0d588749..c0c6bb95 100644 --- a/tests/plus.cpp +++ b/tests/plus.cpp @@ -12,9 +12,10 @@ #include #include #include -#include "server.h" -#include "test.h" +#include + +#include #include #include diff --git a/tests/server.c b/tests/server.c deleted file mode 100644 index 84f15de0..00000000 --- a/tests/server.c +++ /dev/null @@ -1,246 +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: - * - */ - -/* - Startup, and shutdown the memcached servers. -*/ - -#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10 - -#define PID_FILE_BASE "/tmp/%ulibmemcached_memc.pid" - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "server.h" - -static struct timespec global_sleep_value= { .tv_sec= 0, .tv_nsec= 50000 }; - -static void global_sleep(void) -{ -#ifdef WIN32 - sleep(1); -#else - nanosleep(&global_sleep_value, NULL); -#endif -} - -static void kill_file(const char *file_buffer) -{ - FILE *fp= fopen(file_buffer, "r"); - - while ((fp= fopen(file_buffer, "r"))) - { - char pid_buffer[1024]; - - if (fgets(pid_buffer, sizeof(pid_buffer), fp) != NULL) - { - pid_t pid= (pid_t)atoi(pid_buffer); - if (pid != 0) - { - 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(); - } - } - } - } - - global_sleep(); - - fclose(fp); - } -} - -void server_startup(server_startup_st *construct) -{ - if ((construct->server_list= getenv("MEMCACHED_SERVERS"))) - { - printf("servers %s\n", construct->server_list); - construct->servers= memcached_servers_parse(construct->server_list); - construct->server_list= NULL; - construct->count= 0; - } - else - { - { - char server_string_buffer[8096]; - char *end_ptr; - end_ptr= server_string_buffer; - - for (uint32_t x= 0; x < construct->count; x++) - { - int status; - in_port_t port; - - { - char *var; - char variable_buffer[1024]; - - snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x); - - if ((var= getenv(variable_buffer))) - { - port= (in_port_t)atoi(var); - } - else - { - port= (in_port_t)(x + TEST_PORT_BASE); - } - } - - char buffer[PATH_MAX]; - snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x); - kill_file(buffer); - - if (x == 0) - { - snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u -m 128", - MEMCACHED_BINARY, x, port, port); - } - else - { - snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u", - MEMCACHED_BINARY, x, port, port); - } - if (libmemcached_util_ping("localhost", port, NULL)) - { - fprintf(stderr, "Server on port %u already exists\n", port); - } - else - { - status= system(buffer); - fprintf(stderr, "STARTING SERVER: %s status:%d\n", buffer, status); - } - int count; - size_t remaining_length= sizeof(server_string_buffer) - (size_t)(end_ptr -server_string_buffer); - count= snprintf(end_ptr, remaining_length, "localhost:%u,", port); - - if ((size_t)count >= remaining_length || count < 0) - { - fprintf(stderr, "server names grew to be larger then buffer allowed\n"); - abort(); - } - end_ptr+= count; - } - *end_ptr= 0; - - - int *pids= calloc(construct->count, sizeof(int)); - for (uint32_t x= 0; x < construct->count; x++) - { - char buffer[PATH_MAX]; /* Nothing special for number */ - - snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x); - - uint32_t counter= 3000; // Absurd, just to catch run away process - while (pids[x] <= 0 && --counter) - { - FILE *file= fopen(buffer, "r"); - if (file) - { - char pid_buffer[1024]; - char *found= fgets(pid_buffer, sizeof(pid_buffer), file); - - if (found) - { - pids[x]= atoi(pid_buffer); - fclose(file); - - if (pids[x] > 0) - break; - } - fclose(file); - } - global_sleep(); - } - - bool was_started= false; - if (pids[x] > 0) - { - counter= 30; - while (--counter) - { - if (kill(pids[x], 0) == 0) - { - was_started= true; - break; - } - global_sleep(); - } - } - - if (was_started == false) - { - fprintf(stderr, "Failed to open buffer %s(%d)\n", buffer, pids[x]); - for (uint32_t y= 0; y < construct->count; y++) - { - if (pids[y] > 0) - kill(pids[y], SIGTERM); - } - abort(); - } - } - free(pids); - - construct->server_list= strdup(server_string_buffer); - } - printf("servers %s\n", construct->server_list); - construct->servers= memcached_servers_parse(construct->server_list); - } - - assert(construct->servers); - - srandom((unsigned int)time(NULL)); - - for (uint32_t x= 0; x < memcached_server_list_count(construct->servers); x++) - { - printf("\t%s : %d\n", memcached_server_name(&construct->servers[x]), memcached_server_port(&construct->servers[x])); - assert(construct->servers[x].fd == -1); - assert(construct->servers[x].cursor_active == 0); - } - - printf("\n"); -} - -void server_shutdown(server_startup_st *construct) -{ - if (construct->server_list) - { - for (uint32_t x= 0; x < construct->count; x++) - { - char file_buffer[PATH_MAX]; /* Nothing special for number */ - snprintf(file_buffer, sizeof(file_buffer), PID_FILE_BASE, x); - kill_file(file_buffer); - } - - free(construct->server_list); - } -} diff --git a/tests/server.h b/tests/server.h deleted file mode 100644 index 0aef86f9..00000000 --- a/tests/server.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -/* - Server startup and shutdown functions. -*/ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct server_startup_st server_startup_st; - -struct server_startup_st -{ - uint8_t count; - uint8_t udp; - memcached_server_st *servers; - char *server_list; -}; - -void server_startup(server_startup_st *construct); -void server_shutdown(server_startup_st *construct); - -#ifdef __cplusplus -} -#endif diff --git a/tests/start.c b/tests/start.c index e3f2a64c..ae9497f3 100644 --- a/tests/start.c +++ b/tests/start.c @@ -13,7 +13,7 @@ #include #include -#include "server.h" +#include int main(void) { diff --git a/tests/test.c b/tests/test.c deleted file mode 100644 index 348df72b..00000000 --- a/tests/test.c +++ /dev/null @@ -1,371 +0,0 @@ -/* uTest - * 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. - */ - -/* - Sample test application. -*/ - -#include "config.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libmemcached/memcached.h" - -#include "test.h" - -static void world_stats_print(world_stats_st *stats) -{ - fputc('\n', stderr); - fprintf(stderr, "Total Collections\t\t\t\t%u\n", stats->collection_total); - fprintf(stderr, "\tFailed Collections\t\t\t%u\n", stats->collection_failed); - fprintf(stderr, "\tSkipped Collections\t\t\t%u\n", stats->collection_skipped); - fprintf(stderr, "\tSucceeded Collections\t\t%u\n", stats->collection_success); - fputc('\n', stderr); - fprintf(stderr, "Total\t\t\t\t%u\n", stats->total); - fprintf(stderr, "\tFailed\t\t\t%u\n", stats->failed); - fprintf(stderr, "\tSkipped\t\t\t%u\n", stats->skipped); - fprintf(stderr, "\tSucceeded\t\t%u\n", stats->success); -} - -long int timedif(struct timeval a, struct timeval b) -{ - long us, s; - - us = (int)(a.tv_usec - b.tv_usec); - us /= 1000; - s = (int)(a.tv_sec - b.tv_sec); - s *= 1000; - return s + us; -} - -const char *test_strerror(test_return_t code) -{ - switch (code) { - case TEST_SUCCESS: - return "ok"; - case TEST_FAILURE: - return "failed"; - case TEST_MEMORY_ALLOCATION_FAILURE: - return "memory allocation"; - case TEST_SKIPPED: - return "skipped"; - case TEST_MAXIMUM_RETURN: - default: - fprintf(stderr, "Unknown return value\n"); - abort(); - } -} - -void create_core(void) -{ - if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL) - { - pid_t pid= fork(); - - if (pid == 0) - { - abort(); - } - else - { - while (waitpid(pid, NULL, 0) != pid) - { - ; - } - } - } -} - - -static test_return_t _runner_default(test_callback_fn func, void *p) -{ - if (func) - { - return func(p); - } - else - { - return TEST_SUCCESS; - } -} - -static world_runner_st defualt_runners= { - _runner_default, - _runner_default, - _runner_default -}; - -static test_return_t _default_callback(void *p) -{ - (void)p; - - return TEST_SUCCESS; -} - -static inline void set_default_fn(test_callback_fn *fn) -{ - if (*fn == NULL) - { - *fn= _default_callback; - } -} - -static collection_st *init_world(world_st *world) -{ - if (! world->runner) - { - world->runner= &defualt_runners; - } - - set_default_fn(&world->collection.startup); - set_default_fn(&world->collection.shutdown); - - return world->collections; -} - - -int main(int argc, char *argv[]) -{ - test_return_t return_code; - unsigned int x; - char *collection_to_run= NULL; - char *wildcard= NULL; - world_st world; - collection_st *collection; - collection_st *next; - void *world_ptr; - - world_stats_st stats; - -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - if (sasl_client_init(NULL) != SASL_OK) - { - fprintf(stderr, "Failed to initialize sasl library!\n"); - return 1; - } -#endif - - memset(&stats, 0, sizeof(stats)); - memset(&world, 0, sizeof(world)); - get_world(&world); - - collection= init_world(&world); - - if (world.create) - { - test_return_t error; - world_ptr= world.create(&error); - if (error != TEST_SUCCESS) - exit(1); - } - else - { - world_ptr= NULL; - } - - if (argc > 1) - collection_to_run= argv[1]; - - if (argc == 3) - wildcard= argv[2]; - - for (next= collection; next->name; next++) - { - test_return_t collection_rc= TEST_SUCCESS; - test_st *run; - bool failed= false; - bool skipped= false; - - run= next->tests; - if (collection_to_run && fnmatch(collection_to_run, next->name, 0)) - continue; - - stats.collection_total++; - - collection_rc= world.collection.startup(world_ptr); - - if (collection_rc != TEST_SUCCESS) - goto skip_pre; - - if (next->pre) - { - collection_rc= world.runner->pre(next->pre, world_ptr); - } - -skip_pre: - switch (collection_rc) - { - case TEST_SUCCESS: - fprintf(stderr, "\n%s\n\n", next->name); - break; - case TEST_FAILURE: - fprintf(stderr, "\n%s [ failed ]\n\n", next->name); - stats.collection_failed++; - goto cleanup; - case TEST_SKIPPED: - fprintf(stderr, "\n%s [ skipping ]\n\n", next->name); - stats.collection_skipped++; - goto cleanup; - case TEST_MEMORY_ALLOCATION_FAILURE: - case TEST_MAXIMUM_RETURN: - default: - assert(0); - break; - } - - - for (x= 0; run->name; run++) - { - struct timeval start_time, end_time; - long int load_time= 0; - - if (wildcard && fnmatch(wildcard, run->name, 0)) - continue; - - fprintf(stderr, "Testing %s", run->name); - - if (world.test.startup) - { - world.test.startup(world_ptr); - } - - if (run->requires_flush && world.test.flush) - { - world.test.flush(world_ptr); - } - - if (world.test.pre_run) - { - world.test.pre_run(world_ptr); - } - - - // Runner code - { -#if 0 - if (next->pre && world.runner->pre) - { - return_code= world.runner->pre(next->pre, world_ptr); - - if (return_code != TEST_SUCCESS) - { - goto error; - } - } -#endif - - 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 0 - if (next->post && world.runner->post) - { - (void) world.runner->post(next->post, world_ptr); - } -#endif - } - - if (world.test.post_run) - { - world.test.post_run(world_ptr); - } - - stats.total++; - - fprintf(stderr, "\t\t\t\t\t"); - - switch (return_code) - { - case TEST_SUCCESS: - fprintf(stderr, "%ld.%03ld ", load_time / 1000, load_time % 1000); - stats.success++; - break; - case TEST_FAILURE: - stats.failed++; - failed= true; - break; - case TEST_SKIPPED: - stats.skipped++; - skipped= true; - break; - case TEST_MEMORY_ALLOCATION_FAILURE: - fprintf(stderr, "Exhausted memory, quitting\n"); - abort(); - case TEST_MAXIMUM_RETURN: - default: - assert(0); // Coding error. - break; - } - - fprintf(stderr, "[ %s ]\n", test_strerror(return_code)); - - if (world.test.on_error) - { - test_return_t rc; - rc= world.test.on_error(return_code, world_ptr); - - if (rc != TEST_SUCCESS) - break; - } - } - - if (next->post && world.runner->post) - { - (void) world.runner->post(next->post, world_ptr); - } - - if (! failed && ! skipped) - { - stats.collection_success++; - } -cleanup: - - world.collection.shutdown(world_ptr); - } - - if (stats.collection_failed || stats.collection_skipped) - { - fprintf(stderr, "Some test failures and/or skipped test occurred.\n\n"); - } - else - { - fprintf(stderr, "All tests completed successfully\n\n"); - } - - if (world.destroy) - { - test_return_t error; - error= world.destroy(world_ptr); - - if (error != TEST_SUCCESS) - { - fprintf(stderr, "Failure during shutdown.\n"); - stats.failed++; // We do this to make our exit code return 1 - } - } - - world_stats_print(&stats); - -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - sasl_done(); -#endif - - return stats.failed == 0 ? 0 : 1; -} diff --git a/tests/test.h b/tests/test.h deleted file mode 100644 index cc0fd78a..00000000 --- a/tests/test.h +++ /dev/null @@ -1,205 +0,0 @@ -/* uTest - * 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. - */ - -/* - Structures for generic tests. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#if !defined(__cplusplus) -# include -#endif - -typedef struct world_st world_st; -typedef struct collection_st collection_st; -typedef struct test_st test_st; - -typedef enum { - TEST_SUCCESS= 0, /* Backwards compatibility */ - TEST_FAILURE, - TEST_MEMORY_ALLOCATION_FAILURE, - TEST_SKIPPED, - TEST_MAXIMUM_RETURN /* Always add new error code before */ -} test_return_t; - -typedef void *(*test_callback_create_fn)(test_return_t *error); -typedef test_return_t (*test_callback_fn)(void *); -typedef test_return_t (*test_callback_runner_fn)(test_callback_fn, void *); -typedef test_return_t (*test_callback_error_fn)(test_return_t, void *); - -/* Help function for use with gettimeofday() */ -long int timedif(struct timeval a, struct timeval b); - -/** - A structure describing the test case. -*/ -struct test_st { - const char *name; - bool requires_flush; - test_callback_fn test_fn; -}; - - -/** - A structure which describes a collection of test cases. -*/ -struct collection_st { - const char *name; - test_callback_fn pre; - test_callback_fn post; - test_st *tests; -}; - - -/** - Structure which houses the actual callers for the test cases contained in - the collections. -*/ -typedef struct { - test_callback_runner_fn pre; - test_callback_runner_fn run; - test_callback_runner_fn post; -} world_runner_st; - - -/** - world_st is the structure which is passed to the test implementation to be filled. - This must be implemented in order for the test framework to load the tests. We call - get_world() in order to fill this structure. -*/ - -struct world_st { - collection_st *collections; - - /* These methods are called outside of any collection call. */ - test_callback_create_fn create; - test_callback_fn destroy; - - struct { - /* This is called a the beginning of any test run. */ - test_callback_fn startup; - - /* This called on a test if the test requires a flush call (the bool is from test_st) */ - test_callback_fn flush; - - /** - These are run before/after the test. If implemented. Their execution is not controlled - by the test. - */ - test_callback_fn pre_run; - test_callback_fn post_run; - - /** - If an error occurs during the test, this is called. - */ - test_callback_error_fn on_error; - } test; - - struct { - /* This is called a the beginning of any collection run. */ - test_callback_fn startup; - - /* This is called at the end of any collection run. */ - test_callback_fn shutdown; - } collection; - - - /** - Runner represents the callers for the tests. If not implemented we will use - a set of default implementations. - */ - world_runner_st *runner; -}; - - - -/** - @note world_stats_st is a simple structure for tracking test successes. -*/ -typedef struct { - uint32_t collection_success; - uint32_t collection_skipped; - uint32_t collection_failed; - uint32_t collection_total; - uint32_t success; - uint32_t skipped; - uint32_t failed; - uint32_t total; -} world_stats_st; - -/* How we make all of this work :) */ -void get_world(world_st *world); - -void create_core(void); - -/** - @note Friendly print function for errors. -*/ -const char *test_strerror(test_return_t code); - -#define test_fail(A) \ -do \ -{ \ - if (1) { \ - fprintf(stderr, "\nFailed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ - create_core(); \ - return TEST_FAILURE; \ - } \ -} while (0) - -#define test_true(A) \ -do \ -{ \ - if (! (A)) { \ - fprintf(stderr, "\nAssertion failed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ - create_core(); \ - return TEST_FAILURE; \ - } \ -} while (0) - -#define test_true_got(A,B) \ -do \ -{ \ - if (! (A)) { \ - fprintf(stderr, "\nAssertion failed at %s:%d: \"%s\" received \"%s\"\n", __FILE__, __LINE__, #A, (B));\ - create_core(); \ - return TEST_FAILURE; \ - } \ -} while (0) - -#define test_false(A) \ -do \ -{ \ - if ((A)) { \ - fprintf(stderr, "\nAssertion failed at %s:%d: %s\n", __FILE__, __LINE__, #A);\ - create_core(); \ - return TEST_FAILURE; \ - } \ -} while (0) - -#define test_strcmp(A,B) \ -do \ -{ \ - if (strcmp((A), (B))) \ - { \ - fprintf(stderr, "\n%s:%d: %s -> %s\n", __FILE__, __LINE__, (A), (B)); \ - create_core(); \ - return TEST_FAILURE; \ - } \ -} while (0) - -#ifdef __cplusplus -} -#endif diff --git a/unittests/include.am b/unittests/include.am index c1443418..b7ca5d26 100644 --- a/unittests/include.am +++ b/unittests/include.am @@ -12,7 +12,7 @@ unittests_unittests_SOURCES= \ unittests_unittests_CXXFLAGS= ${AM_CXXFLAGS} ${NO_WERROR} ${NO_EFF_CXX} unittests_unittests_LDADD= \ - tests/libserver.la \ + libtest/libserver.la \ libmemcached/libmemcachedinternal.la \ ${TESTS_LDADDS} ${LTLIBGTEST} # Shorthand