MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
engine_testapp.c
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 {
20  ENGINE_HANDLE_V1 *the_engine;
21  TAP_ITERATOR iterator;
22 };
23 
24 #ifndef WIN32
25 static sig_atomic_t alarmed;
26 
27 static void alarm_handler(int sig) {
28  alarmed = 1;
29 }
30 #endif
31 
32 static inline struct mock_engine* get_handle(ENGINE_HANDLE* handle) {
33  return (struct mock_engine*)handle;
34 }
35 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
313 static ENGINE_ERROR_CODE mock_unknown_command(ENGINE_HANDLE* handle,
314  const void* cookie,
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 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)
647  pid_t pid = fork();
648  if (pid == 0) {
649 #endif
650  /* Start the engines and go */
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);
655  return FAIL;
656  }
657  }
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);
662  }
663  }
664  destroy_engine(false);
665 #if !defined(USE_GCOV) && !defined(WIN32)
666  exit((int)ret);
667  } else if (pid == (pid_t)-1) {
668  ret = FAIL;
669  } else {
670  int rc;
671  while (alarmed == 0 && waitpid(pid, &rc, 0) == (pid_t)-1) {
672  if (errno != EINTR) {
673  abort();
674  }
675  }
676 
677  if (alarmed) {
678  kill(pid, 9);
679  ret = TIMEOUT;
680  } else if (WIFEXITED(rc)) {
681  ret = (enum test_result)WEXITSTATUS(rc);
682  } else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) {
683  ret = CORE;
684  } else {
685  ret = DIED;
686  }
687  }
688 #endif
689  }
690 
691  return ret;
692 }
693 
694 static void setup_alarm_handler() {
695 #ifndef WIN32
696  struct sigaction sig_handler;
697 
698  sig_handler.sa_handler = alarm_handler;
699  sig_handler.sa_flags = 0;
700 
701  sigaction(SIGALRM, &sig_handler, NULL);
702 #endif
703 }
704 
705 static void set_test_timeout(int timeout) {
706 #ifndef WIN32
707  alarm(timeout);
708 #endif
709 }
710 
711 static void clear_test_timeout() {
712 #ifndef WIN32
713  alarm(0);
714  alarmed = 0;
715 #endif
716 }
717 
718 int main(int argc, char **argv) {
719  int c, exitcode = 0, num_cases = 0, timeout = 0;
720  bool quiet = false;
721  bool dot = false;
722  const char *engine = NULL;
723  const char *engine_args = NULL;
724  const char *test_suite = NULL;
725  const char *test_case = NULL;
726  engine_test_t *testcases = NULL;
727  logger_descriptor = get_null_logger();
728 
729  /* Hack to remove the warning from C99 */
730  union {
731  GET_TESTS get_tests;
732  void* voidptr;
733  } my_get_test = {.get_tests = NULL };
734 
735  /* Hack to remove the warning from C99 */
736  union {
737  SETUP_SUITE setup_suite;
738  void* voidptr;
739  } my_setup_suite = {.setup_suite = NULL };
740 
741  /* Hack to remove the warning from C99 */
742  union {
743  TEARDOWN_SUITE teardown_suite;
744  void* voidptr;
745  } my_teardown_suite = {.teardown_suite = NULL };
746 
747 
748  /* Use unbuffered stdio */
749  setbuf(stdout, NULL);
750  setbuf(stderr, NULL);
751 
752  setup_alarm_handler();
753 
754  /* process arguments */
755  while (-1 != (c = getopt(argc, argv,
756  "h" /* usage */
757  "E:" /* Engine to load */
758  "e:" /* Engine options */
759  "T:" /* Library with tests to load */
760  "t:" /* Timeout */
761  "q" /* Be more quiet (only report failures) */
762  "." /* dot mode. */
763  "n:" /* test case to run */
764  ))) {
765  switch (c) {
766  case 'E':
767  engine = optarg;
768  break;
769  case 'e':
770  engine_args = optarg;
771  break;
772  case 'h':
773  usage();
774  return 0;
775  case 'T':
776  test_suite = optarg;
777  break;
778  case 't':
779  timeout = atoi(optarg);
780  break;
781  case 'n':
782  test_case = optarg;
783  break;
784  case 'q':
785  quiet = true;
786  break;
787  case '.':
788  dot = true;
789  break;
790  default:
791  fprintf(stderr, "Illegal argument \"%c\"\n", c);
792  return 1;
793  }
794  }
795 
796  //validate args
797  if (engine == NULL) {
798  fprintf(stderr, "You must provide a path to the storage engine library.\n");
799  return 1;
800  }
801 
802  if (test_suite == NULL) {
803  fprintf(stderr, "You must provide a path to the testsuite library.\n");
804  return 1;
805  }
806 
807  //load test_suite
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");
812  return 1;
813  }
814 
815  //get the test cases
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");
820  return 1;
821  }
822  my_get_test.voidptr = symbol;
823  testcases = (*my_get_test.get_tests)();
824 
825  //set up the suite if needed
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);
842  return 1;
843  }
844  }
845 
846 
847  for (num_cases = 0; testcases[num_cases].name; num_cases++) {
848  /* Just counting */
849  }
850 
851  if (!quiet) {
852  printf("1..%d\n", num_cases);
853  }
854 
855  int i;
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)
859  continue;
860  if (!quiet) {
861  printf("Running %s... ", testcases[i].name);
862  fflush(stdout);
863  } else if(dot) {
864  printf(".");
865  need_newline = true;
866  /* Add a newline every few tests */
867  if ((i+1) % 70 == 0) {
868  printf("\n");
869  need_newline = false;
870  }
871  }
872  set_test_timeout(timeout);
873  exitcode += report_test(testcases[i].name,
874  run_test(testcases[i], engine, engine_args),
875  quiet);
876  clear_test_timeout();
877  }
878 
879  if (need_newline) {
880  printf("\n");
881  }
882 
883  //tear down the suite if needed
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);
889  }
890  }
891 
892  printf("# Passed %d of %d tests\n", num_cases - exitcode, num_cases);
893 
894  return exitcode;
895 }