polardbxengine/plugin/innodb_memcached/daemon_memcached/programs/engine_testapp.c

896 lines
29 KiB
C

/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "config.h"
#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include "utilities/engine_loader.h"
#include <memcached/engine_testapp.h>
#include <memcached/extension_loggers.h>
#include <mock_server.h>
struct mock_engine {
ENGINE_HANDLE_V1 me;
ENGINE_HANDLE_V1 *the_engine;
TAP_ITERATOR iterator;
};
#ifndef WIN32
static sig_atomic_t alarmed;
static void alarm_handler(int sig) {
alarmed = 1;
}
#endif
static inline struct mock_engine* get_handle(ENGINE_HANDLE* handle) {
return (struct mock_engine*)handle;
}
static tap_event_t mock_tap_iterator(ENGINE_HANDLE* handle,
const void *cookie, item **itm,
void **es, uint16_t *nes, uint8_t *ttl,
uint16_t *flags, uint32_t *seqno,
uint16_t *vbucket) {
struct mock_engine *me = get_handle(handle);
return me->iterator((ENGINE_HANDLE*)me->the_engine, cookie, itm, es, nes,
ttl, flags, seqno, vbucket);
}
static const engine_info* mock_get_info(ENGINE_HANDLE* handle) {
struct mock_engine *me = get_handle(handle);
return me->the_engine->get_info((ENGINE_HANDLE*)me->the_engine);
}
static ENGINE_ERROR_CODE mock_initialize(ENGINE_HANDLE* handle,
const char* config_str) {
struct mock_engine *me = get_handle(handle);
return me->the_engine->initialize((ENGINE_HANDLE*)me->the_engine, config_str);
}
static void mock_destroy(ENGINE_HANDLE* handle, const bool force) {
struct mock_engine *me = get_handle(handle);
me->the_engine->destroy((ENGINE_HANDLE*)me->the_engine, force);
}
static ENGINE_ERROR_CODE mock_allocate(ENGINE_HANDLE* handle,
const void* cookie,
item **item,
const void* key,
const size_t nkey,
const size_t nbytes,
const int flags,
const rel_time_t exptime) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->allocate((ENGINE_HANDLE*)me->the_engine, c,
item, key, nkey,
nbytes, flags,
exptime)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_remove(ENGINE_HANDLE* handle,
const void* cookie,
const void* key,
const size_t nkey,
uint64_t cas,
uint16_t vbucket)
{
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->remove((ENGINE_HANDLE*)me->the_engine, c, key,
nkey, cas, vbucket)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static void mock_release(ENGINE_HANDLE* handle,
const void *cookie,
item* item) {
struct mock_engine *me = get_handle(handle);
me->the_engine->release((ENGINE_HANDLE*)me->the_engine, cookie, item);
}
static ENGINE_ERROR_CODE mock_get(ENGINE_HANDLE* handle,
const void* cookie,
item** item,
const void* key,
const int nkey,
uint16_t vbucket) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->get((ENGINE_HANDLE*)me->the_engine, c, item,
key, nkey, vbucket)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_get_stats(ENGINE_HANDLE* handle,
const void* cookie,
const char* stat_key,
int nkey,
ADD_STAT add_stat)
{
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->get_stats((ENGINE_HANDLE*)me->the_engine, c, stat_key,
nkey, add_stat)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_store(ENGINE_HANDLE* handle,
const void *cookie,
item* item,
uint64_t *cas,
ENGINE_STORE_OPERATION operation,
uint16_t vbucket) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->store((ENGINE_HANDLE*)me->the_engine, c, item, cas,
operation, vbucket)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_arithmetic(ENGINE_HANDLE* handle,
const void* cookie,
const void* key,
const int nkey,
const bool increment,
const bool create,
const uint64_t delta,
const uint64_t initial,
const rel_time_t exptime,
uint64_t *cas,
uint64_t *result,
uint16_t vbucket) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->arithmetic((ENGINE_HANDLE*)me->the_engine, c, key,
nkey, increment, create,
delta, initial, exptime,
cas, result, vbucket)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_flush(ENGINE_HANDLE* handle,
const void* cookie, time_t when) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->flush((ENGINE_HANDLE*)me->the_engine, c, when)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static void mock_reset_stats(ENGINE_HANDLE* handle, const void *cookie) {
struct mock_engine *me = get_handle(handle);
me->the_engine->reset_stats((ENGINE_HANDLE*)me->the_engine, cookie);
}
static ENGINE_ERROR_CODE mock_unknown_command(ENGINE_HANDLE* handle,
const void* cookie,
protocol_binary_request_header *request,
ADD_RESPONSE response)
{
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->unknown_command((ENGINE_HANDLE*)me->the_engine, c,
request, response)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static void mock_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,
item* item, uint64_t val)
{
struct mock_engine *me = get_handle(handle);
me->the_engine->item_set_cas((ENGINE_HANDLE*)me->the_engine, cookie, item, val);
}
static bool mock_get_item_info(ENGINE_HANDLE *handle, const void *cookie,
const item* item, item_info *item_info)
{
struct mock_engine *me = get_handle(handle);
return me->the_engine->get_item_info((ENGINE_HANDLE*)me->the_engine,
cookie, item, item_info);
}
static void *mock_get_stats_struct(ENGINE_HANDLE* handle, const void* cookie)
{
struct mock_engine *me = get_handle(handle);
return me->the_engine->get_stats_struct((ENGINE_HANDLE*)me->the_engine, cookie);
}
static ENGINE_ERROR_CODE mock_aggregate_stats(ENGINE_HANDLE* handle,
const void* cookie,
void (*callback)(void*, void*),
void *vptr)
{
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->aggregate_stats((ENGINE_HANDLE*)me->the_engine, c,
callback, vptr)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static ENGINE_ERROR_CODE mock_tap_notify(ENGINE_HANDLE* handle,
const void *cookie,
void *engine_specific,
uint16_t nengine,
uint8_t ttl,
uint16_t tap_flags,
tap_event_t tap_event,
uint32_t tap_seqno,
const void *key,
size_t nkey,
uint32_t flags,
uint32_t exptime,
uint64_t cas,
const void *data,
size_t ndata,
uint16_t vbucket) {
struct mock_engine *me = get_handle(handle);
struct mock_connstruct *c = (void*)cookie;
if (c == NULL) {
c = (void*)create_mock_cookie();
}
c->nblocks = 0;
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
pthread_mutex_lock(&c->mutex);
while (ret == ENGINE_SUCCESS &&
(ret = me->the_engine->tap_notify((ENGINE_HANDLE*)me->the_engine, c,
engine_specific, nengine, ttl, tap_flags,
tap_event, tap_seqno, key, nkey, flags,
exptime, cas, data, ndata, vbucket)) == ENGINE_EWOULDBLOCK &&
c->handle_ewouldblock)
{
++c->nblocks;
pthread_cond_wait(&c->cond, &c->mutex);
ret = c->status;
}
pthread_mutex_unlock(&c->mutex);
if (c != cookie) {
destroy_mock_cookie(c);
}
return ret;
}
static TAP_ITERATOR mock_get_tap_iterator(ENGINE_HANDLE* handle, const void* cookie,
const void* client, size_t nclient,
uint32_t flags,
const void* userdata, size_t nuserdata) {
struct mock_engine *me = get_handle(handle);
me->iterator = me->the_engine->get_tap_iterator((ENGINE_HANDLE*)me->the_engine, cookie,
client, nclient, flags, userdata, nuserdata);
return (me->iterator != NULL) ? mock_tap_iterator : NULL;
}
static size_t mock_errinfo(ENGINE_HANDLE *handle, const void* cookie,
char *buffer, size_t buffsz) {
struct mock_engine *me = get_handle(handle);
return me->the_engine->errinfo((ENGINE_HANDLE*)me->the_engine, cookie,
buffer, buffsz);
}
struct mock_engine default_mock_engine = {
.me = {
.interface = {
.interface = 1
},
.get_info = mock_get_info,
.initialize = mock_initialize,
.destroy = mock_destroy,
.allocate = mock_allocate,
.remove = mock_remove,
.release = mock_release,
.get = mock_get,
.store = mock_store,
.arithmetic = mock_arithmetic,
.flush = mock_flush,
.get_stats = mock_get_stats,
.reset_stats = mock_reset_stats,
.get_stats_struct = mock_get_stats_struct,
.aggregate_stats = mock_aggregate_stats,
.unknown_command = mock_unknown_command,
.tap_notify = mock_tap_notify,
.get_tap_iterator = mock_get_tap_iterator,
.item_set_cas = mock_item_set_cas,
.get_item_info = mock_get_item_info,
.errinfo = mock_errinfo
}
};
struct mock_engine mock_engine;
EXTENSION_LOGGER_DESCRIPTOR *logger_descriptor = NULL;
static ENGINE_HANDLE *handle = NULL;
static ENGINE_HANDLE_V1 *handle_v1 = NULL;
static void usage(void) {
printf("\n");
printf("engine_testapp -E <path_to_engine_lib> -T <path_to_testlib>\n");
printf(" [-e <engine_config>] [-h]\n");
printf("\n");
printf("-E <path_to_engine_lib> Path to the engine library file. The\n");
printf(" engine library file is a library file\n");
printf(" (.so or .dll) that the contains the \n");
printf(" implementation of the engine being\n");
printf(" tested.\n");
printf("\n");
printf("-T <path_to_testlib> Path to the test library file. The test\n");
printf(" library file is a library file (.so or\n");
printf(" .dll) that contains the set of tests\n");
printf(" to be executed.\n");
printf("\n");
printf("-t <timeout> Maximum time to run a test.\n");
printf("-e <engine_config> Engine configuration string passed to\n");
printf(" the engine.\n");
printf("-q Only print errors.");
printf("-. Print a . for each executed test.");
printf("\n");
printf("-h Prints this usage text.\n");
printf("\n");
}
static int report_test(const char *name, enum test_result r, bool quiet) {
int rc = 0;
char *msg = NULL;
bool color_enabled = getenv("TESTAPP_ENABLE_COLOR") != NULL;
int color = 0;
char color_str[8] = { 0 };
char *reset_color = "\033[m";
switch(r) {
case SUCCESS:
msg="OK";
color = 32;
break;
case SKIPPED:
msg="SKIPPED";
color = 32;
break;
case FAIL:
color = 31;
msg="FAIL";
rc = 1;
break;
case DIED:
color = 31;
msg = "DIED";
rc = 1;
break;
case TIMEOUT:
color = 31;
msg = "TIMED OUT";
rc = 1;
break;
case CORE:
color = 31;
msg = "CORE DUMPED";
rc = 1;
break;
case PENDING:
color = 33;
msg = "PENDING";
break;
}
assert(msg);
if (color_enabled) {
snprintf(color_str, sizeof(color_str), "\033[%dm", color);
}
if (quiet) {
if (r != SUCCESS) {
printf("%s: %s%s%s\n", name, color_str, msg,
color_enabled ? reset_color : "");
fflush(stdout);
}
} else {
printf("%s%s%s\n", color_str, msg, color_enabled ? reset_color : "");
}
return rc;
}
static ENGINE_HANDLE_V1 *start_your_engines(const char *engine, const char* cfg, bool engine_init) {
init_mock_server(handle);
if (!load_engine(engine, &get_mock_server_api, logger_descriptor, &handle)) {
fprintf(stderr, "Failed to load engine %s.\n", engine);
return NULL;
}
if (engine_init) {
if(!init_engine(handle, cfg, logger_descriptor)) {
fprintf(stderr, "Failed to init engine %s with config %s.\n", engine, cfg);
return NULL;
}
}
mock_engine = default_mock_engine;
handle_v1 = mock_engine.the_engine = (ENGINE_HANDLE_V1*)handle;
handle = (ENGINE_HANDLE*)&mock_engine.me;
handle_v1 = &mock_engine.me;
// Reset all members that aren't set (to allow the users to write
// testcases to verify that they initialize them..
assert(mock_engine.me.interface.interface == mock_engine.the_engine->interface.interface);
if (mock_engine.the_engine->get_stats_struct == NULL) {
mock_engine.me.get_stats_struct = NULL;
}
if (mock_engine.the_engine->aggregate_stats == NULL) {
mock_engine.me.aggregate_stats = NULL;
}
if (mock_engine.the_engine->unknown_command == NULL) {
mock_engine.me.unknown_command = NULL;
}
if (mock_engine.the_engine->tap_notify == NULL) {
mock_engine.me.tap_notify = NULL;
}
if (mock_engine.the_engine->get_tap_iterator == NULL) {
mock_engine.me.get_tap_iterator = NULL;
}
if (mock_engine.the_engine->errinfo == NULL) {
mock_engine.me.errinfo = NULL;
}
return &mock_engine.me;
}
static void destroy_engine(bool force) {
if (handle_v1) {
handle_v1->destroy(handle, force);
handle_v1 = NULL;
handle = NULL;
}
}
static void reload_engine(ENGINE_HANDLE **h, ENGINE_HANDLE_V1 **h1,
const char* engine, const char *cfg, bool init, bool force) {
destroy_engine(force);
handle_v1 = start_your_engines(engine, cfg, init);
handle = (ENGINE_HANDLE*)(handle_v1);
*h1 = handle_v1;
*h = handle;
}
static enum test_result run_test(engine_test_t test, const char *engine, const char *default_cfg) {
enum test_result ret = PENDING;
if (test.tfun != NULL) {
#if !defined(USE_GCOV) && !defined(WIN32)
pid_t pid = fork();
if (pid == 0) {
#endif
/* Start the engines and go */
start_your_engines(engine, test.cfg ? test.cfg : default_cfg, true);
if (test.test_setup != NULL) {
if (!test.test_setup(handle, handle_v1)) {
fprintf(stderr, "Failed to run setup for test %s\n", test.name);
return FAIL;
}
}
ret = test.tfun(handle, handle_v1);
if (test.test_teardown != NULL) {
if (!test.test_teardown(handle, handle_v1)) {
fprintf(stderr, "WARNING: Failed to run teardown for test %s\n", test.name);
}
}
destroy_engine(false);
#if !defined(USE_GCOV) && !defined(WIN32)
exit((int)ret);
} else if (pid == (pid_t)-1) {
ret = FAIL;
} else {
int rc;
while (alarmed == 0 && waitpid(pid, &rc, 0) == (pid_t)-1) {
if (errno != EINTR) {
abort();
}
}
if (alarmed) {
kill(pid, 9);
ret = TIMEOUT;
} else if (WIFEXITED(rc)) {
ret = (enum test_result)WEXITSTATUS(rc);
} else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) {
ret = CORE;
} else {
ret = DIED;
}
}
#endif
}
return ret;
}
static void setup_alarm_handler() {
#ifndef WIN32
struct sigaction sig_handler;
sig_handler.sa_handler = alarm_handler;
sig_handler.sa_flags = 0;
sigaction(SIGALRM, &sig_handler, NULL);
#endif
}
static void set_test_timeout(int timeout) {
#ifndef WIN32
alarm(timeout);
#endif
}
static void clear_test_timeout() {
#ifndef WIN32
alarm(0);
alarmed = 0;
#endif
}
int main(int argc, char **argv) {
int c, exitcode = 0, num_cases = 0, timeout = 0;
bool quiet = false;
bool dot = false;
const char *engine = NULL;
const char *engine_args = NULL;
const char *test_suite = NULL;
const char *test_case = NULL;
engine_test_t *testcases = NULL;
logger_descriptor = get_null_logger();
/* Hack to remove the warning from C99 */
union {
GET_TESTS get_tests;
void* voidptr;
} my_get_test = {.get_tests = NULL };
/* Hack to remove the warning from C99 */
union {
SETUP_SUITE setup_suite;
void* voidptr;
} my_setup_suite = {.setup_suite = NULL };
/* Hack to remove the warning from C99 */
union {
TEARDOWN_SUITE teardown_suite;
void* voidptr;
} my_teardown_suite = {.teardown_suite = NULL };
/* Use unbuffered stdio */
setbuf(stdout, NULL);
setbuf(stderr, NULL);
setup_alarm_handler();
/* process arguments */
while (-1 != (c = getopt(argc, argv,
"h" /* usage */
"E:" /* Engine to load */
"e:" /* Engine options */
"T:" /* Library with tests to load */
"t:" /* Timeout */
"q" /* Be more quiet (only report failures) */
"." /* dot mode. */
"n:" /* test case to run */
))) {
switch (c) {
case 'E':
engine = optarg;
break;
case 'e':
engine_args = optarg;
break;
case 'h':
usage();
return 0;
case 'T':
test_suite = optarg;
break;
case 't':
timeout = atoi(optarg);
break;
case 'n':
test_case = optarg;
break;
case 'q':
quiet = true;
break;
case '.':
dot = true;
break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
return 1;
}
}
//validate args
if (engine == NULL) {
fprintf(stderr, "You must provide a path to the storage engine library.\n");
return 1;
}
if (test_suite == NULL) {
fprintf(stderr, "You must provide a path to the testsuite library.\n");
return 1;
}
//load test_suite
void* handle = dlopen(test_suite, RTLD_NOW | RTLD_LOCAL);
if (handle == NULL) {
const char *msg = dlerror();
fprintf(stderr, "Failed to load testsuite %s: %s\n", test_suite, msg ? msg : "unknown error");
return 1;
}
//get the test cases
void *symbol = dlsym(handle, "get_tests");
if (symbol == NULL) {
const char *msg = dlerror();
fprintf(stderr, "Could not find get_tests function in testsuite %s: %s\n", test_suite, msg ? msg : "unknown error");
return 1;
}
my_get_test.voidptr = symbol;
testcases = (*my_get_test.get_tests)();
//set up the suite if needed
struct test_harness harness = { .default_engine_cfg = engine_args,
.engine_path = engine,
.reload_engine = reload_engine,
.start_engine = start_your_engines,
.create_cookie = create_mock_cookie,
.destroy_cookie = destroy_mock_cookie,
.set_ewouldblock_handling = mock_set_ewouldblock_handling,
.lock_cookie = lock_mock_cookie,
.unlock_cookie = unlock_mock_cookie,
.waitfor_cookie = waitfor_mock_cookie,
.time_travel = mock_time_travel };
symbol = dlsym(handle, "setup_suite");
if (symbol != NULL) {
my_setup_suite.voidptr = symbol;
if (!(*my_setup_suite.setup_suite)(&harness)) {
fprintf(stderr, "Failed to set up test suite %s \n", test_suite);
return 1;
}
}
for (num_cases = 0; testcases[num_cases].name; num_cases++) {
/* Just counting */
}
if (!quiet) {
printf("1..%d\n", num_cases);
}
int i;
bool need_newline = false;
for (i = 0; testcases[i].name; i++) {
if (test_case != NULL && strcmp(test_case, testcases[i].name) != 0)
continue;
if (!quiet) {
printf("Running %s... ", testcases[i].name);
fflush(stdout);
} else if(dot) {
printf(".");
need_newline = true;
/* Add a newline every few tests */
if ((i+1) % 70 == 0) {
printf("\n");
need_newline = false;
}
}
set_test_timeout(timeout);
exitcode += report_test(testcases[i].name,
run_test(testcases[i], engine, engine_args),
quiet);
clear_test_timeout();
}
if (need_newline) {
printf("\n");
}
//tear down the suite if needed
symbol = dlsym(handle, "teardown_suite");
if (symbol != NULL) {
my_teardown_suite.voidptr = symbol;
if (!(*my_teardown_suite.teardown_suite)()) {
fprintf(stderr, "Failed to teardown up test suite %s \n", test_suite);
}
}
printf("# Passed %d of %d tests\n", num_cases - exitcode, num_cases);
return exitcode;
}