1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 #include <assert.h>
4 #include <dlfcn.h>
5 #include <errno.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <signal.h>
12 #include <pthread.h>
13 #include "utilities/engine_loader.h"
14 #include <memcached/engine_testapp.h>
15 #include <memcached/extension_loggers.h>
16 #include <mock_server.h>
17
18 struct mock_engine {
19 ENGINE_HANDLE_V1 me;
20 ENGINE_HANDLE_V1 *the_engine;
21 TAP_ITERATOR iterator;
22 };
23
24 #ifndef WIN32
25 static sig_atomic_t alarmed;
26
alarm_handler(int sig)27 static void alarm_handler(int sig) {
28 alarmed = 1;
29 }
30 #endif
31
get_handle(ENGINE_HANDLE * handle)32 static inline struct mock_engine* get_handle(ENGINE_HANDLE* handle) {
33 return (struct mock_engine*)handle;
34 }
35
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)36 static tap_event_t mock_tap_iterator(ENGINE_HANDLE* handle,
37 const void *cookie, item **itm,
38 void **es, uint16_t *nes, uint8_t *ttl,
39 uint16_t *flags, uint32_t *seqno,
40 uint16_t *vbucket) {
41 struct mock_engine *me = get_handle(handle);
42 return me->iterator((ENGINE_HANDLE*)me->the_engine, cookie, itm, es, nes,
43 ttl, flags, seqno, vbucket);
44 }
45
mock_get_info(ENGINE_HANDLE * handle)46 static const engine_info* mock_get_info(ENGINE_HANDLE* handle) {
47 struct mock_engine *me = get_handle(handle);
48 return me->the_engine->get_info((ENGINE_HANDLE*)me->the_engine);
49 }
50
mock_initialize(ENGINE_HANDLE * handle,const char * config_str)51 static ENGINE_ERROR_CODE mock_initialize(ENGINE_HANDLE* handle,
52 const char* config_str) {
53 struct mock_engine *me = get_handle(handle);
54 return me->the_engine->initialize((ENGINE_HANDLE*)me->the_engine, config_str);
55 }
56
mock_destroy(ENGINE_HANDLE * handle,const bool force)57 static void mock_destroy(ENGINE_HANDLE* handle, const bool force) {
58 struct mock_engine *me = get_handle(handle);
59 me->the_engine->destroy((ENGINE_HANDLE*)me->the_engine, force);
60 }
61
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)62 static ENGINE_ERROR_CODE mock_allocate(ENGINE_HANDLE* handle,
63 const void* cookie,
64 item **item,
65 const void* key,
66 const size_t nkey,
67 const size_t nbytes,
68 const int flags,
69 const rel_time_t exptime) {
70 struct mock_engine *me = get_handle(handle);
71 struct mock_connstruct *c = (void*)cookie;
72 if (c == NULL) {
73 c = (void*)create_mock_cookie();
74 }
75
76 c->nblocks = 0;
77 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
78 pthread_mutex_lock(&c->mutex);
79 while (ret == ENGINE_SUCCESS &&
80 (ret = me->the_engine->allocate((ENGINE_HANDLE*)me->the_engine, c,
81 item, key, nkey,
82 nbytes, flags,
83 exptime)) == ENGINE_EWOULDBLOCK &&
84 c->handle_ewouldblock)
85 {
86 ++c->nblocks;
87 pthread_cond_wait(&c->cond, &c->mutex);
88 ret = c->status;
89 }
90 pthread_mutex_unlock(&c->mutex);
91
92 if (c != cookie) {
93 destroy_mock_cookie(c);
94 }
95
96 return ret;
97 }
98
mock_remove(ENGINE_HANDLE * handle,const void * cookie,const void * key,const size_t nkey,uint64_t cas,uint16_t vbucket)99 static ENGINE_ERROR_CODE mock_remove(ENGINE_HANDLE* handle,
100 const void* cookie,
101 const void* key,
102 const size_t nkey,
103 uint64_t cas,
104 uint16_t vbucket)
105 {
106 struct mock_engine *me = get_handle(handle);
107 struct mock_connstruct *c = (void*)cookie;
108 if (c == NULL) {
109 c = (void*)create_mock_cookie();
110 }
111
112 c->nblocks = 0;
113 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
114 pthread_mutex_lock(&c->mutex);
115 while (ret == ENGINE_SUCCESS &&
116 (ret = me->the_engine->remove((ENGINE_HANDLE*)me->the_engine, c, key,
117 nkey, cas, vbucket)) == ENGINE_EWOULDBLOCK &&
118 c->handle_ewouldblock)
119 {
120 ++c->nblocks;
121 pthread_cond_wait(&c->cond, &c->mutex);
122 ret = c->status;
123 }
124 pthread_mutex_unlock(&c->mutex);
125
126 if (c != cookie) {
127 destroy_mock_cookie(c);
128 }
129
130 return ret;
131 }
132
mock_release(ENGINE_HANDLE * handle,const void * cookie,item * item)133 static void mock_release(ENGINE_HANDLE* handle,
134 const void *cookie,
135 item* item) {
136 struct mock_engine *me = get_handle(handle);
137 me->the_engine->release((ENGINE_HANDLE*)me->the_engine, cookie, item);
138 }
139
mock_get(ENGINE_HANDLE * handle,const void * cookie,item ** item,const void * key,const int nkey,uint16_t vbucket)140 static ENGINE_ERROR_CODE mock_get(ENGINE_HANDLE* handle,
141 const void* cookie,
142 item** item,
143 const void* key,
144 const int nkey,
145 uint16_t vbucket) {
146 struct mock_engine *me = get_handle(handle);
147 struct mock_connstruct *c = (void*)cookie;
148 if (c == NULL) {
149 c = (void*)create_mock_cookie();
150 }
151
152 c->nblocks = 0;
153 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
154 pthread_mutex_lock(&c->mutex);
155 while (ret == ENGINE_SUCCESS &&
156 (ret = me->the_engine->get((ENGINE_HANDLE*)me->the_engine, c, item,
157 key, nkey, vbucket)) == ENGINE_EWOULDBLOCK &&
158 c->handle_ewouldblock)
159 {
160 ++c->nblocks;
161 pthread_cond_wait(&c->cond, &c->mutex);
162 ret = c->status;
163 }
164 pthread_mutex_unlock(&c->mutex);
165
166 if (c != cookie) {
167 destroy_mock_cookie(c);
168 }
169
170 return ret;
171 }
172
mock_get_stats(ENGINE_HANDLE * handle,const void * cookie,const char * stat_key,int nkey,ADD_STAT add_stat)173 static ENGINE_ERROR_CODE mock_get_stats(ENGINE_HANDLE* handle,
174 const void* cookie,
175 const char* stat_key,
176 int nkey,
177 ADD_STAT add_stat)
178 {
179 struct mock_engine *me = get_handle(handle);
180 struct mock_connstruct *c = (void*)cookie;
181 if (c == NULL) {
182 c = (void*)create_mock_cookie();
183 }
184
185 c->nblocks = 0;
186 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
187 pthread_mutex_lock(&c->mutex);
188 while (ret == ENGINE_SUCCESS &&
189 (ret = me->the_engine->get_stats((ENGINE_HANDLE*)me->the_engine, c, stat_key,
190 nkey, add_stat)) == ENGINE_EWOULDBLOCK &&
191 c->handle_ewouldblock)
192 {
193 ++c->nblocks;
194 pthread_cond_wait(&c->cond, &c->mutex);
195 ret = c->status;
196 }
197 pthread_mutex_unlock(&c->mutex);
198
199 if (c != cookie) {
200 destroy_mock_cookie(c);
201 }
202
203 return ret;
204 }
205
mock_store(ENGINE_HANDLE * handle,const void * cookie,item * item,uint64_t * cas,ENGINE_STORE_OPERATION operation,uint16_t vbucket)206 static ENGINE_ERROR_CODE mock_store(ENGINE_HANDLE* handle,
207 const void *cookie,
208 item* item,
209 uint64_t *cas,
210 ENGINE_STORE_OPERATION operation,
211 uint16_t vbucket) {
212 struct mock_engine *me = get_handle(handle);
213 struct mock_connstruct *c = (void*)cookie;
214 if (c == NULL) {
215 c = (void*)create_mock_cookie();
216 }
217
218 c->nblocks = 0;
219 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
220 pthread_mutex_lock(&c->mutex);
221 while (ret == ENGINE_SUCCESS &&
222 (ret = me->the_engine->store((ENGINE_HANDLE*)me->the_engine, c, item, cas,
223 operation, vbucket)) == ENGINE_EWOULDBLOCK &&
224 c->handle_ewouldblock)
225 {
226 ++c->nblocks;
227 pthread_cond_wait(&c->cond, &c->mutex);
228 ret = c->status;
229 }
230 pthread_mutex_unlock(&c->mutex);
231
232 if (c != cookie) {
233 destroy_mock_cookie(c);
234 }
235
236 return ret;
237 }
238
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)239 static ENGINE_ERROR_CODE mock_arithmetic(ENGINE_HANDLE* handle,
240 const void* cookie,
241 const void* key,
242 const int nkey,
243 const bool increment,
244 const bool create,
245 const uint64_t delta,
246 const uint64_t initial,
247 const rel_time_t exptime,
248 uint64_t *cas,
249 uint64_t *result,
250 uint16_t vbucket) {
251 struct mock_engine *me = get_handle(handle);
252 struct mock_connstruct *c = (void*)cookie;
253 if (c == NULL) {
254 c = (void*)create_mock_cookie();
255 }
256
257 c->nblocks = 0;
258 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
259 pthread_mutex_lock(&c->mutex);
260 while (ret == ENGINE_SUCCESS &&
261 (ret = me->the_engine->arithmetic((ENGINE_HANDLE*)me->the_engine, c, key,
262 nkey, increment, create,
263 delta, initial, exptime,
264 cas, result, vbucket)) == ENGINE_EWOULDBLOCK &&
265 c->handle_ewouldblock)
266 {
267 ++c->nblocks;
268 pthread_cond_wait(&c->cond, &c->mutex);
269 ret = c->status;
270 }
271 pthread_mutex_unlock(&c->mutex);
272
273 if (c != cookie) {
274 destroy_mock_cookie(c);
275 }
276
277 return ret;
278 }
279
mock_flush(ENGINE_HANDLE * handle,const void * cookie,time_t when)280 static ENGINE_ERROR_CODE mock_flush(ENGINE_HANDLE* handle,
281 const void* cookie, time_t when) {
282 struct mock_engine *me = get_handle(handle);
283 struct mock_connstruct *c = (void*)cookie;
284 if (c == NULL) {
285 c = (void*)create_mock_cookie();
286 }
287
288 c->nblocks = 0;
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)
294 {
295 ++c->nblocks;
296 pthread_cond_wait(&c->cond, &c->mutex);
297 ret = c->status;
298 }
299 pthread_mutex_unlock(&c->mutex);
300
301 if (c != cookie) {
302 destroy_mock_cookie(c);
303 }
304
305 return ret;
306 }
307
mock_reset_stats(ENGINE_HANDLE * handle,const void * cookie)308 static void mock_reset_stats(ENGINE_HANDLE* handle, const void *cookie) {
309 struct mock_engine *me = get_handle(handle);
310 me->the_engine->reset_stats((ENGINE_HANDLE*)me->the_engine, cookie);
311 }
312
mock_unknown_command(ENGINE_HANDLE * handle,const void * cookie,protocol_binary_request_header * request,ADD_RESPONSE response)313 static ENGINE_ERROR_CODE mock_unknown_command(ENGINE_HANDLE* handle,
314 const void* cookie,
315 protocol_binary_request_header *request,
316 ADD_RESPONSE response)
317 {
318 struct mock_engine *me = get_handle(handle);
319 struct mock_connstruct *c = (void*)cookie;
320 if (c == NULL) {
321 c = (void*)create_mock_cookie();
322 }
323
324 c->nblocks = 0;
325 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
326 pthread_mutex_lock(&c->mutex);
327 while (ret == ENGINE_SUCCESS &&
328 (ret = me->the_engine->unknown_command((ENGINE_HANDLE*)me->the_engine, c,
329 request, response)) == ENGINE_EWOULDBLOCK &&
330 c->handle_ewouldblock)
331 {
332 ++c->nblocks;
333 pthread_cond_wait(&c->cond, &c->mutex);
334 ret = c->status;
335 }
336 pthread_mutex_unlock(&c->mutex);
337
338 if (c != cookie) {
339 destroy_mock_cookie(c);
340 }
341
342 return ret;
343 }
344
mock_item_set_cas(ENGINE_HANDLE * handle,const void * cookie,item * item,uint64_t val)345 static void mock_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,
346 item* item, uint64_t val)
347 {
348 struct mock_engine *me = get_handle(handle);
349 me->the_engine->item_set_cas((ENGINE_HANDLE*)me->the_engine, cookie, item, val);
350 }
351
352
mock_get_item_info(ENGINE_HANDLE * handle,const void * cookie,const item * item,item_info * item_info)353 static bool mock_get_item_info(ENGINE_HANDLE *handle, const void *cookie,
354 const item* item, item_info *item_info)
355 {
356 struct mock_engine *me = get_handle(handle);
357 return me->the_engine->get_item_info((ENGINE_HANDLE*)me->the_engine,
358 cookie, item, item_info);
359 }
360
mock_get_stats_struct(ENGINE_HANDLE * handle,const void * cookie)361 static void *mock_get_stats_struct(ENGINE_HANDLE* handle, const void* cookie)
362 {
363 struct mock_engine *me = get_handle(handle);
364 return me->the_engine->get_stats_struct((ENGINE_HANDLE*)me->the_engine, cookie);
365 }
366
mock_aggregate_stats(ENGINE_HANDLE * handle,const void * cookie,void (* callback)(void *,void *),void * vptr)367 static ENGINE_ERROR_CODE mock_aggregate_stats(ENGINE_HANDLE* handle,
368 const void* cookie,
369 void (*callback)(void*, void*),
370 void *vptr)
371 {
372 struct mock_engine *me = get_handle(handle);
373 struct mock_connstruct *c = (void*)cookie;
374 if (c == NULL) {
375 c = (void*)create_mock_cookie();
376 }
377
378 c->nblocks = 0;
379 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
380 pthread_mutex_lock(&c->mutex);
381 while (ret == ENGINE_SUCCESS &&
382 (ret = me->the_engine->aggregate_stats((ENGINE_HANDLE*)me->the_engine, c,
383 callback, vptr)) == ENGINE_EWOULDBLOCK &&
384 c->handle_ewouldblock)
385 {
386 ++c->nblocks;
387 pthread_cond_wait(&c->cond, &c->mutex);
388 ret = c->status;
389 }
390 pthread_mutex_unlock(&c->mutex);
391
392 if (c != cookie) {
393 destroy_mock_cookie(c);
394 }
395
396 return ret;
397 }
398
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)399 static ENGINE_ERROR_CODE mock_tap_notify(ENGINE_HANDLE* handle,
400 const void *cookie,
401 void *engine_specific,
402 uint16_t nengine,
403 uint8_t ttl,
404 uint16_t tap_flags,
405 tap_event_t tap_event,
406 uint32_t tap_seqno,
407 const void *key,
408 size_t nkey,
409 uint32_t flags,
410 uint32_t exptime,
411 uint64_t cas,
412 const void *data,
413 size_t ndata,
414 uint16_t vbucket) {
415
416 struct mock_engine *me = get_handle(handle);
417 struct mock_connstruct *c = (void*)cookie;
418 if (c == NULL) {
419 c = (void*)create_mock_cookie();
420 }
421
422 c->nblocks = 0;
423 ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
424 pthread_mutex_lock(&c->mutex);
425 while (ret == ENGINE_SUCCESS &&
426 (ret = me->the_engine->tap_notify((ENGINE_HANDLE*)me->the_engine, c,
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)
431 {
432 ++c->nblocks;
433 pthread_cond_wait(&c->cond, &c->mutex);
434 ret = c->status;
435 }
436 pthread_mutex_unlock(&c->mutex);
437
438 if (c != cookie) {
439 destroy_mock_cookie(c);
440 }
441
442 return ret;
443 }
444
445
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)446 static TAP_ITERATOR mock_get_tap_iterator(ENGINE_HANDLE* handle, const void* cookie,
447 const void* client, size_t nclient,
448 uint32_t flags,
449 const void* userdata, size_t nuserdata) {
450 struct mock_engine *me = get_handle(handle);
451 me->iterator = me->the_engine->get_tap_iterator((ENGINE_HANDLE*)me->the_engine, cookie,
452 client, nclient, flags, userdata, nuserdata);
453 return (me->iterator != NULL) ? mock_tap_iterator : NULL;
454 }
455
mock_errinfo(ENGINE_HANDLE * handle,const void * cookie,char * buffer,size_t buffsz)456 static size_t mock_errinfo(ENGINE_HANDLE *handle, const void* cookie,
457 char *buffer, size_t buffsz) {
458 struct mock_engine *me = get_handle(handle);
459 return me->the_engine->errinfo((ENGINE_HANDLE*)me->the_engine, cookie,
460 buffer, buffsz);
461 }
462
463
464 struct mock_engine default_mock_engine = {
465 .me = {
466 .interface = {
467 .interface = 1
468 },
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,
475 .get = mock_get,
476 .store = mock_store,
477 .arithmetic = mock_arithmetic,
478 .flush = mock_flush,
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
489 }
490 };
491 struct mock_engine mock_engine;
492
493 EXTENSION_LOGGER_DESCRIPTOR *logger_descriptor = NULL;
494 static ENGINE_HANDLE *handle = NULL;
495 static ENGINE_HANDLE_V1 *handle_v1 = NULL;
496
usage(void)497 static void usage(void) {
498 printf("\n");
499 printf("engine_testapp -E <path_to_engine_lib> -T <path_to_testlib>\n");
500 printf(" [-e <engine_config>] [-h]\n");
501 printf("\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");
507 printf("\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");
512 printf("\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.");
518 printf("\n");
519 printf("-h Prints this usage text.\n");
520 printf("\n");
521 }
522
report_test(const char * name,enum test_result r,bool quiet)523 static int report_test(const char *name, enum test_result r, bool quiet) {
524 int rc = 0;
525 char *msg = NULL;
526 bool color_enabled = getenv("TESTAPP_ENABLE_COLOR") != NULL;
527 int color = 0;
528 char color_str[8] = { 0 };
529 char *reset_color = "\033[m";
530 switch(r) {
531 case SUCCESS:
532 msg="OK";
533 color = 32;
534 break;
535 case SKIPPED:
536 msg="SKIPPED";
537 color = 32;
538 break;
539 case FAIL:
540 color = 31;
541 msg="FAIL";
542 rc = 1;
543 break;
544 case DIED:
545 color = 31;
546 msg = "DIED";
547 rc = 1;
548 break;
549 case TIMEOUT:
550 color = 31;
551 msg = "TIMED OUT";
552 rc = 1;
553 break;
554 case CORE:
555 color = 31;
556 msg = "CORE DUMPED";
557 rc = 1;
558 break;
559 case PENDING:
560 color = 33;
561 msg = "PENDING";
562 break;
563 }
564 assert(msg);
565 if (color_enabled) {
566 snprintf(color_str, sizeof(color_str), "\033[%dm", color);
567 }
568 if (quiet) {
569 if (r != SUCCESS) {
570 printf("%s: %s%s%s\n", name, color_str, msg,
571 color_enabled ? reset_color : "");
572 fflush(stdout);
573 }
574 } else {
575 printf("%s%s%s\n", color_str, msg, color_enabled ? reset_color : "");
576 }
577 return rc;
578 }
579
start_your_engines(const char * engine,const char * cfg,bool engine_init)580 static ENGINE_HANDLE_V1 *start_your_engines(const char *engine, const char* cfg, bool engine_init) {
581
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);
585 return NULL;
586 }
587
588 if (engine_init) {
589 if(!init_engine(handle, cfg, logger_descriptor)) {
590 fprintf(stderr, "Failed to init engine %s with config %s.\n", engine, cfg);
591 return NULL;
592 }
593 }
594
595 mock_engine = default_mock_engine;
596 handle_v1 = mock_engine.the_engine = (ENGINE_HANDLE_V1*)handle;
597 handle = (ENGINE_HANDLE*)&mock_engine.me;
598 handle_v1 = &mock_engine.me;
599
600 // Reset all members that aren't set (to allow the users to write
601 // testcases to verify that they initialize them..
602 assert(mock_engine.me.interface.interface == mock_engine.the_engine->interface.interface);
603
604 if (mock_engine.the_engine->get_stats_struct == NULL) {
605 mock_engine.me.get_stats_struct = NULL;
606 }
607 if (mock_engine.the_engine->aggregate_stats == NULL) {
608 mock_engine.me.aggregate_stats = NULL;
609 }
610 if (mock_engine.the_engine->unknown_command == NULL) {
611 mock_engine.me.unknown_command = NULL;
612 }
613 if (mock_engine.the_engine->tap_notify == NULL) {
614 mock_engine.me.tap_notify = NULL;
615 }
616 if (mock_engine.the_engine->get_tap_iterator == NULL) {
617 mock_engine.me.get_tap_iterator = NULL;
618 }
619 if (mock_engine.the_engine->errinfo == NULL) {
620 mock_engine.me.errinfo = NULL;
621 }
622
623 return &mock_engine.me;
624 }
625
destroy_engine(bool force)626 static void destroy_engine(bool force) {
627 if (handle_v1) {
628 handle_v1->destroy(handle, force);
629 handle_v1 = NULL;
630 handle = NULL;
631 }
632 }
633
reload_engine(ENGINE_HANDLE ** h,ENGINE_HANDLE_V1 ** h1,const char * engine,const char * cfg,bool init,bool force)634 static void reload_engine(ENGINE_HANDLE **h, ENGINE_HANDLE_V1 **h1,
635 const char* engine, const char *cfg, bool init, bool force) {
636 destroy_engine(force);
637 handle_v1 = start_your_engines(engine, cfg, init);
638 handle = (ENGINE_HANDLE*)(handle_v1);
639 *h1 = handle_v1;
640 *h = handle;
641 }
642
643 static engine_test_t* current_testcase;
644
get_current_testcase(void)645 static const engine_test_t* get_current_testcase(void)
646 {
647 return current_testcase;
648 }
649
650
run_test(engine_test_t test,const char * engine,const char * default_cfg)651 static enum test_result run_test(engine_test_t test, const char *engine, const char *default_cfg) {
652 enum test_result ret = PENDING;
653 if (test.tfun != NULL) {
654 #if !defined(USE_GCOV) && !defined(WIN32)
655 pid_t pid = fork();
656 if (pid == 0) {
657 #endif
658 current_testcase = &test;
659 if (test.prepare != NULL) {
660 if ((ret = test.prepare(&test)) == SUCCESS) {
661 ret = PENDING;
662 }
663 }
664
665 if (ret == PENDING) {
666 /* Start the engines and go */
667 start_your_engines(engine, test.cfg ? test.cfg : default_cfg, true);
668 if (test.test_setup != NULL) {
669 if (!test.test_setup(handle, handle_v1)) {
670 fprintf(stderr, "Failed to run setup for test %s\n", test.name);
671 #if !defined(USE_GCOV) && !defined(WIN32)
672 exit((int)ret);
673 #else
674 return FAIL;
675 #endif
676 }
677 }
678 ret = test.tfun(handle, handle_v1);
679 if (test.test_teardown != NULL) {
680 if (!test.test_teardown(handle, handle_v1)) {
681 fprintf(stderr, "WARNING: Failed to run teardown for test %s\n", test.name);
682 }
683 }
684 destroy_engine(false);
685
686 if (test.cleanup) {
687 test.cleanup(&test, ret);
688 }
689 }
690 #if !defined(USE_GCOV) && !defined(WIN32)
691 exit((int)ret);
692 } else if (pid == (pid_t)-1) {
693 ret = FAIL;
694 } else {
695 int rc;
696 while (alarmed == 0 && waitpid(pid, &rc, 0) == (pid_t)-1) {
697 if (errno != EINTR) {
698 abort();
699 }
700 }
701
702 if (alarmed) {
703 kill(pid, 9);
704 ret = TIMEOUT;
705 } else if (WIFEXITED(rc)) {
706 ret = (enum test_result)WEXITSTATUS(rc);
707 } else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) {
708 ret = CORE;
709 } else {
710 ret = DIED;
711 }
712 }
713 #endif
714 }
715
716 return ret;
717 }
718
setup_alarm_handler()719 static void setup_alarm_handler() {
720 #ifndef WIN32
721 struct sigaction sig_handler;
722
723 sig_handler.sa_handler = alarm_handler;
724 sig_handler.sa_flags = 0;
725
726 sigaction(SIGALRM, &sig_handler, NULL);
727 #endif
728 }
729
set_test_timeout(int timeout)730 static void set_test_timeout(int timeout) {
731 #ifndef WIN32
732 alarm(timeout);
733 #endif
734 }
735
clear_test_timeout()736 static void clear_test_timeout() {
737 #ifndef WIN32
738 alarm(0);
739 alarmed = 0;
740 #endif
741 }
742
main(int argc,char ** argv)743 int main(int argc, char **argv) {
744 int c, exitcode = 0, num_cases = 0, timeout = 0;
745 bool quiet = false;
746 bool dot = false;
747 const char *engine = NULL;
748 const char *engine_args = NULL;
749 const char *test_suite = NULL;
750 const char *test_case = NULL;
751 engine_test_t *testcases = NULL;
752 logger_descriptor = get_null_logger();
753
754 /* Hack to remove the warning from C99 */
755 union {
756 GET_TESTS get_tests;
757 void* voidptr;
758 } my_get_test = {.get_tests = NULL };
759
760 /* Hack to remove the warning from C99 */
761 union {
762 SETUP_SUITE setup_suite;
763 void* voidptr;
764 } my_setup_suite = {.setup_suite = NULL };
765
766 /* Hack to remove the warning from C99 */
767 union {
768 TEARDOWN_SUITE teardown_suite;
769 void* voidptr;
770 } my_teardown_suite = {.teardown_suite = NULL };
771
772
773 /* Use unbuffered stdio */
774 setbuf(stdout, NULL);
775 setbuf(stderr, NULL);
776
777 setup_alarm_handler();
778
779 /* process arguments */
780 while (-1 != (c = getopt(argc, argv,
781 "h" /* usage */
782 "E:" /* Engine to load */
783 "e:" /* Engine options */
784 "T:" /* Library with tests to load */
785 "t:" /* Timeout */
786 "q" /* Be more quiet (only report failures) */
787 "." /* dot mode. */
788 "n:" /* test case to run */
789 ))) {
790 switch (c) {
791 case 'E':
792 engine = optarg;
793 break;
794 case 'e':
795 engine_args = optarg;
796 break;
797 case 'h':
798 usage();
799 return 0;
800 case 'T':
801 test_suite = optarg;
802 break;
803 case 't':
804 timeout = atoi(optarg);
805 break;
806 case 'n':
807 test_case = optarg;
808 break;
809 case 'q':
810 quiet = true;
811 break;
812 case '.':
813 dot = true;
814 break;
815 default:
816 fprintf(stderr, "Illegal argument \"%c\"\n", c);
817 return 1;
818 }
819 }
820
821 //validate args
822 if (engine == NULL) {
823 fprintf(stderr, "You must provide a path to the storage engine library.\n");
824 return 1;
825 }
826
827 if (test_suite == NULL) {
828 fprintf(stderr, "You must provide a path to the testsuite library.\n");
829 return 1;
830 }
831
832 //load test_suite
833 void* handle = dlopen(test_suite, RTLD_NOW | RTLD_LOCAL);
834 if (handle == NULL) {
835 const char *msg = dlerror();
836 fprintf(stderr, "Failed to load testsuite %s: %s\n", test_suite, msg ? msg : "unknown error");
837 return 1;
838 }
839
840 //get the test cases
841 void *symbol = dlsym(handle, "get_tests");
842 if (symbol == NULL) {
843 const char *msg = dlerror();
844 fprintf(stderr, "Could not find get_tests function in testsuite %s: %s\n", test_suite, msg ? msg : "unknown error");
845 return 1;
846 }
847 my_get_test.voidptr = symbol;
848 testcases = (*my_get_test.get_tests)();
849
850 //set up the suite if needed
851 struct test_harness harness = { .default_engine_cfg = engine_args,
852 .engine_path = engine,
853 .reload_engine = reload_engine,
854 .start_engine = start_your_engines,
855 .create_cookie = create_mock_cookie,
856 .destroy_cookie = destroy_mock_cookie,
857 .set_ewouldblock_handling = mock_set_ewouldblock_handling,
858 .lock_cookie = lock_mock_cookie,
859 .unlock_cookie = unlock_mock_cookie,
860 .waitfor_cookie = waitfor_mock_cookie,
861 .time_travel = mock_time_travel,
862 .get_current_testcase = get_current_testcase };
863 symbol = dlsym(handle, "setup_suite");
864 if (symbol != NULL) {
865 my_setup_suite.voidptr = symbol;
866 if (!(*my_setup_suite.setup_suite)(&harness)) {
867 fprintf(stderr, "Failed to set up test suite %s \n", test_suite);
868 return 1;
869 }
870 }
871
872
873 for (num_cases = 0; testcases[num_cases].name; num_cases++) {
874 /* Just counting */
875 }
876
877 if (!quiet) {
878 printf("1..%d\n", num_cases);
879 }
880
881 int i;
882 bool need_newline = false;
883 for (i = 0; testcases[i].name; i++) {
884 if (test_case != NULL && strcmp(test_case, testcases[i].name) != 0)
885 continue;
886 if (!quiet) {
887 printf("Running %s... ", testcases[i].name);
888 fflush(stdout);
889 } else if(dot) {
890 printf(".");
891 need_newline = true;
892 /* Add a newline every few tests */
893 if ((i+1) % 70 == 0) {
894 printf("\n");
895 need_newline = false;
896 }
897 }
898 set_test_timeout(timeout);
899 exitcode += report_test(testcases[i].name,
900 run_test(testcases[i], engine, engine_args),
901 quiet);
902 clear_test_timeout();
903 }
904
905 if (need_newline) {
906 printf("\n");
907 }
908
909 //tear down the suite if needed
910 symbol = dlsym(handle, "teardown_suite");
911 if (symbol != NULL) {
912 my_teardown_suite.voidptr = symbol;
913 if (!(*my_teardown_suite.teardown_suite)()) {
914 fprintf(stderr, "Failed to teardown up test suite %s \n", test_suite);
915 }
916 }
917
918 printf("# Passed %d of %d tests\n", num_cases - exitcode, num_cases);
919
920 return exitcode;
921 }
922