13 #include "utilities/engine_loader.h"
14 #include <memcached/engine_testapp.h>
15 #include <memcached/extension_loggers.h>
16 #include <mock_server.h>
25 static sig_atomic_t alarmed;
27 static void alarm_handler(
int sig) {
37 const void *cookie, item **itm,
38 void **es, uint16_t *nes, uint8_t *ttl,
39 uint16_t *
flags, uint32_t *seqno,
42 return me->iterator((
ENGINE_HANDLE*)me->the_engine, cookie, itm, es, nes,
43 ttl, flags, seqno, vbucket);
51 static ENGINE_ERROR_CODE mock_initialize(
ENGINE_HANDLE* handle,
52 const char* config_str) {
57 static void mock_destroy(
ENGINE_HANDLE* handle,
const bool force) {
69 const rel_time_t exptime) {
73 c = (
void*)create_mock_cookie();
77 ENGINE_ERROR_CODE
ret = ENGINE_SUCCESS;
78 pthread_mutex_lock(&c->mutex);
79 while (ret == ENGINE_SUCCESS &&
83 exptime)) == ENGINE_EWOULDBLOCK &&
84 c->handle_ewouldblock)
87 pthread_cond_wait(&c->cond, &c->mutex);
90 pthread_mutex_unlock(&c->mutex);
93 destroy_mock_cookie(c);
109 c = (
void*)create_mock_cookie();
113 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
114 pthread_mutex_lock(&c->mutex);
115 while (ret == ENGINE_SUCCESS &&
117 nkey, cas, vbucket)) == ENGINE_EWOULDBLOCK &&
118 c->handle_ewouldblock)
121 pthread_cond_wait(&c->cond, &c->mutex);
124 pthread_mutex_unlock(&c->mutex);
127 destroy_mock_cookie(c);
149 c = (
void*)create_mock_cookie();
153 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
154 pthread_mutex_lock(&c->mutex);
155 while (ret == ENGINE_SUCCESS &&
157 key, nkey, vbucket)) == ENGINE_EWOULDBLOCK &&
158 c->handle_ewouldblock)
161 pthread_cond_wait(&c->cond, &c->mutex);
164 pthread_mutex_unlock(&c->mutex);
167 destroy_mock_cookie(c);
173 static ENGINE_ERROR_CODE mock_get_stats(
ENGINE_HANDLE* handle,
175 const char* stat_key,
182 c = (
void*)create_mock_cookie();
186 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
187 pthread_mutex_lock(&c->mutex);
188 while (ret == ENGINE_SUCCESS &&
190 nkey, add_stat)) == ENGINE_EWOULDBLOCK &&
191 c->handle_ewouldblock)
194 pthread_cond_wait(&c->cond, &c->mutex);
197 pthread_mutex_unlock(&c->mutex);
200 destroy_mock_cookie(c);
210 ENGINE_STORE_OPERATION operation,
215 c = (
void*)create_mock_cookie();
219 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
220 pthread_mutex_lock(&c->mutex);
221 while (ret == ENGINE_SUCCESS &&
223 operation, vbucket)) == ENGINE_EWOULDBLOCK &&
224 c->handle_ewouldblock)
227 pthread_cond_wait(&c->cond, &c->mutex);
230 pthread_mutex_unlock(&c->mutex);
233 destroy_mock_cookie(c);
239 static ENGINE_ERROR_CODE mock_arithmetic(
ENGINE_HANDLE* handle,
243 const bool increment,
245 const uint64_t delta,
246 const uint64_t initial,
247 const rel_time_t exptime,
254 c = (
void*)create_mock_cookie();
258 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
259 pthread_mutex_lock(&c->mutex);
260 while (ret == ENGINE_SUCCESS &&
262 nkey, increment, create,
263 delta, initial, exptime,
264 cas, result, vbucket)) == ENGINE_EWOULDBLOCK &&
265 c->handle_ewouldblock)
268 pthread_cond_wait(&c->cond, &c->mutex);
271 pthread_mutex_unlock(&c->mutex);
274 destroy_mock_cookie(c);
281 const void* cookie, time_t when) {
285 c = (
void*)create_mock_cookie();
289 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
290 pthread_mutex_lock(&c->mutex);
291 while (ret == ENGINE_SUCCESS &&
292 (ret = me->the_engine->
flush((
ENGINE_HANDLE*)me->the_engine, c, when)) == ENGINE_EWOULDBLOCK &&
293 c->handle_ewouldblock)
296 pthread_cond_wait(&c->cond, &c->mutex);
299 pthread_mutex_unlock(&c->mutex);
302 destroy_mock_cookie(c);
308 static void mock_reset_stats(
ENGINE_HANDLE* handle,
const void *cookie) {
313 static ENGINE_ERROR_CODE mock_unknown_command(
ENGINE_HANDLE* handle,
321 c = (
void*)create_mock_cookie();
325 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
326 pthread_mutex_lock(&c->mutex);
327 while (ret == ENGINE_SUCCESS &&
329 request, response)) == ENGINE_EWOULDBLOCK &&
330 c->handle_ewouldblock)
333 pthread_cond_wait(&c->cond, &c->mutex);
336 pthread_mutex_unlock(&c->mutex);
339 destroy_mock_cookie(c);
345 static void mock_item_set_cas(
ENGINE_HANDLE *handle,
const void *cookie,
346 item* item, uint64_t val)
353 static bool mock_get_item_info(
ENGINE_HANDLE *handle,
const void *cookie,
358 cookie, item, item_info);
361 static void *mock_get_stats_struct(
ENGINE_HANDLE* handle,
const void* cookie)
367 static ENGINE_ERROR_CODE mock_aggregate_stats(
ENGINE_HANDLE* handle,
369 void (*callback)(
void*,
void*),
375 c = (
void*)create_mock_cookie();
379 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
380 pthread_mutex_lock(&c->mutex);
381 while (ret == ENGINE_SUCCESS &&
383 callback, vptr)) == ENGINE_EWOULDBLOCK &&
384 c->handle_ewouldblock)
387 pthread_cond_wait(&c->cond, &c->mutex);
390 pthread_mutex_unlock(&c->mutex);
393 destroy_mock_cookie(c);
399 static ENGINE_ERROR_CODE mock_tap_notify(
ENGINE_HANDLE* handle,
401 void *engine_specific,
405 tap_event_t tap_event,
419 c = (
void*)create_mock_cookie();
423 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
424 pthread_mutex_lock(&c->mutex);
425 while (ret == ENGINE_SUCCESS &&
427 engine_specific, nengine, ttl, tap_flags,
428 tap_event, tap_seqno, key, nkey, flags,
429 exptime, cas, data, ndata, vbucket)) == ENGINE_EWOULDBLOCK &&
430 c->handle_ewouldblock)
433 pthread_cond_wait(&c->cond, &c->mutex);
436 pthread_mutex_unlock(&c->mutex);
439 destroy_mock_cookie(c);
447 const void* client,
size_t nclient,
449 const void* userdata,
size_t nuserdata) {
452 client, nclient, flags, userdata, nuserdata);
453 return (me->iterator != NULL) ? mock_tap_iterator : NULL;
456 static size_t mock_errinfo(
ENGINE_HANDLE *handle,
const void* cookie,
457 char *buffer,
size_t buffsz) {
469 .get_info = mock_get_info,
470 .initialize = mock_initialize,
471 .destroy = mock_destroy,
472 .allocate = mock_allocate,
473 .remove = mock_remove,
474 .release = mock_release,
477 .arithmetic = mock_arithmetic,
479 .get_stats = mock_get_stats,
480 .reset_stats = mock_reset_stats,
481 .get_stats_struct = mock_get_stats_struct,
482 .aggregate_stats = mock_aggregate_stats,
483 .unknown_command = mock_unknown_command,
484 .tap_notify = mock_tap_notify,
485 .get_tap_iterator = mock_get_tap_iterator,
486 .item_set_cas = mock_item_set_cas,
487 .get_item_info = mock_get_item_info,
488 .errinfo = mock_errinfo
497 static void usage(
void) {
499 printf(
"engine_testapp -E <path_to_engine_lib> -T <path_to_testlib>\n");
500 printf(
" [-e <engine_config>] [-h]\n");
502 printf(
"-E <path_to_engine_lib> Path to the engine library file. The\n");
503 printf(
" engine library file is a library file\n");
504 printf(
" (.so or .dll) that the contains the \n");
505 printf(
" implementation of the engine being\n");
506 printf(
" tested.\n");
508 printf(
"-T <path_to_testlib> Path to the test library file. The test\n");
509 printf(
" library file is a library file (.so or\n");
510 printf(
" .dll) that contains the set of tests\n");
511 printf(
" to be executed.\n");
513 printf(
"-t <timeout> Maximum time to run a test.\n");
514 printf(
"-e <engine_config> Engine configuration string passed to\n");
515 printf(
" the engine.\n");
516 printf(
"-q Only print errors.");
517 printf(
"-. Print a . for each executed test.");
519 printf(
"-h Prints this usage text.\n");
523 static int report_test(
const char *
name,
enum test_result r,
bool quiet) {
526 bool color_enabled = getenv(
"TESTAPP_ENABLE_COLOR") != NULL;
528 char color_str[8] = { 0 };
529 char *reset_color =
"\033[m";
566 snprintf(color_str,
sizeof(color_str),
"\033[%dm", color);
570 printf(
"%s: %s%s%s\n", name, color_str, msg,
571 color_enabled ? reset_color :
"");
575 printf(
"%s%s%s\n", color_str, msg, color_enabled ? reset_color :
"");
580 static ENGINE_HANDLE_V1 *start_your_engines(
const char *engine,
const char* cfg,
bool engine_init) {
582 init_mock_server(handle);
583 if (!load_engine(engine, &get_mock_server_api, logger_descriptor, &handle)) {
584 fprintf(stderr,
"Failed to load engine %s.\n", engine);
589 if(!init_engine(handle, cfg, logger_descriptor)) {
590 fprintf(stderr,
"Failed to init engine %s with config %s.\n", engine, cfg);
626 static void destroy_engine(
bool force) {
628 handle_v1->
destroy(handle, force);
635 const char* engine,
const char *cfg,
bool init,
bool force) {
636 destroy_engine(force);
637 handle_v1 = start_your_engines(engine, cfg, init);
643 static enum test_result run_test(
engine_test_t test,
const char *engine,
const char *default_cfg) {
644 enum test_result ret = PENDING;
645 if (test.tfun != NULL) {
646 #if !defined(USE_GCOV) && !defined(WIN32)
651 start_your_engines(engine, test.cfg ? test.cfg : default_cfg,
true);
652 if (test.test_setup != NULL) {
653 if (!test.test_setup(handle, handle_v1)) {
654 fprintf(stderr,
"Failed to run setup for test %s\n", test.name);
658 ret = test.tfun(handle, handle_v1);
659 if (test.test_teardown != NULL) {
660 if (!test.test_teardown(handle, handle_v1)) {
661 fprintf(stderr,
"WARNING: Failed to run teardown for test %s\n", test.name);
664 destroy_engine(
false);
665 #if !defined(USE_GCOV) && !defined(WIN32)
667 }
else if (pid == (pid_t)-1) {
671 while (alarmed == 0 && waitpid(pid, &rc, 0) == (pid_t)-1) {
672 if (errno != EINTR) {
680 }
else if (WIFEXITED(rc)) {
681 ret = (
enum test_result)WEXITSTATUS(rc);
682 }
else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) {
694 static void setup_alarm_handler() {
698 sig_handler.sa_handler = alarm_handler;
699 sig_handler.sa_flags = 0;
705 static void set_test_timeout(
int timeout) {
711 static void clear_test_timeout() {
718 int main(
int argc,
char **argv) {
719 int c, exitcode = 0, num_cases = 0, timeout = 0;
722 const char *engine = NULL;
723 const char *engine_args = NULL;
724 const char *test_suite = NULL;
727 logger_descriptor = get_null_logger();
733 } my_get_test = {.get_tests = NULL };
737 SETUP_SUITE setup_suite;
739 } my_setup_suite = {.setup_suite = NULL };
743 TEARDOWN_SUITE teardown_suite;
745 } my_teardown_suite = {.teardown_suite = NULL };
749 setbuf(stdout, NULL);
750 setbuf(stderr, NULL);
752 setup_alarm_handler();
755 while (-1 != (c = getopt(argc, argv,
770 engine_args = optarg;
779 timeout = atoi(optarg);
791 fprintf(stderr,
"Illegal argument \"%c\"\n", c);
797 if (engine == NULL) {
798 fprintf(stderr,
"You must provide a path to the storage engine library.\n");
802 if (test_suite == NULL) {
803 fprintf(stderr,
"You must provide a path to the testsuite library.\n");
808 void* handle = dlopen(test_suite, RTLD_NOW | RTLD_LOCAL);
809 if (handle == NULL) {
810 const char *msg = dlerror();
811 fprintf(stderr,
"Failed to load testsuite %s: %s\n", test_suite, msg ? msg :
"unknown error");
816 void *symbol = dlsym(handle,
"get_tests");
817 if (symbol == NULL) {
818 const char *msg = dlerror();
819 fprintf(stderr,
"Could not find get_tests function in testsuite %s: %s\n", test_suite, msg ? msg :
"unknown error");
822 my_get_test.voidptr = symbol;
823 testcases = (*my_get_test.get_tests)();
826 struct test_harness harness = { .default_engine_cfg = engine_args,
827 .engine_path = engine,
828 .reload_engine = reload_engine,
829 .start_engine = start_your_engines,
830 .create_cookie = create_mock_cookie,
831 .destroy_cookie = destroy_mock_cookie,
832 .set_ewouldblock_handling = mock_set_ewouldblock_handling,
833 .lock_cookie = lock_mock_cookie,
834 .unlock_cookie = unlock_mock_cookie,
835 .waitfor_cookie = waitfor_mock_cookie,
836 .time_travel = mock_time_travel };
837 symbol = dlsym(handle,
"setup_suite");
838 if (symbol != NULL) {
839 my_setup_suite.voidptr = symbol;
840 if (!(*my_setup_suite.setup_suite)(&harness)) {
841 fprintf(stderr,
"Failed to set up test suite %s \n", test_suite);
847 for (num_cases = 0; testcases[num_cases].name; num_cases++) {
852 printf(
"1..%d\n", num_cases);
856 bool need_newline =
false;
857 for (i = 0; testcases[
i].name; i++) {
858 if (test_case != NULL && strcmp(test_case, testcases[i].name) != 0)
861 printf(
"Running %s... ", testcases[i].name);
867 if ((i+1) % 70 == 0) {
869 need_newline =
false;
872 set_test_timeout(timeout);
873 exitcode += report_test(testcases[i].name,
874 run_test(testcases[i], engine, engine_args),
876 clear_test_timeout();
884 symbol = dlsym(handle,
"teardown_suite");
885 if (symbol != NULL) {
886 my_teardown_suite.voidptr = symbol;
887 if (!(*my_teardown_suite.teardown_suite)()) {
888 fprintf(stderr,
"Failed to teardown up test suite %s \n", test_suite);
892 printf(
"# Passed %d of %d tests\n", num_cases - exitcode, num_cases);