1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2#include "config.h" 3#include <stdio.h> 4#include <stdlib.h> 5#include <strings.h> 6#include <string.h> 7#include <stdint.h> 8#include <ctype.h> 9#include <time.h> 10#include <errno.h> 11#include <platform/platform.h> 12 13#include "genhash.h" 14 15#include "bucket_engine.h" 16 17#include <memcached/engine.h> 18#include "memcached/util.h" 19 20#include "bucket_engine.h" 21 22#ifdef WIN32 23#define BUCKET_ENGINE_PATH "bucket_engine.dll" 24#define ENGINE_PATH "bucket_engine_mock_engine.dll" 25#define DEFAULT_CONFIG "engine=bucket_engine_mock_engine.dll;default=true;admin=admin;auto_create=false" 26#define DEFAULT_CONFIG_NO_DEF "engine=bucket_engine_mock_engine.dll;default=false;admin=admin;auto_create=false" 27#define DEFAULT_CONFIG_AC "engine=bucket_engine_mock_engine.dll;default=true;admin=admin;auto_create=true" 28#else 29#define BUCKET_ENGINE_PATH "bucket_engine.so" 30#define ENGINE_PATH "bucket_engine_mock_engine.so" 31#define DEFAULT_CONFIG "engine=bucket_engine_mock_engine.so;default=true;admin=admin;auto_create=false" 32#define DEFAULT_CONFIG_NO_DEF "engine=bucket_engine_mock_engine.so;default=false;admin=admin;auto_create=false" 33#define DEFAULT_CONFIG_AC "engine=bucket_engine_mock_engine.so;default=true;admin=admin;auto_create=true" 34#endif 35 36#define MOCK_CONFIG_NO_ALLOC "no_alloc" 37 38#define CONN_MAGIC 0xbeefcafe 39 40cb_mutex_t notify_mutex; 41cb_cond_t notify_cond; 42ENGINE_ERROR_CODE notify_code; 43 44/** 45 * Session cas elements 46 */ 47uint64_t session_cas = 0x0102030405060708; 48uint8_t session_ctr = 0; 49cb_mutex_t session_mutex; 50 51static void delay(void) { 52#ifdef WIN32 53 Sleep(1); 54#else 55 usleep(1000); 56#endif 57} 58 59static void notify_io_complete(const void *cookie, ENGINE_ERROR_CODE code) { 60 (void)cookie; 61 cb_mutex_enter(¬ify_mutex); 62 notify_code = code; 63 cb_cond_signal(¬ify_cond); 64 cb_mutex_exit(¬ify_mutex); 65} 66 67protocol_binary_response_status last_status = 0; 68char *last_key = NULL; 69char *last_body = NULL; 70 71genhash_t* stats_hash; 72 73enum test_result { 74 SUCCESS = 11, 75 FAIL = 13, 76 DIED = 14, 77 CORE = 15, 78 PENDING = 19 79}; 80 81struct test { 82 const char *name; 83 enum test_result (*tfun)(ENGINE_HANDLE *, ENGINE_HANDLE_V1 *); 84 const char *cfg; 85}; 86 87struct connstruct { 88 uint64_t magic; 89 const char *uname; 90 const char *config; 91 void *engine_data; 92 bool connected; 93 bool admin; 94 struct connstruct *next; 95}; 96 97struct engine_event_handler { 98 EVENT_CALLBACK cb; 99 const void *cb_data; 100 struct engine_event_handler *next; 101}; 102 103static struct connstruct *connstructs; 104 105static cb_mutex_t connstructs_mutex; 106 107static struct engine_event_handler *engine_event_handlers[MAX_ENGINE_EVENT_TYPE + 1]; 108 109static void perform_callbacks(ENGINE_EVENT_TYPE type, 110 const void *data, 111 const void *cookie) { 112 struct connstruct *c = (struct connstruct*) cookie; 113 struct engine_event_handler *h; 114 for (h = engine_event_handlers[type]; h; h = h->next) { 115 h->cb(c, type, data, h->cb_data); 116 } 117} 118 119static const char* get_server_version(void) { 120 return "bucket mock"; 121} 122 123static void get_auth_data(const void *cookie, auth_data_t *data) { 124 struct connstruct *c = (struct connstruct *)cookie; 125 if (c != NULL) { 126 data->username = c->uname; 127 data->config = c->config; 128 } 129} 130 131static void mock_connect(struct connstruct *c) { 132 cb_mutex_enter(&connstructs_mutex); 133 c->connected = true; 134 cb_mutex_exit(&connstructs_mutex); 135 136 perform_callbacks(ON_CONNECT, NULL, c); 137 if (c->uname) { 138 auth_data_t ad; 139 get_auth_data(c, &ad); 140 perform_callbacks(ON_AUTH, (const void*)&ad, c); 141 } 142} 143 144static void mock_disconnect(struct connstruct *c) { 145 bool old_value; 146 cb_mutex_enter(&connstructs_mutex); 147 if ((old_value = c->connected)) { 148 c->connected = false; 149 } 150 cb_mutex_exit(&connstructs_mutex); 151 if (old_value) { 152 perform_callbacks(ON_DISCONNECT, NULL, c); 153 } 154} 155 156static struct connstruct *mk_conn(const char *user, const char *config) { 157 struct connstruct *rv = calloc(sizeof(struct connstruct), 1); 158 cb_assert(rv); 159 rv->magic = CONN_MAGIC; 160 rv->admin = false; 161 rv->uname = user ? strdup(user) : NULL; 162 rv->config = config ? strdup(config) : NULL; 163 rv->connected = false; 164 cb_mutex_enter(&connstructs_mutex); 165 rv->next = connstructs; 166 connstructs = rv; 167 cb_mutex_exit(&connstructs_mutex); 168 mock_connect(rv); 169 return rv; 170} 171 172static void register_callback(ENGINE_HANDLE *eh, 173 ENGINE_EVENT_TYPE type, 174 EVENT_CALLBACK cb, 175 const void *cb_data) { 176 struct engine_event_handler *h = 177 calloc(sizeof(struct engine_event_handler), 1); 178 cb_assert(h); 179 h->cb = cb; 180 h->cb_data = cb_data; 181 h->next = engine_event_handlers[type]; 182 engine_event_handlers[type] = h; 183 (void)eh; 184} 185 186static void store_engine_specific(const void *cookie, 187 void *engine_data) { 188 if (cookie) { 189 struct connstruct *c = (struct connstruct *)cookie; 190 cb_assert(c->magic == CONN_MAGIC); 191 c->engine_data = engine_data; 192 } 193} 194 195static void *get_engine_specific(const void *cookie) { 196 struct connstruct *c = (struct connstruct *)cookie; 197 cb_assert(c == NULL || c->magic == CONN_MAGIC); 198 return c ? c->engine_data : NULL; 199} 200 201static bool validate_session_cas(const uint64_t cas) { 202 bool ret = true; 203 cb_mutex_enter(&(session_mutex)); 204 if (cas != 0) { 205 if (session_cas != cas) { 206 ret = false; 207 } else { 208 session_ctr++; 209 } 210 } else { 211 session_ctr++; 212 } 213 cb_mutex_exit(&(session_mutex)); 214 return ret; 215} 216 217static void decrement_session_ctr() { 218 cb_mutex_enter(&(session_mutex)); 219 cb_assert(session_ctr != 0); 220 session_ctr--; 221 cb_mutex_exit(&(session_mutex)); 222} 223 224static ENGINE_ERROR_CODE reserve_cookie(const void *cookie) 225{ 226 (void)cookie; 227 return ENGINE_SUCCESS; 228} 229 230static ENGINE_ERROR_CODE release_cookie(const void *cookie) 231{ 232 (void)cookie; 233 return ENGINE_SUCCESS; 234} 235 236static void cookie_set_admin(const void *cookie) { 237 cb_assert(cookie); 238 ((struct connstruct *)cookie)->admin = true; 239} 240 241static bool cookie_is_admin(const void *cookie) { 242 cb_assert(cookie); 243 return ((struct connstruct *)cookie)->admin; 244} 245 246static void *create_stats(void) { 247 /* XXX: Not sure if ``big buffer'' is right in faking this part of 248 the server. */ 249 void *s = calloc(1, 256); 250 cb_assert(s); 251 return s; 252} 253 254static void destroy_stats(void *s) { 255 cb_assert(s); 256 free(s); 257} 258 259static void logger_log(EXTENSION_LOG_LEVEL severity, 260 const void* client_cookie, 261 const char *fmt, ...) 262{ 263 (void)severity; 264 (void)client_cookie; 265 (void)fmt; 266} 267 268static const char *logger_get_name(void) { 269 return "blackhole logger"; 270} 271 272static EXTENSION_LOGGER_DESCRIPTOR blackhole_logger_descriptor; 273 274static void *get_extension(extension_type_t type) { 275 void *ret = NULL; 276 if (type == EXTENSION_LOGGER) { 277 blackhole_logger_descriptor.get_name = logger_get_name; 278 blackhole_logger_descriptor.log = logger_log; 279 ret = &blackhole_logger_descriptor; 280 } 281 return ret; 282} 283 284static rel_time_t get_current_time(void) { 285 return (rel_time_t)time(NULL); 286} 287 288/** 289 * Callback the engines may call to get the public server interface 290 * @param interface the requested interface from the server 291 * @return pointer to a structure containing the interface. The client should 292 * know the layout and perform the proper casts. 293 */ 294static SERVER_HANDLE_V1 *get_server_api(void) 295{ 296 static SERVER_CORE_API core_api; 297 static SERVER_COOKIE_API cookie_api; 298 static SERVER_STAT_API server_stat_api; 299 static SERVER_EXTENSION_API extension_api; 300 static SERVER_CALLBACK_API callback_api; 301 static SERVER_HANDLE_V1 rv; 302 303 core_api.server_version = get_server_version; 304 core_api.get_current_time = get_current_time; 305 core_api.parse_config = parse_config; 306 307 cookie_api.get_auth_data = get_auth_data; 308 cookie_api.store_engine_specific = store_engine_specific; 309 cookie_api.get_engine_specific = get_engine_specific; 310 cookie_api.validate_session_cas = validate_session_cas; 311 cookie_api.decrement_session_ctr = decrement_session_ctr; 312 cookie_api.notify_io_complete = notify_io_complete; 313 cookie_api.reserve = reserve_cookie; 314 cookie_api.release = release_cookie; 315 cookie_api.set_admin = cookie_set_admin; 316 cookie_api.is_admin = cookie_is_admin; 317 318 server_stat_api.new_stats = create_stats; 319 server_stat_api.release_stats = destroy_stats; 320 321 extension_api.get_extension = get_extension; 322 323 callback_api.register_callback = register_callback; 324 callback_api.perform_callbacks = perform_callbacks; 325 326 rv.interface = 1; 327 rv.core = &core_api; 328 rv.stat = &server_stat_api; 329 rv.extension = &extension_api; 330 rv.callback = &callback_api; 331 rv.cookie = &cookie_api; 332 333 return &rv; 334} 335 336static bool add_response(const void *key, uint16_t keylen, 337 const void *ext, uint8_t extlen, 338 const void *body, uint32_t bodylen, 339 uint8_t datatype, uint16_t status, 340 uint64_t cas, const void *cookie) { 341 (void)ext; 342 (void)extlen; 343 (void)datatype; 344 (void)cas; 345 (void)cookie; 346 last_status = status; 347 if (last_body) { 348 free(last_body); 349 last_body = NULL; 350 } 351 if (bodylen > 0) { 352 last_body = malloc(bodylen); 353 cb_assert(last_body); 354 memcpy(last_body, body, bodylen); 355 } 356 if (last_key) { 357 free(last_key); 358 last_key = NULL; 359 } 360 if (keylen > 0) { 361 last_key = malloc(keylen); 362 cb_assert(last_key); 363 memcpy(last_key, key, keylen); 364 } 365 return true; 366} 367 368static ENGINE_HANDLE *load_engine(const char *soname, const char *config_str) { 369 ENGINE_ERROR_CODE error; 370 ENGINE_HANDLE *engine = NULL; 371 /* Hack to remove the warning from C99 */ 372 union my_hack { 373 CREATE_INSTANCE create; 374 void* voidptr; 375 } my_create; 376 void *symbol; 377 char *errmsg; 378 cb_dlhandle_t handle = cb_dlopen(soname, &errmsg); 379 if (handle == NULL) { 380 fprintf(stderr, "Failed to open library \"%s\": %s\n", 381 soname ? soname : "self", errmsg); 382 free(errmsg); 383 return NULL; 384 } 385 386 symbol = cb_dlsym(handle, "create_instance", &errmsg); 387 if (symbol == NULL) { 388 fprintf(stderr, 389 "Could not find symbol \"create_instance\" in %s: %s\n", 390 soname ? soname : "self", errmsg); 391 free(errmsg); 392 return NULL; 393 } 394 my_create.voidptr = symbol; 395 396 /* request a instance with protocol version 1 */ 397 error = (*my_create.create)(1, get_server_api, &engine); 398 399 if (error != ENGINE_SUCCESS || engine == NULL) { 400 fprintf(stderr, "Failed to create instance. Error code: %d\n", error); 401 cb_dlclose(handle); 402 return NULL; 403 } 404 405 if (engine->interface == 1) { 406 ENGINE_HANDLE_V1 *v1 = (ENGINE_HANDLE_V1*)engine; 407 if (v1->initialize(engine, config_str) != ENGINE_SUCCESS) { 408 v1->destroy(engine, false); 409 fprintf(stderr, "Failed to initialize instance. Error code: %d\n", 410 error); 411 cb_dlclose(handle); 412 return NULL; 413 } 414 } else { 415 fprintf(stderr, "Unsupported interface level\n"); 416 cb_dlclose(handle); 417 return NULL; 418 } 419 420 return engine; 421} 422 423/* ---------------------------------------------------------------------- */ 424/* The actual test stuff... */ 425/* ---------------------------------------------------------------------- */ 426 427static bool item_eq(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, 428 const void *c1, item *item1, 429 const void *c2, item *item2) { 430 item_info i1; 431 item_info i2; 432 i1.nvalue = 1; 433 i2.nvalue = 1; 434 435 if (!h1->get_item_info(h, c1, item1, &i1) || 436 !h1->get_item_info(h, c2, item2, &i2)) 437 return false; 438 439 return i1.exptime == i2.exptime 440 && i1.flags == i2.flags 441 && i1.nkey == i2.nkey 442 && i1.nbytes == i2.nbytes 443 && i1.nvalue == i2.nvalue 444 && memcmp(i1.key, i2.key, i1.nkey) == 0 445 && memcmp(i1.value[0].iov_base, i2.value[0].iov_base, 446 i1.nbytes) == 0; 447} 448 449static void assert_item_eq(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, 450 const void *c1, item *i1, 451 const void *c2, item *i2) { 452 cb_assert(item_eq(h, h1, c1, i1, c2, i2)); 453} 454 455/* Convenient storage abstraction */ 456static void store(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, 457 const void *cookie, 458 const char *key, const char *value, 459 item **outitem) { 460 461 item *itm = NULL; 462 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 463 item_info info; 464 info.nvalue = 1; 465 466 rv = h1->allocate(h, cookie, &itm, 467 key, strlen(key), 468 strlen(value), 9258, 3600, 469 PROTOCOL_BINARY_RAW_BYTES); 470 cb_assert(rv == ENGINE_SUCCESS); 471 472 cb_assert(h1->get_item_info(h, cookie, itm, &info)); 473 cb_assert(info.nvalue == 1); 474 cb_assert(info.value[0].iov_base); 475 cb_assert(value); 476 477 memcpy((char*)info.value[0].iov_base, value, strlen(value)); 478 479 rv = h1->store(h, cookie, itm, 0, OPERATION_SET, 0); 480 cb_assert(rv == ENGINE_SUCCESS); 481 482 if (outitem) { 483 *outitem = itm; 484 } 485} 486 487static enum test_result test_default_storage(ENGINE_HANDLE *h, 488 ENGINE_HANDLE_V1 *h1) { 489 item *itm = NULL, *fetched_item; 490 const void *cookie = mk_conn(NULL, NULL); 491 char *key = "somekey"; 492 char *value = "some value"; 493 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 494 item_info info; 495 info.nvalue = 1; 496 497 rv = h1->allocate(h, cookie, &itm, 498 key, strlen(key), 499 strlen(value), 9258, 3600, 500 PROTOCOL_BINARY_RAW_BYTES); 501 cb_assert(rv == ENGINE_SUCCESS); 502 503 cb_assert(h1->get_item_info(h, cookie, itm, &info)); 504 505 memcpy((char*)info.value[0].iov_base, value, strlen(value)); 506 507 rv = h1->store(h, cookie, itm, 0, OPERATION_SET, 0); 508 cb_assert(rv == ENGINE_SUCCESS); 509 510 rv = h1->get(h, cookie, &fetched_item, key, (int)strlen(key), 0); 511 cb_assert(rv == ENGINE_SUCCESS); 512 513 assert_item_eq(h, h1, cookie, itm, cookie, fetched_item); 514 515 /* no effect, but increases coverage. */ 516 h1->reset_stats(h, cookie); 517 518 return SUCCESS; 519} 520 521static enum test_result test_default_storage_key_overrun(ENGINE_HANDLE *h, 522 ENGINE_HANDLE_V1 *h1) { 523 item *itm = NULL, *fetched_item; 524 const void *cookie = mk_conn(NULL, NULL); 525 char *key = "somekeyx"; 526 char *value = "some value"; 527 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 528 item_info info; 529 info.nvalue = 1; 530 531 rv = h1->allocate(h, cookie, &itm, 532 key, strlen(key)-1, 533 strlen(value), 9258, 3600, 534 PROTOCOL_BINARY_RAW_BYTES); 535 cb_assert(rv == ENGINE_SUCCESS); 536 537 h1->get_item_info(h, cookie, itm, &info); 538 539 memcpy((char*)info.value[0].iov_base, value, strlen(value)); 540 541 rv = h1->store(h, cookie, itm, 0, OPERATION_SET, 0); 542 cb_assert(rv == ENGINE_SUCCESS); 543 544 rv = h1->get(h, cookie, &fetched_item, "somekey", (int)strlen("somekey"), 0); 545 cb_assert(rv == ENGINE_SUCCESS); 546 547 assert_item_eq(h, h1, cookie, itm, cookie, fetched_item); 548 549 h1->get_item_info(h, cookie, fetched_item, &info); 550 551 rv = h1->remove(h, cookie, info.key, info.nkey, &info.cas, 0); 552 cb_assert(rv == ENGINE_SUCCESS); 553 554 return SUCCESS; 555} 556 557static enum test_result test_default_unlinked_remove(ENGINE_HANDLE *h, 558 ENGINE_HANDLE_V1 *h1) { 559 item *itm = NULL; 560 const void *cookie = mk_conn(NULL, NULL); 561 char *key = "somekeyx"; 562 const char *value = "the value"; 563 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 564 uint64_t cas = 0; 565 566 rv = h1->allocate(h, cookie, &itm, 567 key, strlen(key)-1, 568 strlen(value), 9258, 3600, 569 PROTOCOL_BINARY_RAW_BYTES); 570 cb_assert(rv == ENGINE_SUCCESS); 571 rv = h1->remove(h, cookie, key, strlen(key), &cas, 0); 572 cb_assert(rv == ENGINE_KEY_ENOENT); 573 574 return SUCCESS; 575} 576 577static enum test_result test_two_engines_no_autocreate(ENGINE_HANDLE *h, 578 ENGINE_HANDLE_V1 *h1) { 579 item *itm = NULL, *fetched_item; 580 const void *cookie = mk_conn("autouser", NULL); 581 char *key = "somekey"; 582 char *value = "some value"; 583 uint64_t cas_out = 0, result = 0; 584 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 585 uint64_t cas = 0; 586 587 rv = h1->allocate(h, cookie, &itm, 588 key, strlen(key), 589 strlen(value), 9258, 3600, 590 PROTOCOL_BINARY_RAW_BYTES); 591 cb_assert(rv == ENGINE_DISCONNECT); 592 593 rv = h1->store(h, cookie, itm, 0, OPERATION_SET, 0); 594 cb_assert(rv == ENGINE_DISCONNECT); 595 596 rv = h1->get(h, cookie, &fetched_item, key, (int)strlen(key), 0); 597 cb_assert(rv == ENGINE_DISCONNECT); 598 599 rv = h1->remove(h, cookie, key, strlen(key), &cas, 0); 600 cb_assert(rv == ENGINE_DISCONNECT); 601 602 rv = h1->arithmetic(h, cookie, key, (int)strlen(key), 603 true, true, 1, 1, 0, &cas_out, PROTOCOL_BINARY_RAW_BYTES, 604 &result, 0); 605 cb_assert(rv == ENGINE_DISCONNECT); 606 607 /* no effect, but increases coverage. */ 608 h1->reset_stats(h, cookie); 609 610 return SUCCESS; 611} 612 613static enum test_result test_no_default_storage(ENGINE_HANDLE *h, 614 ENGINE_HANDLE_V1 *h1) { 615 item *itm = NULL, *fetched_item; 616 const void *cookie = mk_conn(NULL, NULL); 617 char *key = "somekey"; 618 char *value = "some value"; 619 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 620 621 rv = h1->allocate(h, cookie, &itm, 622 key, strlen(key), 623 strlen(value), 9258, 3600, 624 PROTOCOL_BINARY_RAW_BYTES); 625 cb_assert(rv == ENGINE_DISCONNECT); 626 627 rv = h1->get(h, cookie, &fetched_item, key, (int)strlen(key), 0); 628 cb_assert(rv == ENGINE_DISCONNECT); 629 630 return SUCCESS; 631} 632 633static enum test_result test_two_engines(ENGINE_HANDLE *h, 634 ENGINE_HANDLE_V1 *h1) { 635 item *item1, *item2, *fetched_item1 = NULL, *fetched_item2 = NULL; 636 const void *cookie1 = mk_conn("user1", NULL), *cookie2 = mk_conn("user2", NULL); 637 char *key = "somekey"; 638 char *value1 = "some value1", *value2 = "some value 2"; 639 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 640 641 store(h, h1, cookie1, key, value1, &item1); 642 store(h, h1, cookie2, key, value2, &item2); 643 644 rv = h1->get(h, cookie1, &fetched_item1, key, (int)strlen(key), 0); 645 cb_assert(rv == ENGINE_SUCCESS); 646 rv = h1->get(h, cookie2, &fetched_item2, key, (int)strlen(key), 0); 647 cb_assert(rv == ENGINE_SUCCESS); 648 649 cb_assert(!item_eq(h, h1, cookie1, fetched_item1, cookie2, fetched_item2)); 650 assert_item_eq(h, h1, cookie1, item1, cookie1, fetched_item1); 651 assert_item_eq(h, h1, cookie2, item2, cookie2, fetched_item2); 652 653 return SUCCESS; 654} 655 656static enum test_result test_two_engines_del(ENGINE_HANDLE *h, 657 ENGINE_HANDLE_V1 *h1) { 658 item *item1, *item2, *fetched_item1 = NULL, *fetched_item2 = NULL; 659 const void *cookie1 = mk_conn("user1", NULL), *cookie2 = mk_conn("user2", NULL); 660 char *key = "somekey"; 661 char *value1 = "some value1", *value2 = "some value 2"; 662 ENGINE_ERROR_CODE rv; 663 uint64_t cas = 0; 664 665 store(h, h1, cookie1, key, value1, &item1); 666 store(h, h1, cookie2, key, value2, &item2); 667 668 /* Delete an item */ 669 rv = h1->remove(h, cookie1, key, strlen(key), &cas, 0); 670 cb_assert(rv == ENGINE_SUCCESS); 671 672 rv = h1->get(h, cookie1, &fetched_item1, key, (int)strlen(key), 0); 673 cb_assert(rv == ENGINE_KEY_ENOENT); 674 cb_assert(fetched_item1 == NULL); 675 rv = h1->get(h, cookie2, &fetched_item2, key, (int)strlen(key), 0); 676 cb_assert(rv == ENGINE_SUCCESS); 677 678 assert_item_eq(h, h1, cookie1, item2, cookie2, fetched_item2); 679 680 return SUCCESS; 681} 682 683static enum test_result test_two_engines_flush(ENGINE_HANDLE *h, 684 ENGINE_HANDLE_V1 *h1) { 685 item *item1, *item2, *fetched_item1 = NULL, *fetched_item2 = NULL; 686 const void *cookie1 = mk_conn("user1", NULL), *cookie2 = mk_conn("user2", NULL); 687 char *key = "somekey"; 688 char *value1 = "some value1", *value2 = "some value 2"; 689 ENGINE_ERROR_CODE rv; 690 691 store(h, h1, cookie1, key, value1, &item1); 692 store(h, h1, cookie2, key, value2, &item2); 693 694 /* flush it */ 695 rv = h1->flush(h, cookie1, 0); 696 cb_assert(rv == ENGINE_SUCCESS); 697 698 rv = h1->get(h, cookie1, &fetched_item1, key, (int)strlen(key), 0); 699 cb_assert(rv == ENGINE_KEY_ENOENT); 700 cb_assert(fetched_item1 == NULL); 701 rv = h1->get(h, cookie2, &fetched_item2, key, (int)strlen(key), 0); 702 cb_assert(rv == ENGINE_SUCCESS); 703 704 assert_item_eq(h, h1, cookie2, item2, cookie2, fetched_item2); 705 706 return SUCCESS; 707} 708 709static enum test_result test_arith(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) { 710 const void *cookie1 = mk_conn("user1", NULL), *cookie2 = mk_conn("user2", NULL); 711 char *key = "somekey"; 712 uint64_t result = 0, cas = 0; 713 ENGINE_ERROR_CODE rv; 714 715 /* Initialize the first one. */ 716 rv = h1->arithmetic(h, cookie1, key, (int)strlen(key), 717 true, true, 1, 1, 0, &cas, PROTOCOL_BINARY_RAW_BYTES, 718 &result, 0); 719 cb_assert(rv == ENGINE_SUCCESS); 720 cb_assert(cas == 0); 721 cb_assert(result == 1); 722 723 /* Fail an init of the second one. */ 724 rv = h1->arithmetic(h, cookie2, key, (int)strlen(key), 725 true, false, 1, 1, 0, &cas, PROTOCOL_BINARY_RAW_BYTES, 726 &result, 0); 727 cb_assert(rv == ENGINE_KEY_ENOENT); 728 729 /* Update the first again. */ 730 rv = h1->arithmetic(h, cookie1, key, (int)strlen(key), 731 true, true, 1, 1, 0, &cas, PROTOCOL_BINARY_RAW_BYTES, 732 &result, 0); 733 cb_assert(rv == ENGINE_SUCCESS); 734 cb_assert(cas == 0); 735 cb_assert(result == 2); 736 737 return SUCCESS; 738} 739 740static enum test_result test_get_info(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) { 741 const engine_info* info = h1->get_info(h); 742 return strncmp(info->description, "Bucket engine", 13) == 0 ? SUCCESS : FAIL; 743} 744 745static void* create_packet4(uint8_t opcode, const char *key, const char *val, 746 size_t vlen, uint64_t cas) { 747 void *pkt_raw = calloc(1, 748 sizeof(protocol_binary_request_header) 749 + strlen(key) 750 + vlen); 751 protocol_binary_request_header *req = 752 (protocol_binary_request_header*)pkt_raw; 753 cb_assert(pkt_raw); 754 req->request.opcode = opcode; 755 req->request.bodylen = htonl((uint32_t)(strlen(key) + vlen)); 756 req->request.keylen = htons((uint16_t)strlen(key)); 757 req->request.cas = htonll(cas); 758 memcpy((char*)pkt_raw + sizeof(protocol_binary_request_header), 759 key, strlen(key)); 760 memcpy((char*)pkt_raw + sizeof(protocol_binary_request_header) + strlen(key), 761 val, vlen); 762 return pkt_raw; 763} 764 765static void* create_packet(uint8_t opcode, const char *key, const char *val) { 766 return create_packet4(opcode, key, val, strlen(val), 0); 767} 768 769static void* create_create_bucket_pkt(const char *user, const char *path, 770 const char *args) { 771 char buf[1024]; 772 snprintf(buf, sizeof(buf), "%s%c%s", path, 0, args); 773 return create_packet4(PROTOCOL_BINARY_CMD_CREATE_BUCKET, user, 774 buf, strlen(path) + strlen(args) + 1, 0); 775} 776 777static void* create_create_bucket_pkt_with_cas(const char *user, const char *path, 778 const char *args, uint64_t cas) { 779 char buf[1024]; 780 snprintf(buf, sizeof(buf), "%s%c%s", path, 0, args); 781 return create_packet4(PROTOCOL_BINARY_CMD_CREATE_BUCKET, user, 782 buf, strlen(path) + strlen(args) + 1, cas); 783} 784 785static enum test_result test_create_bucket(ENGINE_HANDLE *h, 786 ENGINE_HANDLE_V1 *h1) { 787 const void *adm_cookie = mk_conn("admin", NULL); 788 const char *key = "somekey"; 789 const char *value = "the value"; 790 item *itm; 791 void *pkt; 792 ENGINE_ERROR_CODE rv; 793 794 rv = h1->allocate(h, mk_conn("someuser", NULL), &itm, 795 key, strlen(key), 796 strlen(value), 9258, 3600, 797 PROTOCOL_BINARY_RAW_BYTES); 798 cb_assert(rv == ENGINE_DISCONNECT); 799 800 pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 801 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 802 free(pkt); 803 cb_assert(rv == ENGINE_SUCCESS); 804 cb_assert(last_status == 0); 805 806 rv = h1->allocate(h, mk_conn("someuser", NULL), &itm, 807 key, strlen(key), 808 strlen(value), 9258, 3600, 809 PROTOCOL_BINARY_RAW_BYTES); 810 cb_assert(rv == ENGINE_SUCCESS); 811 812 return SUCCESS; 813} 814 815static enum test_result test_double_create_bucket(ENGINE_HANDLE *h, 816 ENGINE_HANDLE_V1 *h1) { 817 const void *adm_cookie = mk_conn("admin", NULL); 818 ENGINE_ERROR_CODE rv; 819 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 820 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 821 free(pkt); 822 cb_assert(rv == ENGINE_SUCCESS); 823 cb_assert(last_status == 0); 824 825 pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 826 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 827 free(pkt); 828 cb_assert(rv == ENGINE_SUCCESS); 829 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS); 830 831 return SUCCESS; 832} 833 834static enum test_result test_create_bucket_with_params(ENGINE_HANDLE *h, 835 ENGINE_HANDLE_V1 *h1) { 836 const void *adm_cookie = mk_conn("admin", NULL), *other_cookie = mk_conn("someuser", NULL); 837 const char *key = "somekey"; 838 const char *value = "the value"; 839 item *itm; 840 ENGINE_ERROR_CODE rv; 841 void *pkt; 842 843 rv = h1->allocate(h, adm_cookie, &itm, 844 key, strlen(key), 845 strlen(value), 9258, 3600, 846 PROTOCOL_BINARY_RAW_BYTES); 847 cb_assert(rv == ENGINE_DISCONNECT); 848 849 pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, "no_alloc"); 850 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 851 free(pkt); 852 cb_assert(rv == ENGINE_SUCCESS); 853 cb_assert(last_status == 0); 854 855 rv = h1->allocate(h, other_cookie, &itm, 856 key, strlen(key), 857 strlen(value), 9258, 3600, 858 PROTOCOL_BINARY_RAW_BYTES); 859 cb_assert(rv == ENGINE_DISCONNECT); 860 861 return SUCCESS; 862} 863 864static enum test_result test_create_bucket_with_cas(ENGINE_HANDLE *h, 865 ENGINE_HANDLE_V1 *h1) { 866 const void *adm_cookie = mk_conn("admin", NULL); 867 const char *key = "somekey"; 868 const char *value = "the value"; 869 item *itm; 870 void *pkt; 871 ENGINE_ERROR_CODE rv; 872 873 rv = h1->allocate(h, mk_conn("someuser", NULL), &itm, 874 key, strlen(key), 875 strlen(value), 9258, 3600, 876 PROTOCOL_BINARY_RAW_BYTES); 877 cb_assert(rv == ENGINE_DISCONNECT); 878 879 pkt = create_create_bucket_pkt_with_cas("someuser", ENGINE_PATH, "", 880 0x0111111111111111); 881 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 882 free(pkt); 883 cb_assert(rv == ENGINE_KEY_EEXISTS); 884 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS); 885 886 pkt = create_create_bucket_pkt_with_cas("someuser", ENGINE_PATH, "", 887 0x0102030405060708); 888 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 889 free(pkt); 890 cb_assert(rv == ENGINE_SUCCESS); 891 cb_assert(last_status == 0); 892 893 rv = h1->allocate(h, mk_conn("someuser", NULL), &itm, 894 key, strlen(key), 895 strlen(value), 9258, 3600, 896 PROTOCOL_BINARY_RAW_BYTES); 897 cb_assert(rv == ENGINE_SUCCESS); 898 899 return SUCCESS; 900} 901static enum test_result test_admin_user(ENGINE_HANDLE *h, 902 ENGINE_HANDLE_V1 *h1) { 903 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 904 905 /* Test with no user. */ 906 void *pkt = create_create_bucket_pkt("newbucket", ENGINE_PATH, ""); 907 rv = h1->unknown_command(h, mk_conn(NULL, NULL), pkt, add_response); 908 free(pkt); 909 cb_assert(rv == ENGINE_ENOTSUP); 910 911 /* Test with non-admin */ 912 pkt = create_create_bucket_pkt("newbucket", ENGINE_PATH, ""); 913 rv = h1->unknown_command(h, mk_conn("notadmin", NULL), pkt, add_response); 914 free(pkt); 915 cb_assert(rv == ENGINE_ENOTSUP); 916 917 /* Test with admin */ 918 pkt = create_create_bucket_pkt("newbucket", ENGINE_PATH, ""); 919 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 920 free(pkt); 921 cb_assert(rv == ENGINE_SUCCESS); 922 cb_assert(last_status == 0); 923 924 return SUCCESS; 925} 926 927static enum test_result do_test_delete_bucket(ENGINE_HANDLE *h, 928 ENGINE_HANDLE_V1 *h1, 929 bool delete_on_same_connection) { 930 const void *adm_cookie = mk_conn("admin", NULL); 931 const char *key = "somekey"; 932 const char *value = "the value"; 933 item *itm; 934 ENGINE_ERROR_CODE rv; 935 const void *other_cookie; 936 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 937 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 938 free(pkt); 939 cb_assert(rv == ENGINE_SUCCESS); 940 cb_assert(last_status == 0); 941 942 if (delete_on_same_connection) { 943 pkt = create_packet(PROTOCOL_BINARY_CMD_SELECT_BUCKET, "someuser", ""); 944 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 945 free(pkt); 946 cb_assert(rv == ENGINE_SUCCESS); 947 cb_assert(last_status == 0); 948 } 949 950 other_cookie = mk_conn("someuser", NULL); 951 rv = h1->allocate(h, other_cookie, &itm, 952 key, strlen(key), 953 strlen(value), 9258, 3600, 954 PROTOCOL_BINARY_RAW_BYTES); 955 cb_assert(rv == ENGINE_SUCCESS); 956 957 pkt = create_packet(PROTOCOL_BINARY_CMD_DELETE_BUCKET, "someuser", "force=false"); 958 cb_mutex_enter(¬ify_mutex); 959 notify_code = ENGINE_FAILED; 960 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 961 cb_assert(rv == ENGINE_EWOULDBLOCK); 962 cb_cond_wait(¬ify_cond, ¬ify_mutex); 963 cb_assert(notify_code == ENGINE_SUCCESS); 964 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 965 free(pkt); 966 cb_assert(rv == ENGINE_SUCCESS); 967 968 pkt = create_packet(PROTOCOL_BINARY_CMD_DELETE_BUCKET, "someuser", "force=false"); 969 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 970 free(pkt); 971 cb_assert(rv == ENGINE_SUCCESS); 972 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT); 973 974 rv = h1->allocate(h, other_cookie, &itm, 975 key, strlen(key), 976 strlen(value), 9258, 3600, 977 PROTOCOL_BINARY_RAW_BYTES); 978 cb_assert(rv == ENGINE_DISCONNECT); 979 980 return SUCCESS; 981} 982 983static enum test_result test_delete_bucket(ENGINE_HANDLE *h, 984 ENGINE_HANDLE_V1 *h1) { 985 return do_test_delete_bucket(h, h1, false); 986} 987 988static enum test_result test_delete_bucket_sameconnection(ENGINE_HANDLE *h, 989 ENGINE_HANDLE_V1 *h1) { 990 return do_test_delete_bucket(h, h1, true); 991} 992 993struct handle_pair { 994 ENGINE_HANDLE *h; 995 ENGINE_HANDLE_V1 *h1; 996}; 997 998static void conc_del_bucket_thread(void *arg) { 999 struct handle_pair *hp = arg; 1000 1001 bool have_connection = false; 1002 void *cokie = NULL; 1003 while (true) { 1004 static const char *key = "somekey"; 1005 static size_t klen = 7; 1006 static size_t vlen = 9; 1007 item *itm; 1008 ENGINE_ERROR_CODE rv; 1009 1010 cokie = have_connection ? cokie : mk_conn("someuser", NULL); 1011 have_connection = true; 1012 1013 rv = hp->h1->allocate(hp->h, cokie, &itm, 1014 key, klen, 1015 vlen, 9258, 3600, 1016 PROTOCOL_BINARY_RAW_BYTES); 1017 if (rv == ENGINE_DISCONNECT) { 1018 break; 1019 } 1020 1021 cb_assert(rv == ENGINE_SUCCESS); 1022 1023 hp->h1->release(hp->h, cokie, itm); 1024 1025 if (rand() % 3 == 0) { 1026 have_connection = false; 1027 mock_disconnect(cokie); 1028 } 1029 } 1030 mock_disconnect(cokie); 1031} 1032 1033static enum test_result test_release(ENGINE_HANDLE *h, 1034 ENGINE_HANDLE_V1 *h1) { 1035 void *cokie = mk_conn("someuser", NULL); 1036 item *itm; 1037 const void *key = "release_me"; 1038 const size_t klen = strlen(key); 1039 const size_t vlen = 81985; 1040 ENGINE_ERROR_CODE rv = h1->allocate(h, cokie, &itm, 1041 key, klen, 1042 vlen, 9258, 3600, 1043 PROTOCOL_BINARY_RAW_BYTES); 1044 cb_assert(rv == ENGINE_SUCCESS); 1045 h1->release(h, cokie, itm); 1046 1047 return SUCCESS; 1048} 1049 1050static int getenv_int_with_default(const char *env_var, int default_value) { 1051 char *val = getenv(env_var); 1052 char *ptr; 1053 long lrv; 1054 1055 if (!val) { 1056 return default_value; 1057 } 1058 lrv = (int)strtol(val, &ptr, 10); 1059 if (*val && !*ptr) { 1060 int rv = (int)lrv; 1061 if ((long)lrv == lrv) { 1062 return rv; 1063 } 1064 } 1065 return default_value; 1066} 1067 1068static enum test_result do_test_delete_bucket_concurrent(ENGINE_HANDLE *h, 1069 ENGINE_HANDLE_V1 *h1, 1070 bool keep_one_refcount) 1071{ 1072 struct bucket_engine *bucket_engine = (struct bucket_engine *)h; 1073 const void *adm_cookie = mk_conn("admin", NULL); 1074 proxied_engine_handle_t *peh; 1075 int n_threads; 1076 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1077 struct handle_pair hp; 1078 int i; 1079 void *other_cookie = mk_conn("someuser", NULL); 1080 item *itm; 1081 const char* key = "testkey"; 1082 const char* value = "testvalue"; 1083 cb_thread_t *threads; 1084 1085 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 1086 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1087 free(pkt); 1088 cb_assert(rv == ENGINE_SUCCESS); 1089 cb_assert(last_status == 0); 1090 1091 peh = genhash_find(bucket_engine->engines, "someuser", strlen("someuser")); 1092 cb_assert(peh); 1093 1094 cb_assert(peh->refcount == 1); 1095 if (keep_one_refcount) { 1096 peh->refcount++; 1097 } 1098 1099 n_threads = getenv_int_with_default("PROTOCOL_BINARY_CMD_DELETE_BUCKET_CONCURRENT_THREADS", 17); 1100 if (n_threads < 1) { 1101 n_threads = 1; 1102 } 1103 threads = calloc(n_threads, sizeof(*threads)); 1104 cb_assert(threads != NULL); 1105 hp.h = h; 1106 hp.h1 = h1; 1107 1108 for (i = 0; i < n_threads; i++) { 1109 int r = cb_create_thread(&threads[i], conc_del_bucket_thread, &hp, 0); 1110 cb_assert(r == 0); 1111 } 1112 1113 delay(); 1114 1115 pkt = create_packet(PROTOCOL_BINARY_CMD_DELETE_BUCKET, "someuser", "force=false"); 1116 1117 cb_mutex_enter(¬ify_mutex); 1118 notify_code = ENGINE_FAILED; 1119 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1120 cb_assert(rv == ENGINE_EWOULDBLOCK); 1121 cb_cond_wait(¬ify_cond, ¬ify_mutex); 1122 cb_assert(notify_code == ENGINE_SUCCESS); 1123 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1124 free(pkt); 1125 cb_assert(rv == ENGINE_SUCCESS); 1126 1127 rv = h1->allocate(h, other_cookie, &itm, 1128 key, strlen(key), 1129 strlen(value), 9258, 3600, 1130 PROTOCOL_BINARY_RAW_BYTES); 1131 cb_assert(rv == ENGINE_DISCONNECT); 1132 mock_disconnect(other_cookie); 1133 for (i = 0; i < n_threads; i++) { 1134 int r = cb_join_thread(threads[i]); 1135 cb_assert(r == 0); 1136 } 1137 1138 if (keep_one_refcount) { 1139 cb_assert(peh->refcount == 1); 1140 cb_assert(peh->state == STATE_NULL); 1141 cb_assert(bucket_engine->shutdown.bucket_counter == 1); 1142 } 1143 1144 cb_mutex_enter(&bucket_engine->shutdown.mutex); 1145 if (keep_one_refcount) { 1146 cb_assert(peh->refcount == 1); 1147 peh->refcount = 0; 1148 cb_assert(bucket_engine->shutdown.bucket_counter == 1); 1149 cb_cond_broadcast(&bucket_engine->shutdown.refcount_cond); 1150 } 1151 /* we cannot use shutdown.cond because it'll only be signalled 1152 * when in_progress is set, but we don't want to set in_progress 1153 * to avoid aborting normal "refcount drops to 0" loop. */ 1154 while (bucket_engine->shutdown.bucket_counter == 1) { 1155 cb_mutex_exit(&bucket_engine->shutdown.mutex); 1156 delay(); 1157 cb_mutex_enter(&bucket_engine->shutdown.mutex); 1158 } 1159 cb_assert(bucket_engine->shutdown.bucket_counter == 0); 1160 cb_mutex_exit(&bucket_engine->shutdown.mutex); 1161 1162 cb_mutex_initialize(¬ify_mutex); 1163 free(threads); 1164 1165 return SUCCESS; 1166} 1167 1168 1169static enum test_result test_delete_bucket_concurrent(ENGINE_HANDLE *h, 1170 ENGINE_HANDLE_V1 *h1) { 1171 return do_test_delete_bucket_concurrent(h, h1, true); 1172} 1173 1174static enum test_result test_delete_bucket_concurrent_multi(ENGINE_HANDLE *h, 1175 ENGINE_HANDLE_V1 *h1) { 1176 enum test_result rv = SUCCESS; 1177 int i = getenv_int_with_default("PROTOCOL_BINARY_CMD_DELETE_BUCKET_CONCURRENT_ITERATIONS", 100); 1178 if (i < 1) { 1179 i = 1; 1180 } 1181 while (--i >= 0) { 1182 rv = do_test_delete_bucket_concurrent(h, h1, i & 1); 1183 if (rv != SUCCESS) { 1184 break; 1185 } 1186 } 1187 return rv; 1188} 1189 1190static enum test_result test_delete_bucket_shutdown_race(ENGINE_HANDLE *h, 1191 ENGINE_HANDLE_V1 *h1) 1192{ 1193 const void *adm_cookie = mk_conn("admin", NULL); 1194 ENGINE_ERROR_CODE rv; 1195 const void *cookie1; 1196 item *item1; 1197 const char *key = "somekey"; 1198 const char *value1 = "some value1"; 1199 void *pkt; 1200 1201 pkt = create_create_bucket_pkt("mybucket", ENGINE_PATH, ""); 1202 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1203 free(pkt); 1204 cb_assert(rv == ENGINE_SUCCESS); 1205 cb_assert(last_status == 0); 1206 1207 cookie1 = mk_conn("mybucket", NULL); 1208 store(h, h1, cookie1, key, value1, &item1); 1209 1210 pkt = create_packet(PROTOCOL_BINARY_CMD_DELETE_BUCKET, "mybucket", "force=false"); 1211 cb_mutex_enter(¬ify_mutex); 1212 notify_code = ENGINE_FAILED; 1213 1214 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1215 cb_assert(rv == ENGINE_EWOULDBLOCK); 1216 cb_cond_wait(¬ify_cond, ¬ify_mutex); 1217 cb_assert(notify_code == ENGINE_SUCCESS); 1218 1219 /* we've got one ref-count open for the bucket, so we should have */ 1220 /* a deadlock if we try to shut down the bucket now... */ 1221 /* There is actually a bug in bucket_engine that allows us to */ 1222 /* call destroy twice without any side effects (it doesn't free */ 1223 /* the memory allocated for the engine handle).. */ 1224 /* We do however need to clear out the connstructs list */ 1225 /* to avoid having on_disconnect handling to be sent */ 1226 /* to the free'd engine (it is called by the framework before */ 1227 /* it runs destroy, but we've performed the destroy) */ 1228 h1->destroy(h, false); 1229 connstructs = NULL; 1230 1231 return SUCCESS; 1232} 1233 1234static enum test_result test_bucket_name_validation(ENGINE_HANDLE *h, 1235 ENGINE_HANDLE_V1 *h1) { 1236 1237 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1238 void *pkt = create_create_bucket_pkt("bucket one", ENGINE_PATH, ""); 1239 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1240 free(pkt); 1241 cb_assert(rv == ENGINE_SUCCESS); 1242 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_NOT_STORED); 1243 1244 pkt = create_create_bucket_pkt("", ENGINE_PATH, ""); 1245 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1246 free(pkt); 1247 cb_assert(rv == ENGINE_SUCCESS); 1248 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_NOT_STORED); 1249 1250 return SUCCESS; 1251} 1252 1253static enum test_result test_list_buckets_none(ENGINE_HANDLE *h, 1254 ENGINE_HANDLE_V1 *h1) { 1255 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1256 /* Go find all the buckets. */ 1257 void *pkt = create_packet(PROTOCOL_BINARY_CMD_LIST_BUCKETS, "", ""); 1258 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1259 free(pkt); 1260 cb_assert(rv == ENGINE_SUCCESS); 1261 cb_assert(last_status == 0); 1262 1263 /* Now verify the body looks alright. */ 1264 cb_assert(last_body == NULL); 1265 1266 return SUCCESS; 1267} 1268 1269static enum test_result test_list_buckets_one(ENGINE_HANDLE *h, 1270 ENGINE_HANDLE_V1 *h1) { 1271 1272 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1273 1274 /* Create a bucket first. */ 1275 void *pkt = create_create_bucket_pkt("bucket1", ENGINE_PATH, ""); 1276 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1277 free(pkt); 1278 cb_assert(rv == ENGINE_SUCCESS); 1279 cb_assert(last_status == 0); 1280 1281 /* Now go find all the buckets. */ 1282 pkt = create_packet(PROTOCOL_BINARY_CMD_LIST_BUCKETS, "", ""); 1283 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1284 free(pkt); 1285 cb_assert(rv == ENGINE_SUCCESS); 1286 cb_assert(last_status == 0); 1287 1288 /* Now verify the body looks alright. */ 1289 cb_assert(strncmp(last_body, "bucket1", 7) == 0); 1290 1291 return SUCCESS; 1292} 1293 1294static enum test_result test_list_buckets_two(ENGINE_HANDLE *h, 1295 ENGINE_HANDLE_V1 *h1) { 1296 const void *cookie = mk_conn("admin", NULL); 1297 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1298 /* Create two buckets first. */ 1299 void *pkt = create_create_bucket_pkt("bucket1", ENGINE_PATH, ""); 1300 rv = h1->unknown_command(h, cookie, pkt, add_response); 1301 free(pkt); 1302 cb_assert(rv == ENGINE_SUCCESS); 1303 cb_assert(last_status == 0); 1304 1305 pkt = create_create_bucket_pkt("bucket2", ENGINE_PATH, ""); 1306 rv = h1->unknown_command(h, cookie, pkt, add_response); 1307 free(pkt); 1308 cb_assert(rv == ENGINE_SUCCESS); 1309 cb_assert(last_status == 0); 1310 1311 /* Now go find all the buckets. */ 1312 pkt = create_packet(PROTOCOL_BINARY_CMD_LIST_BUCKETS, "", ""); 1313 rv = h1->unknown_command(h, cookie, pkt, add_response); 1314 free(pkt); 1315 cb_assert(rv == ENGINE_SUCCESS); 1316 cb_assert(last_status == 0); 1317 1318 /* Now verify the body looks alright. */ 1319 cb_assert(memcmp(last_body, "bucket1 bucket2", 15) == 0 1320 || memcmp(last_body, "bucket2 bucket1", 15) == 0); 1321 1322 return SUCCESS; 1323} 1324 1325static enum test_result test_unknown_call(ENGINE_HANDLE *h, 1326 ENGINE_HANDLE_V1 *h1) { 1327 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1328 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 1329 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1330 free(pkt); 1331 cb_assert(rv == ENGINE_SUCCESS); 1332 cb_assert(last_status == 0); 1333 1334 pkt = create_packet(0xfe, "somekey", "someval"); 1335 rv = h1->unknown_command(h, mk_conn("someuser", NULL), pkt, add_response); 1336 free(pkt); 1337 cb_assert(rv == ENGINE_ENOTSUP); 1338 1339 return SUCCESS; 1340} 1341 1342static enum test_result test_select_no_admin(ENGINE_HANDLE *h, 1343 ENGINE_HANDLE_V1 *h1) { 1344 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1345 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 1346 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1347 free(pkt); 1348 cb_assert(rv == ENGINE_SUCCESS); 1349 cb_assert(last_status == 0); 1350 1351 pkt = create_packet(PROTOCOL_BINARY_CMD_SELECT_BUCKET, "stuff", ""); 1352 rv = h1->unknown_command(h, mk_conn("notadmin", NULL), pkt, add_response); 1353 free(pkt); 1354 cb_assert(rv == ENGINE_ENOTSUP); 1355 1356 return SUCCESS; 1357} 1358 1359static enum test_result test_select_no_bucket(ENGINE_HANDLE *h, 1360 ENGINE_HANDLE_V1 *h1) { 1361 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1362 void *pkt = create_packet(PROTOCOL_BINARY_CMD_SELECT_BUCKET, "stuff", ""); 1363 rv = h1->unknown_command(h, mk_conn("admin", NULL), pkt, add_response); 1364 free(pkt); 1365 cb_assert(rv == ENGINE_SUCCESS); 1366 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT); 1367 1368 return SUCCESS; 1369} 1370 1371static enum test_result test_select(ENGINE_HANDLE *h, 1372 ENGINE_HANDLE_V1 *h1) { 1373 item *item1, *fetched_item1 = NULL, *fetched_item2; 1374 const void *cookie1 = mk_conn("user1", NULL), *admin = mk_conn("admin", NULL); 1375 char *key = "somekey"; 1376 char *value1 = "some value1"; 1377 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1378 void *pkt; 1379 1380 store(h, h1, cookie1, key, value1, &item1); 1381 rv = h1->get(h, cookie1, &fetched_item1, key, (int)strlen(key), 0); 1382 cb_assert(rv == ENGINE_SUCCESS); 1383 rv = h1->get(h, admin, &fetched_item2, key, (int)strlen(key), 0); 1384 cb_assert(rv == ENGINE_KEY_ENOENT); 1385 1386 assert_item_eq(h, h1, cookie1, item1, cookie1, fetched_item1); 1387 1388 pkt = create_packet(PROTOCOL_BINARY_CMD_SELECT_BUCKET, "user1", ""); 1389 rv = h1->unknown_command(h, admin, pkt, add_response); 1390 free(pkt); 1391 cb_assert(rv == ENGINE_SUCCESS); 1392 cb_assert(last_status == 0); 1393 1394 rv = h1->get(h, admin, &fetched_item2, key, (int)strlen(key), 0); 1395 cb_assert(rv == ENGINE_SUCCESS); 1396 assert_item_eq(h, h1, cookie1, item1, admin, fetched_item2); 1397 1398 return SUCCESS; 1399} 1400 1401static void add_stats(const char *key, const uint16_t klen, 1402 const char *val, const uint32_t vlen, 1403 const void *cookie) { 1404 (void)cookie; 1405 genhash_update(stats_hash, key, klen, val, vlen); 1406} 1407 1408static enum test_result test_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) { 1409 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1410 1411 rv = h1->get_stats(h, mk_conn("user", NULL), NULL, 0, add_stats); 1412 cb_assert(rv == ENGINE_SUCCESS); 1413 cb_assert(genhash_size(stats_hash) == 2); 1414 1415 cb_assert(memcmp("0", 1416 genhash_find(stats_hash, "bucket_conns", strlen("bucket_conns")), 1417 1) == 0); 1418 cb_assert(genhash_find(stats_hash, "bucket_active_conns", 1419 strlen("bucket_active_conns")) != NULL); 1420 1421 return SUCCESS; 1422} 1423 1424static enum test_result test_stats_bucket(ENGINE_HANDLE *h, 1425 ENGINE_HANDLE_V1 *h1) { 1426 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1427 const void *adm_cookie = mk_conn("admin", NULL); 1428 1429 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 1430 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1431 free(pkt); 1432 cb_assert(rv == ENGINE_SUCCESS); 1433 cb_assert(last_status == 0); 1434 1435 rv = h1->get_stats(h, mk_conn("user", NULL), "bucket", 6, add_stats); 1436 cb_assert(rv == ENGINE_FAILED); 1437 cb_assert(genhash_size(stats_hash) == 0); 1438 1439 rv = h1->get_stats(h, adm_cookie, "bucket", 6, add_stats); 1440 cb_assert(rv == ENGINE_SUCCESS); 1441 cb_assert(genhash_size(stats_hash) == 1); 1442 1443 cb_assert(NULL == genhash_find(stats_hash, "bucket_conns", strlen("bucket_conns"))); 1444 1445 cb_assert(memcmp("running", 1446 genhash_find(stats_hash, "someuser", strlen("someuser")), 1447 7) == 0); 1448 1449 return SUCCESS; 1450} 1451 1452static enum test_result test_unknown_call_no_bucket(ENGINE_HANDLE *h, 1453 ENGINE_HANDLE_V1 *h1) { 1454 1455 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1456 1457 void *pkt = create_packet(0xfe, "somekey", "someval"); 1458 rv = h1->unknown_command(h, mk_conn("someuser", NULL), pkt, add_response); 1459 free(pkt); 1460 cb_assert(rv == ENGINE_DISCONNECT); 1461 1462 return SUCCESS; 1463} 1464 1465static enum test_result test_auto_config(ENGINE_HANDLE *h, 1466 ENGINE_HANDLE_V1 *h1) { 1467 item *itm = NULL; 1468 const void *cookie = mk_conn("someuser", MOCK_CONFIG_NO_ALLOC); 1469 char *key = "somekey"; 1470 char *value = "some value"; 1471 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1472 1473 rv = h1->allocate(h, cookie, &itm, 1474 key, strlen(key), 1475 strlen(value), 9258, 3600, 1476 PROTOCOL_BINARY_RAW_BYTES); 1477 cb_assert(rv == ENGINE_ENOMEM); 1478 1479 return SUCCESS; 1480} 1481 1482static enum test_result test_get_tap_iterator(ENGINE_HANDLE *h, 1483 ENGINE_HANDLE_V1 *h1) { 1484 /* This is run for its side effect of not crashing. */ 1485 const void *cookie = mk_conn(NULL, NULL); 1486 tap_event_t e; 1487 TAP_ITERATOR ti = h1->get_tap_iterator(h, cookie, 1488 NULL, 0, 0, NULL, 0); 1489 cb_assert(ti != NULL); 1490 do { 1491 item *it; 1492 void *engine_specific; 1493 uint16_t nengine_specific; 1494 uint8_t ttl; 1495 uint16_t flags; 1496 uint32_t seqno; 1497 uint16_t vbucket; 1498 e = ti(h, cookie, &it, &engine_specific, &nengine_specific, &ttl, 1499 &flags, &seqno, &vbucket); 1500 } while (e != TAP_DISCONNECT); 1501 1502 mock_disconnect((void*)cookie); 1503 1504 return SUCCESS; 1505} 1506 1507static enum test_result test_tap_notify(ENGINE_HANDLE *h, 1508 ENGINE_HANDLE_V1 *h1) { 1509 ENGINE_ERROR_CODE ec = h1->tap_notify(h, mk_conn("someuser", ""), 1510 NULL, 0, 0, 0, TAP_MUTATION, 0, 1511 "akey", 4, 1512 0, 0, 0, PROTOCOL_BINARY_RAW_BYTES, 1513 "aval", 4, 0); 1514 cb_assert(ec == ENGINE_SUCCESS); 1515 return SUCCESS; 1516} 1517 1518#define MAX_CONNECTIONS_IN_POOL 1000 1519struct { 1520 cb_mutex_t mutex; 1521 int connected; 1522 int pend_close; 1523 TAP_ITERATOR iter; 1524 struct connstruct *conn; 1525} connection_pool[MAX_CONNECTIONS_IN_POOL]; 1526 1527static void init_connection_pool(void) { 1528 int ii; 1529 for (ii = 0; ii < MAX_CONNECTIONS_IN_POOL; ++ii) { 1530 connection_pool[ii].conn = mk_conn(NULL, NULL); 1531 mock_disconnect(connection_pool[ii].conn); 1532 connection_pool[ii].connected = 0; 1533 connection_pool[ii].pend_close = 0; 1534 connection_pool[ii].iter = NULL; 1535 cb_mutex_initialize(&connection_pool[ii].mutex); 1536 } 1537} 1538 1539static void cleanup_connection_pool(ENGINE_HANDLE *h) { 1540 int ii; 1541 for (ii = 0; ii < MAX_CONNECTIONS_IN_POOL; ++ii) { 1542 if (connection_pool[ii].pend_close) { 1543 tap_event_t e = connection_pool[ii].iter(h, 1544 connection_pool[ii].conn, 1545 NULL, NULL, NULL, 1546 NULL, NULL, NULL, NULL); 1547 cb_assert(e == TAP_DISCONNECT); 1548 } 1549 mock_disconnect(connection_pool[ii].conn); 1550 cb_mutex_destroy(&connection_pool[ii].mutex); 1551 } 1552} 1553 1554static void network_io_thread(void *arg) { 1555 int num_ops = 500000; 1556 ENGINE_HANDLE *h = arg; 1557 ENGINE_HANDLE_V1 *h1 = arg; 1558 int ii; 1559 for (ii = 0; ii < num_ops; ++ii) { 1560 long idx = (rand() & 0xffff) % 1000; 1561 cb_mutex_enter(&connection_pool[idx].mutex); 1562 if (!connection_pool[idx].pend_close) { 1563 if (!connection_pool[idx].connected) { 1564 mock_connect(connection_pool[idx].conn); 1565 connection_pool[idx].connected = 1; 1566 if (h != NULL) { 1567 /* run tap connect */ 1568 TAP_ITERATOR ti; 1569 ti = h1->get_tap_iterator(h, 1570 connection_pool[idx].conn, 1571 NULL, 0, 0, NULL, 0); 1572 cb_assert(ti != NULL); 1573 connection_pool[idx].iter = ti; 1574 connection_pool[idx].pend_close = 1; 1575 } 1576 } else { 1577 mock_disconnect(connection_pool[idx].conn); 1578 connection_pool[idx].connected = 0; 1579 } 1580 } else { 1581 tap_event_t e = connection_pool[idx].iter(h, 1582 connection_pool[idx].conn, 1583 NULL, NULL, NULL, 1584 NULL, NULL, NULL, NULL); 1585 cb_assert(e == TAP_DISCONNECT); 1586 connection_pool[idx].pend_close = 0; 1587 } 1588 cb_mutex_exit(&connection_pool[idx].mutex); 1589 } 1590} 1591 1592static enum test_result test_concurrent_connect_disconnect(ENGINE_HANDLE *h, 1593 ENGINE_HANDLE_V1 *h1) { 1594#define num_workers 10 1595 cb_thread_t workers[num_workers]; 1596 int i; 1597 1598 (void)h1; 1599 init_connection_pool(); 1600 for (i = 0; i < num_workers; i++) { 1601 int rc = cb_create_thread(&workers[i], network_io_thread, NULL, 0); 1602 cb_assert(rc == 0); 1603 } 1604 1605 for (i = 0; i < num_workers; i++) { 1606 int rc = cb_join_thread(workers[i]); 1607 cb_assert(rc == 0); 1608 } 1609 1610#undef num_workers 1611 cleanup_connection_pool(h); 1612 return SUCCESS; 1613} 1614 1615static enum test_result test_concurrent_connect_disconnect_tap(ENGINE_HANDLE *h, 1616 ENGINE_HANDLE_V1 *h1) { 1617#define num_workers 40 1618 cb_thread_t workers[num_workers]; 1619 int i; 1620 (void)h1; 1621 init_connection_pool(); 1622 for (i = 0; i < num_workers; i++) { 1623 int rc = cb_create_thread(&workers[i], network_io_thread, h, 0); 1624 cb_assert(rc == 0); 1625 } 1626 1627 for (i = 0; i < num_workers; i++) { 1628 int rc = cb_join_thread(workers[i]); 1629 cb_assert(rc == 0); 1630 } 1631#undef num_workers 1632 1633 return SUCCESS; 1634} 1635 1636static enum test_result test_topkeys(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) { 1637 ENGINE_ERROR_CODE rv = ENGINE_SUCCESS; 1638 const void *adm_cookie = mk_conn("admin", NULL); 1639 int cmd; 1640 char *val; 1641 void *pkt = create_create_bucket_pkt("someuser", ENGINE_PATH, ""); 1642 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1643 cb_assert(rv == ENGINE_SUCCESS); 1644 free(pkt); 1645 1646 1647 pkt = create_packet(PROTOCOL_BINARY_CMD_GET_REPLICA, "somekey", "someval"); 1648 rv = h1->unknown_command(h, adm_cookie, pkt, add_response); 1649 cb_assert(rv == ENGINE_SUCCESS); 1650 free(pkt); 1651 1652 for (cmd = 0x90; cmd < 0xff; ++cmd) { 1653 pkt = create_packet(cmd, "somekey", "someval"); 1654 h1->unknown_command(h, adm_cookie, pkt, add_response); 1655 free(pkt); 1656 } 1657 1658 rv = h1->get_stats(h, adm_cookie, "topkeys", 7, add_stats); 1659 cb_assert(rv == ENGINE_SUCCESS); 1660 cb_assert(genhash_size(stats_hash) == 1); 1661 val = genhash_find(stats_hash, "somekey", strlen("somekey")); 1662 cb_assert(val != NULL); 1663 cb_assert(strstr(val, "get_replica=1,evict=1,getl=1,unlock=1,get_meta=2,set_meta=2,del_meta=2") != NULL); 1664 return SUCCESS; 1665} 1666 1667static ENGINE_HANDLE_V1 *start_your_engines(const char *cfg) { 1668 ENGINE_HANDLE_V1 *h = (ENGINE_HANDLE_V1 *)load_engine(BUCKET_ENGINE_PATH, cfg); 1669 cb_assert(h); 1670 return h; 1671} 1672 1673static int report_test(enum test_result r) { 1674 int rc = 0; 1675 char *msg = NULL; 1676 bool color_enabled = getenv("TESTAPP_ENABLE_COLOR") != NULL; 1677 int color = 0; 1678 char color_str[8] = { 0 }; 1679 char *reset_color = "\033[m"; 1680 switch(r) { 1681 case SUCCESS: 1682 msg="OK"; 1683 color = 32; 1684 break; 1685 case FAIL: 1686 color = 31; 1687 msg="FAIL"; 1688 rc = 1; 1689 break; 1690 case DIED: 1691 color = 31; 1692 msg = "DIED"; 1693 rc = 1; 1694 break; 1695 case CORE: 1696 color = 31; 1697 msg = "CORE DUMPED"; 1698 rc = 1; 1699 break; 1700 case PENDING: 1701 color = 33; 1702 msg = "PENDING"; 1703 break; 1704 default: 1705 color = 31; 1706 msg = "UNKNOWN"; 1707 } 1708 if (color_enabled) { 1709 snprintf(color_str, sizeof(color_str), "\033[%dm", color); 1710 } 1711 printf("%s%s%s\n", color_str, msg, color_enabled ? reset_color : ""); 1712 return rc; 1713} 1714 1715static void disconnect_all_connections(struct connstruct *c) { 1716 while (c) { 1717 struct connstruct *next = c->next; 1718 mock_disconnect(c); 1719 free((void*)c->uname); 1720 free((void*)c->config); 1721 free(c); 1722 c = next; 1723 } 1724} 1725 1726static void destroy_event_handlers_rec(struct engine_event_handler *h) { 1727 if (h) { 1728 destroy_event_handlers_rec(h->next); 1729 free(h); 1730 } 1731} 1732 1733static void destroy_event_handlers(void) { 1734 int i = 0; 1735 for (i = 0; i < MAX_ENGINE_EVENT_TYPE; i++) { 1736 destroy_event_handlers_rec(engine_event_handlers[i]); 1737 engine_event_handlers[i] = NULL; 1738 } 1739} 1740 1741static int hash_key_eq(const void *key, size_t nk, 1742 const void *other, size_t no) { 1743 return nk == no && (memcmp(key, other, nk) == 0); 1744} 1745 1746static void* hash_strdup(const void *x, size_t n) { 1747 char *rv = calloc(n + 1, sizeof(char)); 1748 cb_assert(rv); 1749 return memcpy(rv, x, n); 1750} 1751 1752static int execute_test(struct test test) { 1753 enum test_result ret = PENDING; 1754 1755 if (test.tfun != NULL) { 1756 ENGINE_HANDLE_V1 *h; 1757 struct hash_ops stats_hash_ops; 1758 1759 /* Initialize the stats collection thingy */ 1760 stats_hash_ops.hashfunc = genhash_string_hash; 1761 stats_hash_ops.hasheq = hash_key_eq; 1762 stats_hash_ops.dupKey = hash_strdup; 1763 stats_hash_ops.dupValue = hash_strdup; 1764 stats_hash_ops.freeKey = free; 1765 stats_hash_ops.freeValue = free; 1766 1767 last_status = 0xff; 1768 stats_hash = genhash_init(25, stats_hash_ops); 1769 1770 /* Start the engines and go */ 1771 h = start_your_engines(test.cfg ? test.cfg : DEFAULT_CONFIG); 1772 ret = test.tfun((ENGINE_HANDLE*)h, h); 1773 /* we expect all threads to be dead so no need to guard 1774 * concurrent connstructs access anymore */ 1775 disconnect_all_connections(connstructs); 1776 destroy_event_handlers(); 1777 connstructs = NULL; 1778 h->destroy((ENGINE_HANDLE*)h, false); 1779 genhash_free(stats_hash); 1780 } 1781 1782 return (int)ret; 1783} 1784 1785struct warmer_arg { 1786 union { 1787 ENGINE_HANDLE *h; 1788 ENGINE_HANDLE_V1 *h1; 1789 } handles; 1790 int tid; 1791}; 1792 1793static void bench_warmer(void *arg) { 1794 struct warmer_arg *wa = arg; 1795 char key[32]; 1796 const void *cookie = mk_conn("bench", NULL); 1797 int i; 1798 1799 snprintf(key, sizeof(key), "k%d", wa->tid); 1800 for (i = 0; i < 10000000; i++) { 1801 item *itm = NULL; 1802 store(wa->handles.h, wa->handles.h1, cookie, key, "v", &itm); 1803 cb_assert(itm); 1804 } 1805} 1806 1807static void runBench(void) { 1808 ENGINE_HANDLE_V1 *h1 = start_your_engines(DEFAULT_CONFIG); 1809 ENGINE_HANDLE *h = (ENGINE_HANDLE*)h1; 1810 const void *adm_cookie = mk_conn("admin", NULL); 1811 void *pkt = create_create_bucket_pkt("bench", ENGINE_PATH, ""); 1812 ENGINE_ERROR_CODE rv = h1->unknown_command(h, adm_cookie, pkt, 1813 add_response); 1814#define NUM_WORKERS 4 1815 cb_thread_t workers[NUM_WORKERS]; 1816 struct warmer_arg args[NUM_WORKERS]; 1817 int i; 1818 int rc; 1819 1820 free(pkt); 1821 cb_assert(rv == ENGINE_SUCCESS); 1822 cb_assert(last_status == 0); 1823 1824 for (i = 0; i < NUM_WORKERS; i++) { 1825 args[i].handles.h = h; 1826 args[i].tid = i; 1827 rc = cb_create_thread(&workers[i], bench_warmer, &args[i], 0); 1828 cb_assert(rc == 0); 1829 } 1830 1831 for (i = 0; i < NUM_WORKERS; i++) { 1832 rc = cb_join_thread(workers[i]); 1833 cb_assert(rc == 0); 1834 } 1835#undef NUM_WORKERS 1836} 1837 1838int main(int argc, char **argv) { 1839 int i; 1840 int rc; 1841 int maxtests; 1842 int errors = 0; 1843 1844 struct test tests[] = { 1845 {"get info", test_get_info, NULL}, 1846 {"default storage", test_default_storage, NULL}, 1847 {"default storage key overrun", test_default_storage_key_overrun, NULL}, 1848 {"default unlinked remove", test_default_unlinked_remove, NULL}, 1849 {"no default storage", 1850 test_no_default_storage, 1851#ifdef WIN32 1852 "engine=bucket_engine_mock_engine.dll;default=false" 1853#else 1854 "engine=bucket_engine_mock_engine.so;default=false" 1855#endif 1856 }, 1857 {"user storage with no default", 1858 test_two_engines, 1859#ifdef WIN32 1860 "engine=bucket_engine_mock_engine.dll;default=false" 1861#else 1862 "engine=bucket_engine_mock_engine.so;default=false" 1863#endif 1864 }, 1865 {"distinct storage", test_two_engines, DEFAULT_CONFIG_AC}, 1866 {"distinct storage (no auto-create)", test_two_engines_no_autocreate, 1867 DEFAULT_CONFIG_NO_DEF}, 1868 {"delete from one of two nodes", test_two_engines_del, 1869 DEFAULT_CONFIG_AC}, 1870 {"flush from one of two nodes", test_two_engines_flush, 1871 DEFAULT_CONFIG_AC}, 1872 {"isolated arithmetic", test_arith, DEFAULT_CONFIG_AC}, 1873 {"create bucket", test_create_bucket, DEFAULT_CONFIG_NO_DEF}, 1874 {"double create bucket", test_double_create_bucket, 1875 DEFAULT_CONFIG_NO_DEF}, 1876 {"create bucket with params", test_create_bucket_with_params, 1877 DEFAULT_CONFIG_NO_DEF}, 1878 {"create bucket with cas", test_create_bucket_with_cas, 1879 DEFAULT_CONFIG_NO_DEF}, 1880 {"bucket name verification", test_bucket_name_validation, NULL}, 1881 {"delete bucket", test_delete_bucket, 1882 DEFAULT_CONFIG_NO_DEF}, 1883 {"delete bucket (same connection)", test_delete_bucket_sameconnection, 1884 DEFAULT_CONFIG_NO_DEF}, 1885 {"concurrent access delete bucket", test_delete_bucket_concurrent, 1886 DEFAULT_CONFIG_NO_DEF}, 1887 {"concurrent access delete bucket multiple times", test_delete_bucket_concurrent_multi, 1888 DEFAULT_CONFIG_NO_DEF}, 1889 {"delete bucket shutdwn race", test_delete_bucket_shutdown_race, 1890 DEFAULT_CONFIG_NO_DEF}, 1891 {"list buckets with none", test_list_buckets_none, NULL}, 1892 {"list buckets with one", test_list_buckets_one, NULL}, 1893 {"list buckets", test_list_buckets_two, NULL}, 1894 {"fail to select a bucket when not admin", test_select_no_admin, NULL}, 1895 {"select a bucket as admin", test_select, DEFAULT_CONFIG_AC}, 1896 {"fail to select non-existent bucket as admin", 1897 test_select_no_bucket, NULL}, 1898 {"stats call", test_stats, NULL}, 1899 {"stats bucket call", test_stats_bucket, NULL}, 1900 {"release call", test_release, NULL}, 1901 {"unknown call delegation", test_unknown_call, NULL}, 1902 {"unknown call delegation (no bucket)", test_unknown_call_no_bucket, 1903 DEFAULT_CONFIG_NO_DEF}, 1904 {"admin verification", test_admin_user, NULL}, 1905 {"auto create with config", test_auto_config, 1906 DEFAULT_CONFIG_AC}, 1907 {"get tap iterator", test_get_tap_iterator, NULL}, 1908 {"tap notify", test_tap_notify, NULL}, 1909 {"concurrent connect/disconnect", 1910 test_concurrent_connect_disconnect, NULL }, 1911 {"concurrent connect/disconnect (tap)", 1912 test_concurrent_connect_disconnect_tap, NULL }, 1913 {"topkeys", test_topkeys, NULL }, 1914 {NULL, NULL, NULL} 1915 }; 1916 1917 cb_mutex_initialize(&connstructs_mutex); 1918 cb_mutex_initialize(¬ify_mutex); 1919 cb_mutex_initialize(&session_mutex); 1920 cb_cond_initialize(¬ify_cond); 1921 1922 putenv("MEMCACHED_TOP_KEYS=10"); 1923 for (maxtests = 0; tests[maxtests].name; maxtests++) { 1924 } 1925 if (argc == 2) { 1926 /* Run a certain test */ 1927 int testno = atoi(argv[1]); 1928 if (testno >= maxtests) { 1929 fprintf(stderr, "Invalid test specified\n"); 1930 exit(EXIT_FAILURE); 1931 } 1932 1933 exit(execute_test(tests[testno])); 1934 } else { 1935 /* iterate through all of the tests */ 1936 for (i = 0; tests[i].name; i++) { 1937 char cmd[1024]; 1938 enum test_result ret; 1939 1940 fprintf(stdout, "Running %s... ", tests[i].name); 1941 fflush(stdout); 1942 snprintf(cmd, sizeof(cmd), "%s %d", argv[0], i); 1943 1944 rc = system(cmd); 1945#ifdef WIN32 1946 ret = (enum test_result)rc; 1947#else 1948 if (WIFEXITED(rc)) { 1949 ret = (enum test_result)WEXITSTATUS(rc); 1950#ifdef WCOREDUMP 1951 } else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) { 1952 ret = CORE; 1953#endif 1954 } else { 1955 ret = DIED; 1956 } 1957#endif 1958 errors += report_test(ret); 1959 } 1960 } 1961 1962 if (getenv("BUCKET_ENGINE_BENCH") != NULL) { 1963 runBench(); 1964 } 1965 1966 if (errors == 0) { 1967 fprintf(stdout, "All tests pass\n"); 1968 return EXIT_SUCCESS; 1969 } else { 1970 fprintf(stderr, "One or more tests failed\n"); 1971 return EXIT_FAILURE; 1972 } 1973} 1974