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(&notify_mutex);
62    notify_code = code;
63    cb_cond_signal(&notify_cond);
64    cb_mutex_exit(&notify_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(&notify_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(&notify_cond, &notify_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(&notify_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(&notify_cond, &notify_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(&notify_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(&notify_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(&notify_cond, &notify_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(&notify_mutex);
1919    cb_mutex_initialize(&session_mutex);
1920    cb_cond_initialize(&notify_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