1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3#include <assert.h>
4#include <errno.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10#include <signal.h>
11#include <getopt.h>
12#include <time.h>
13#include <platform/platform.h>
14#include "utilities/engine_loader.h"
15#include <memcached/engine_testapp.h>
16#include <memcached/extension_loggers.h>
17#include "mock_server.h"
18
19struct mock_engine {
20    ENGINE_HANDLE_V1 me;
21    ENGINE_HANDLE_V1 *the_engine;
22    TAP_ITERATOR iterator;
23};
24
25static bool color_enabled;
26
27#ifndef WIN32
28static sig_atomic_t alarmed;
29
30static void alarm_handler(int sig) {
31    alarmed = 1;
32}
33#endif
34
35static struct mock_engine* get_handle(ENGINE_HANDLE* handle) {
36    return (struct mock_engine*)handle;
37}
38
39static tap_event_t mock_tap_iterator(ENGINE_HANDLE* handle,
40                                     const void *cookie, item **itm,
41                                     void **es, uint16_t *nes, uint8_t *ttl,
42                                     uint16_t *flags, uint32_t *seqno,
43                                     uint16_t *vbucket) {
44   struct mock_engine *me = get_handle(handle);
45   return me->iterator((ENGINE_HANDLE*)me->the_engine, cookie, itm, es, nes,
46                       ttl, flags, seqno, vbucket);
47}
48
49static const engine_info* mock_get_info(ENGINE_HANDLE* handle) {
50    struct mock_engine *me = get_handle(handle);
51    return me->the_engine->get_info((ENGINE_HANDLE*)me->the_engine);
52}
53
54static ENGINE_ERROR_CODE mock_initialize(ENGINE_HANDLE* handle,
55                                         const char* config_str) {
56    struct mock_engine *me = get_handle(handle);
57    return me->the_engine->initialize((ENGINE_HANDLE*)me->the_engine, config_str);
58}
59
60static void mock_destroy(ENGINE_HANDLE* handle, const bool force) {
61    struct mock_engine *me = get_handle(handle);
62    me->the_engine->destroy((ENGINE_HANDLE*)me->the_engine, force);
63}
64
65static ENGINE_ERROR_CODE mock_allocate(ENGINE_HANDLE* handle,
66                                       const void* cookie,
67                                       item **item,
68                                       const void* key,
69                                       const size_t nkey,
70                                       const size_t nbytes,
71                                       const int flags,
72                                       const rel_time_t exptime,
73                                       uint8_t datatype) {
74    struct mock_engine *me = get_handle(handle);
75    struct mock_connstruct *c = (void*)cookie;
76    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
77
78    if (c == NULL) {
79        c = (void*)create_mock_cookie();
80    }
81
82    c->nblocks = 0;
83    cb_mutex_enter(&c->mutex);
84    while (ret == ENGINE_SUCCESS &&
85           (ret = me->the_engine->allocate((ENGINE_HANDLE*)me->the_engine, c,
86                                           item, key, nkey,
87                                           nbytes, flags,
88                                           exptime, datatype)) == ENGINE_EWOULDBLOCK &&
89           c->handle_ewouldblock)
90    {
91        ++c->nblocks;
92        cb_cond_wait(&c->cond, &c->mutex);
93        ret = c->status;
94    }
95    cb_mutex_exit(&c->mutex);
96
97    if (c != cookie) {
98        destroy_mock_cookie(c);
99    }
100
101    return ret;
102}
103
104static ENGINE_ERROR_CODE mock_remove(ENGINE_HANDLE* handle,
105                                     const void* cookie,
106                                     const void* key,
107                                     const size_t nkey,
108                                     uint64_t* cas,
109                                     uint16_t vbucket)
110{
111    struct mock_engine *me = get_handle(handle);
112    struct mock_connstruct *c = (void*)cookie;
113    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
114
115    if (c == NULL) {
116        c = (void*)create_mock_cookie();
117    }
118
119    c->nblocks = 0;
120    cb_mutex_enter(&c->mutex);
121    while (ret == ENGINE_SUCCESS &&
122           (ret = me->the_engine->remove((ENGINE_HANDLE*)me->the_engine, c, key,
123                                         nkey, cas, vbucket)) == ENGINE_EWOULDBLOCK &&
124           c->handle_ewouldblock)
125    {
126        ++c->nblocks;
127        cb_cond_wait(&c->cond, &c->mutex);
128        ret = c->status;
129    }
130    cb_mutex_exit(&c->mutex);
131
132    if (c != cookie) {
133        destroy_mock_cookie(c);
134    }
135
136    return ret;
137}
138
139static void mock_release(ENGINE_HANDLE* handle,
140                         const void *cookie,
141                         item* item) {
142    struct mock_engine *me = get_handle(handle);
143    me->the_engine->release((ENGINE_HANDLE*)me->the_engine, cookie, item);
144}
145
146static ENGINE_ERROR_CODE mock_get(ENGINE_HANDLE* handle,
147                                  const void* cookie,
148                                  item** item,
149                                  const void* key,
150                                  const int nkey,
151                                  uint16_t vbucket) {
152    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
153    struct mock_engine *me = get_handle(handle);
154    struct mock_connstruct *c = (void*)cookie;
155    if (c == NULL) {
156        c = (void*)create_mock_cookie();
157    }
158
159    c->nblocks = 0;
160    cb_mutex_enter(&c->mutex);
161    while (ret == ENGINE_SUCCESS &&
162           (ret = me->the_engine->get((ENGINE_HANDLE*)me->the_engine, c, item,
163                                      key, nkey, vbucket)) == ENGINE_EWOULDBLOCK &&
164           c->handle_ewouldblock)
165    {
166        ++c->nblocks;
167        cb_cond_wait(&c->cond, &c->mutex);
168        ret = c->status;
169    }
170    cb_mutex_exit(&c->mutex);
171
172    if (c != cookie) {
173        destroy_mock_cookie(c);
174    }
175
176    return ret;
177}
178
179static ENGINE_ERROR_CODE mock_get_stats(ENGINE_HANDLE* handle,
180                                        const void* cookie,
181                                        const char* stat_key,
182                                        int nkey,
183                                        ADD_STAT add_stat)
184{
185    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
186    struct mock_engine *me = get_handle(handle);
187    struct mock_connstruct *c = (void*)cookie;
188    if (c == NULL) {
189        c = (void*)create_mock_cookie();
190    }
191
192    c->nblocks = 0;
193    cb_mutex_enter(&c->mutex);
194    while (ret == ENGINE_SUCCESS &&
195           (ret = me->the_engine->get_stats((ENGINE_HANDLE*)me->the_engine, c, stat_key,
196                                            nkey, add_stat)) == ENGINE_EWOULDBLOCK &&
197           c->handle_ewouldblock)
198    {
199        ++c->nblocks;
200        cb_cond_wait(&c->cond, &c->mutex);
201        ret = c->status;
202    }
203    cb_mutex_exit(&c->mutex);
204
205    if (c != cookie) {
206        destroy_mock_cookie(c);
207    }
208
209    return ret;
210}
211
212static ENGINE_ERROR_CODE mock_store(ENGINE_HANDLE* handle,
213                                    const void *cookie,
214                                    item* item,
215                                    uint64_t *cas,
216                                    ENGINE_STORE_OPERATION operation,
217                                    uint16_t vbucket) {
218    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
219    struct mock_engine *me = get_handle(handle);
220    struct mock_connstruct *c = (void*)cookie;
221    if (c == NULL) {
222        c = (void*)create_mock_cookie();
223    }
224
225    c->nblocks = 0;
226    cb_mutex_enter(&c->mutex);
227    while (ret == ENGINE_SUCCESS &&
228           (ret = me->the_engine->store((ENGINE_HANDLE*)me->the_engine, c, item, cas,
229                                        operation, vbucket)) == ENGINE_EWOULDBLOCK &&
230           c->handle_ewouldblock)
231    {
232        ++c->nblocks;
233        cb_cond_wait(&c->cond, &c->mutex);
234        ret = c->status;
235    }
236    cb_mutex_exit(&c->mutex);
237
238    if (c != cookie) {
239        destroy_mock_cookie(c);
240    }
241
242    return ret;
243}
244
245static ENGINE_ERROR_CODE mock_arithmetic(ENGINE_HANDLE* handle,
246                                         const void* cookie,
247                                         const void* key,
248                                         const int nkey,
249                                         const bool increment,
250                                         const bool create,
251                                         const uint64_t delta,
252                                         const uint64_t initial,
253                                         const rel_time_t exptime,
254                                         uint64_t *cas,
255                                         uint8_t datatype,
256                                         uint64_t *result,
257                                         uint16_t vbucket) {
258    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
259    struct mock_engine *me = get_handle(handle);
260    struct mock_connstruct *c = (void*)cookie;
261    if (c == NULL) {
262        c = (void*)create_mock_cookie();
263    }
264
265    c->nblocks = 0;
266    cb_mutex_enter(&c->mutex);
267    while (ret == ENGINE_SUCCESS &&
268           (ret = me->the_engine->arithmetic((ENGINE_HANDLE*)me->the_engine, c, key,
269                                             nkey, increment, create,
270                                             delta, initial, exptime,
271                                             cas, datatype,
272                                             result, vbucket)) == ENGINE_EWOULDBLOCK &&
273           c->handle_ewouldblock)
274    {
275        ++c->nblocks;
276        cb_cond_wait(&c->cond, &c->mutex);
277        ret = c->status;
278    }
279    cb_mutex_exit(&c->mutex);
280
281    if (c != cookie) {
282        destroy_mock_cookie(c);
283    }
284
285    return ret;
286}
287
288static ENGINE_ERROR_CODE mock_flush(ENGINE_HANDLE* handle,
289                                    const void* cookie, time_t when) {
290    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
291    struct mock_engine *me = get_handle(handle);
292    struct mock_connstruct *c = (void*)cookie;
293    if (c == NULL) {
294        c = (void*)create_mock_cookie();
295    }
296
297    c->nblocks = 0;
298    cb_mutex_enter(&c->mutex);
299    while (ret == ENGINE_SUCCESS &&
300           (ret = me->the_engine->flush((ENGINE_HANDLE*)me->the_engine, c, when)) == ENGINE_EWOULDBLOCK &&
301           c->handle_ewouldblock)
302    {
303        ++c->nblocks;
304        cb_cond_wait(&c->cond, &c->mutex);
305        ret = c->status;
306    }
307    cb_mutex_exit(&c->mutex);
308
309    if (c != cookie) {
310        destroy_mock_cookie(c);
311    }
312
313    return ret;
314}
315
316static void mock_reset_stats(ENGINE_HANDLE* handle, const void *cookie) {
317    struct mock_engine *me = get_handle(handle);
318    me->the_engine->reset_stats((ENGINE_HANDLE*)me->the_engine, cookie);
319}
320
321static ENGINE_ERROR_CODE mock_unknown_command(ENGINE_HANDLE* handle,
322                                              const void* cookie,
323                                              protocol_binary_request_header *request,
324                                              ADD_RESPONSE response)
325{
326    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
327    struct mock_engine *me = get_handle(handle);
328    struct mock_connstruct *c = (void*)cookie;
329    if (c == NULL) {
330        c = (void*)create_mock_cookie();
331    }
332
333    c->nblocks = 0;
334    cb_mutex_enter(&c->mutex);
335    while (ret == ENGINE_SUCCESS &&
336           (ret = me->the_engine->unknown_command((ENGINE_HANDLE*)me->the_engine, c,
337                                                  request, response)) == ENGINE_EWOULDBLOCK &&
338           c->handle_ewouldblock)
339    {
340        ++c->nblocks;
341        cb_cond_wait(&c->cond, &c->mutex);
342        ret = c->status;
343    }
344    cb_mutex_exit(&c->mutex);
345
346    if (c != cookie) {
347        destroy_mock_cookie(c);
348    }
349
350    return ret;
351}
352
353static void mock_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,
354                              item* item, uint64_t val)
355{
356    struct mock_engine *me = get_handle(handle);
357    me->the_engine->item_set_cas((ENGINE_HANDLE*)me->the_engine, cookie, item, val);
358}
359
360
361static bool mock_get_item_info(ENGINE_HANDLE *handle, const void *cookie,
362                               const item* item, item_info *item_info)
363{
364    struct mock_engine *me = get_handle(handle);
365    return me->the_engine->get_item_info((ENGINE_HANDLE*)me->the_engine,
366                                         cookie, item, item_info);
367}
368
369static void *mock_get_stats_struct(ENGINE_HANDLE* handle, const void* cookie)
370{
371    struct mock_engine *me = get_handle(handle);
372    return me->the_engine->get_stats_struct((ENGINE_HANDLE*)me->the_engine, cookie);
373}
374
375static ENGINE_ERROR_CODE mock_aggregate_stats(ENGINE_HANDLE* handle,
376                                              const void* cookie,
377                                              void (*callback)(void*, void*),
378                                              void *vptr)
379{
380    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
381    struct mock_engine *me = get_handle(handle);
382    struct mock_connstruct *c = (void*)cookie;
383    if (c == NULL) {
384        c = (void*)create_mock_cookie();
385    }
386
387    c->nblocks = 0;
388    cb_mutex_enter(&c->mutex);
389    while (ret == ENGINE_SUCCESS &&
390           (ret = me->the_engine->aggregate_stats((ENGINE_HANDLE*)me->the_engine, c,
391                                                  callback, vptr)) == ENGINE_EWOULDBLOCK &&
392           c->handle_ewouldblock)
393    {
394        ++c->nblocks;
395        cb_cond_wait(&c->cond, &c->mutex);
396        ret = c->status;
397    }
398    cb_mutex_exit(&c->mutex);
399
400    if (c != cookie) {
401        destroy_mock_cookie(c);
402    }
403
404    return ret;
405}
406
407static ENGINE_ERROR_CODE mock_tap_notify(ENGINE_HANDLE* handle,
408                                        const void *cookie,
409                                        void *engine_specific,
410                                        uint16_t nengine,
411                                        uint8_t ttl,
412                                        uint16_t tap_flags,
413                                        tap_event_t tap_event,
414                                        uint32_t tap_seqno,
415                                        const void *key,
416                                        size_t nkey,
417                                        uint32_t flags,
418                                        uint32_t exptime,
419                                        uint64_t cas,
420                                        uint8_t datatype,
421                                        const void *data,
422                                        size_t ndata,
423                                        uint16_t vbucket) {
424
425    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
426    struct mock_engine *me = get_handle(handle);
427    struct mock_connstruct *c = (void*)cookie;
428    if (c == NULL) {
429        c = (void*)create_mock_cookie();
430    }
431
432    c->nblocks = 0;
433    cb_mutex_enter(&c->mutex);
434    while (ret == ENGINE_SUCCESS &&
435           (ret = me->the_engine->tap_notify((ENGINE_HANDLE*)me->the_engine, c,
436                                             engine_specific, nengine, ttl, tap_flags,
437                                             tap_event, tap_seqno, key, nkey, flags,
438                                             exptime, cas, datatype,
439                                             data, ndata, vbucket)) == ENGINE_EWOULDBLOCK &&
440           c->handle_ewouldblock)
441    {
442        ++c->nblocks;
443        cb_cond_wait(&c->cond, &c->mutex);
444        ret = c->status;
445    }
446    cb_mutex_exit(&c->mutex);
447
448    if (c != cookie) {
449        destroy_mock_cookie(c);
450    }
451
452    return ret;
453}
454
455
456static TAP_ITERATOR mock_get_tap_iterator(ENGINE_HANDLE* handle, const void* cookie,
457                                           const void* client, size_t nclient,
458                                           uint32_t flags,
459                                           const void* userdata, size_t nuserdata) {
460    struct mock_engine *me = get_handle(handle);
461    me->iterator = me->the_engine->get_tap_iterator((ENGINE_HANDLE*)me->the_engine, cookie,
462                                                    client, nclient, flags, userdata, nuserdata);
463    return (me->iterator != NULL) ? mock_tap_iterator : NULL;
464}
465
466static size_t mock_errinfo(ENGINE_HANDLE *handle, const void* cookie,
467                           char *buffer, size_t buffsz) {
468    struct mock_engine *me = get_handle(handle);
469    return me->the_engine->errinfo((ENGINE_HANDLE*)me->the_engine, cookie,
470                                   buffer, buffsz);
471}
472
473static ENGINE_ERROR_CODE mock_upr_step(ENGINE_HANDLE* handle,
474                                       const void* cookie,
475                                       struct upr_message_producers *producers) {
476    struct mock_engine *me = get_handle(handle);
477    return me->the_engine->upr.step((ENGINE_HANDLE*)me->the_engine, cookie,
478                                    producers);
479}
480
481static ENGINE_ERROR_CODE mock_upr_open(ENGINE_HANDLE* handle,
482                                       const void* cookie,
483                                       uint32_t opaque,
484                                       uint32_t seqno,
485                                       uint32_t flags,
486                                       void *name,
487                                       uint16_t nname) {
488    struct mock_engine *me = get_handle(handle);
489    return me->the_engine->upr.open((ENGINE_HANDLE*)me->the_engine, cookie,
490                                    opaque, seqno, flags, name, nname);
491}
492
493static ENGINE_ERROR_CODE mock_upr_add_stream(ENGINE_HANDLE* handle,
494                                             const void* cookie,
495                                             uint32_t opaque,
496                                             uint16_t vbucket,
497                                             uint32_t flags) {
498
499    struct mock_engine *me = get_handle(handle);
500    struct mock_connstruct *c = (void*)cookie;
501    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
502
503    if (c == NULL) {
504        c = (void*)create_mock_cookie();
505    }
506
507    c->nblocks = 0;
508    cb_mutex_enter(&c->mutex);
509    while (ret == ENGINE_SUCCESS &&
510           (ret = me->the_engine->upr.add_stream((ENGINE_HANDLE*)me->the_engine, c,
511                                                 opaque, vbucket, flags))
512                                                == ENGINE_EWOULDBLOCK &&
513           c->handle_ewouldblock)
514        {
515            ++c->nblocks;
516            cb_cond_wait(&c->cond, &c->mutex);
517            ret = c->status;
518        }
519    cb_mutex_exit(&c->mutex);
520
521    if (c != cookie) {
522        destroy_mock_cookie(c);
523    }
524
525    return ret;
526}
527
528static ENGINE_ERROR_CODE mock_upr_close_stream(ENGINE_HANDLE* handle,
529                                               const void* cookie,
530                                               uint32_t opaque,
531                                               uint16_t vbucket) {
532    struct mock_engine *me = get_handle(handle);
533    return me->the_engine->upr.close_stream((ENGINE_HANDLE*)me->the_engine,
534                                            cookie, opaque, vbucket);
535}
536
537static ENGINE_ERROR_CODE mock_upr_stream_req(ENGINE_HANDLE* handle,
538                                             const void* cookie,
539                                             uint32_t flags,
540                                             uint32_t opaque,
541                                             uint16_t vbucket,
542                                             uint64_t start_seqno,
543                                             uint64_t end_seqno,
544                                             uint64_t vbucket_uuid,
545                                             uint64_t high_seqno,
546                                             uint64_t *rollback_seqno,
547                                             upr_add_failover_log callback) {
548    struct mock_engine *me = get_handle(handle);
549    return me->the_engine->upr.stream_req((ENGINE_HANDLE*)me->the_engine,
550                                          cookie, flags, opaque, vbucket,
551                                          start_seqno, end_seqno, vbucket_uuid,
552                                          high_seqno, rollback_seqno,
553                                          callback);
554}
555
556static ENGINE_ERROR_CODE mock_upr_get_failover_log(ENGINE_HANDLE* handle,
557                                                   const void* cookie,
558                                                   uint32_t opaque,
559                                                   uint16_t vbucket,
560                                                   upr_add_failover_log cb) {
561    struct mock_engine *me = get_handle(handle);
562    return me->the_engine->upr.get_failover_log((ENGINE_HANDLE*)me->the_engine,
563                                                cookie, opaque, vbucket, cb);
564}
565
566static ENGINE_ERROR_CODE mock_upr_stream_end(ENGINE_HANDLE* handle,
567                                             const void* cookie,
568                                             uint32_t opaque,
569                                             uint16_t vbucket,
570                                             uint32_t flags) {
571    struct mock_engine *me = get_handle(handle);
572    return me->the_engine->upr.stream_end((ENGINE_HANDLE*)me->the_engine,
573                                          cookie, opaque, vbucket, flags);
574}
575
576static ENGINE_ERROR_CODE mock_upr_snapshot_marker(ENGINE_HANDLE* handle,
577                                                  const void* cookie,
578                                                  uint32_t opaque,
579                                                  uint16_t vbucket) {
580    struct mock_engine *me = get_handle(handle);
581    return me->the_engine->upr.snapshot_marker((ENGINE_HANDLE*)me->the_engine,
582                                               cookie, opaque, vbucket);
583}
584
585static ENGINE_ERROR_CODE mock_upr_mutation(ENGINE_HANDLE* handle,
586                                           const void* cookie,
587                                           uint32_t opaque,
588                                           const void *key,
589                                           uint16_t nkey,
590                                           const void *value,
591                                           uint32_t nvalue,
592                                           uint64_t cas,
593                                           uint16_t vbucket,
594                                           uint32_t flags,
595                                           uint8_t datatype,
596                                           uint64_t bySeqno,
597                                           uint64_t revSeqno,
598                                           uint32_t expiration,
599                                           uint32_t lockTime,
600                                           const void *meta,
601                                           uint16_t nmeta,
602                                           uint8_t nru) {
603
604    struct mock_engine *me = get_handle(handle);
605    struct mock_connstruct *c = (void*)cookie;
606    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
607    if (c == NULL) {
608        c = (void*)create_mock_cookie();
609    }
610
611    c->nblocks = 0;
612    cb_mutex_enter(&c->mutex);
613    while (ret == ENGINE_SUCCESS &&
614           (ret = me->the_engine->upr.mutation((ENGINE_HANDLE*)me->the_engine, c, opaque, key,
615                                               nkey, value, nvalue, cas, vbucket, flags,
616                                               datatype, bySeqno, revSeqno, expiration,
617                                               lockTime, meta, nmeta, nru)) == ENGINE_EWOULDBLOCK &&
618           c->handle_ewouldblock)
619        {
620            ++c->nblocks;
621            cb_cond_wait(&c->cond, &c->mutex);
622            ret = c->status;
623        }
624    cb_mutex_exit(&c->mutex);
625
626    if (c != cookie) {
627        destroy_mock_cookie(c);
628    }
629
630    return ret;
631}
632
633static ENGINE_ERROR_CODE mock_upr_deletion(ENGINE_HANDLE* handle,
634                                           const void* cookie,
635                                           uint32_t opaque,
636                                           const void *key,
637                                           uint16_t nkey,
638                                           uint64_t cas,
639                                           uint16_t vbucket,
640                                           uint64_t bySeqno,
641                                           uint64_t revSeqno,
642                                           const void *meta,
643                                           uint16_t nmeta) {
644
645    struct mock_engine *me = get_handle(handle);
646    struct mock_connstruct *c = (void*)cookie;
647    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
648    if (c == NULL) {
649        c = (void*)create_mock_cookie();
650    }
651
652    c->nblocks = 0;
653    cb_mutex_enter(&c->mutex);
654    while (ret == ENGINE_SUCCESS &&
655           (ret = me->the_engine->upr.deletion((ENGINE_HANDLE*)me->the_engine, c,
656                                               opaque, key, nkey, cas, vbucket, bySeqno,
657                                               revSeqno, meta, nmeta)) == ENGINE_EWOULDBLOCK &&
658           c->handle_ewouldblock)
659        {
660            ++c->nblocks;
661            cb_cond_wait(&c->cond, &c->mutex);
662            ret = c->status;
663        }
664    cb_mutex_exit(&c->mutex);
665
666    if (c != cookie) {
667        destroy_mock_cookie(c);
668    }
669
670    return ret;
671}
672
673static ENGINE_ERROR_CODE mock_upr_expiration(ENGINE_HANDLE* handle,
674                                             const void* cookie,
675                                             uint32_t opaque,
676                                             const void *key,
677                                             uint16_t nkey,
678                                             uint64_t cas,
679                                             uint16_t vbucket,
680                                             uint64_t bySeqno,
681                                             uint64_t revSeqno,
682                                             const void *meta,
683                                             uint16_t nmeta) {
684
685    struct mock_engine *me = get_handle(handle);
686    struct mock_connstruct *c = (void*)cookie;
687    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
688    if (c == NULL) {
689        c = (void*)create_mock_cookie();
690    }
691
692    c->nblocks = 0;
693    cb_mutex_enter(&c->mutex);
694    while (ret == ENGINE_SUCCESS &&
695           (ret = me->the_engine->upr.expiration((ENGINE_HANDLE*)me->the_engine, c,
696                                                 opaque, key, nkey, cas, vbucket, bySeqno,
697                                                 revSeqno, meta, nmeta)) == ENGINE_EWOULDBLOCK &&
698           c->handle_ewouldblock)
699        {
700            ++c->nblocks;
701            cb_cond_wait(&c->cond, &c->mutex);
702            ret = c->status;
703        }
704    cb_mutex_exit(&c->mutex);
705
706    if (c != cookie) {
707        destroy_mock_cookie(c);
708    }
709
710    return ret;
711}
712
713static ENGINE_ERROR_CODE mock_upr_flush(ENGINE_HANDLE* handle,
714                                        const void* cookie,
715                                        uint32_t opaque,
716                                        uint16_t vbucket) {
717
718    struct mock_engine *me = get_handle(handle);
719    struct mock_connstruct *c = (void*)cookie;
720    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
721    if (c == NULL) {
722        c = (void*)create_mock_cookie();
723    }
724
725    c->nblocks = 0;
726    cb_mutex_enter(&c->mutex);
727    while (ret == ENGINE_SUCCESS &&
728           (ret = me->the_engine->upr.flush((ENGINE_HANDLE*)me->the_engine, c, opaque,
729                                            vbucket)) == ENGINE_EWOULDBLOCK &&
730           c->handle_ewouldblock)
731        {
732            ++c->nblocks;
733            cb_cond_wait(&c->cond, &c->mutex);
734            ret = c->status;
735        }
736    cb_mutex_exit(&c->mutex);
737
738    if (c != cookie) {
739        destroy_mock_cookie(c);
740    }
741
742    return ret;
743}
744
745static ENGINE_ERROR_CODE mock_upr_set_vbucket_state(ENGINE_HANDLE* handle,
746                                                    const void* cookie,
747                                                    uint32_t opaque,
748                                                    uint16_t vbucket,
749                                                    vbucket_state_t state) {
750    struct mock_engine *me = get_handle(handle);
751    return me->the_engine->upr.set_vbucket_state((ENGINE_HANDLE*)me->the_engine,
752                                                 cookie, opaque, vbucket,
753                                                 state);
754}
755
756static ENGINE_ERROR_CODE mock_upr_noop(ENGINE_HANDLE* handle,
757                                       const void* cookie,
758                                       uint32_t opaque) {
759    struct mock_engine *me = get_handle(handle);
760    return me->the_engine->upr.noop((ENGINE_HANDLE*)me->the_engine,
761                                    cookie, opaque);
762}
763
764static ENGINE_ERROR_CODE mock_upr_buffer_acknowledgement(ENGINE_HANDLE* handle,
765                                                         const void* cookie,
766                                                         uint32_t opaque,
767                                                         uint16_t vbucket,
768                                                         uint32_t bb) {
769    struct mock_engine *me = get_handle(handle);
770    return me->the_engine->upr.buffer_acknowledgement((ENGINE_HANDLE*)me->the_engine,
771                                                      cookie, opaque, vbucket, bb);
772}
773
774static ENGINE_ERROR_CODE mock_upr_response_handler(ENGINE_HANDLE* handle,
775                                                   const void* cookie,
776                                                   protocol_binary_response_header *response) {
777    struct mock_engine *me = get_handle(handle);
778    return me->the_engine->upr.response_handler((ENGINE_HANDLE*)me->the_engine,
779                                                cookie, response);
780}
781
782struct mock_engine mock_engine;
783
784EXTENSION_LOGGER_DESCRIPTOR *logger_descriptor = NULL;
785static ENGINE_HANDLE *handle = NULL;
786static ENGINE_HANDLE_V1 *handle_v1 = NULL;
787
788static void usage(void) {
789    printf("\n");
790    printf("engine_testapp -E <path_to_engine_lib> -T <path_to_testlib>\n");
791    printf("               [-e <engine_config>] [-h]\n");
792    printf("\n");
793    printf("-E <path_to_engine_lib>      Path to the engine library file. The\n");
794    printf("                             engine library file is a library file\n");
795    printf("                             (.so or .dll) that the contains the \n");
796    printf("                             implementation of the engine being\n");
797    printf("                             tested.\n");
798    printf("\n");
799    printf("-T <path_to_testlib>         Path to the test library file. The test\n");
800    printf("                             library file is a library file (.so or\n");
801    printf("                             .dll) that contains the set of tests\n");
802    printf("                             to be executed.\n");
803    printf("\n");
804    printf("-t <timeout>                 Maximum time to run a test.\n");
805    printf("-e <engine_config>           Engine configuration string passed to\n");
806    printf("                             the engine.\n");
807    printf("-q                           Only print errors.");
808    printf("-.                           Print a . for each executed test.");
809    printf("\n");
810    printf("-h                           Prints this usage text.\n");
811    printf("-v                           verbose output\n");
812    printf("\n");
813}
814
815static int report_test(const char *name, time_t duration, enum test_result r, bool quiet, bool compact) {
816    int rc = 0;
817    char *msg = NULL;
818    int color = 0;
819    char color_str[8] = { 0 };
820    const char *reset_color = color_enabled ? "\033[m" : "";
821
822    switch (r) {
823    case SUCCESS:
824        msg="OK";
825        color = 32;
826        break;
827    case SKIPPED:
828        msg="SKIPPED";
829        color = 32;
830        break;
831    case FAIL:
832        color = 31;
833        msg="FAIL";
834        rc = 1;
835        break;
836    case DIED:
837        color = 31;
838        msg = "DIED";
839        rc = 1;
840        break;
841    case TIMEOUT:
842        color = 31;
843        msg = "TIMED OUT";
844        rc = 1;
845        break;
846    case CORE:
847        color = 31;
848        msg = "CORE DUMPED";
849        rc = 1;
850        break;
851    case PENDING:
852        color = 33;
853        msg = "PENDING";
854        break;
855    default:
856        color = 31;
857        msg = "UNKNOWN";
858        rc = 1;
859    }
860
861    assert(msg);
862    if (color_enabled) {
863        snprintf(color_str, sizeof(color_str), "\033[%dm", color);
864    }
865
866    if (quiet) {
867        if (r != SUCCESS) {
868            printf("%s:  (%u sec) %s%s%s\n", name, duration,
869                   color_str, msg, reset_color);
870            fflush(stdout);
871        }
872    } else {
873        if (compact && (r == SUCCESS || r == SKIPPED || r == PENDING)) {
874            size_t len = strlen(name) + 27; /* for "Running [0/0] xxxx ..." etc */
875            size_t ii;
876
877            fprintf(stdout, "\r");
878            for (ii = 0; ii < len; ++ii) {
879                fprintf(stdout, " ");
880            }
881            fprintf(stdout, "\r");
882            fflush(stdout);
883        } else {
884            printf("(%u sec) %s%s%s\n", duration, color_str, msg, reset_color);
885        }
886    }
887    return rc;
888}
889
890static ENGINE_HANDLE_V1 *start_your_engines(const char *engine, const char* cfg, bool engine_init) {
891
892    init_mock_server(handle);
893    if (!load_engine(engine, &get_mock_server_api, logger_descriptor, &handle)) {
894        fprintf(stderr, "Failed to load engine %s.\n", engine);
895        return NULL;
896    }
897
898    if (engine_init) {
899        if(!init_engine(handle, cfg, logger_descriptor)) {
900            fprintf(stderr, "Failed to init engine %s with config %s.\n", engine, cfg);
901            return NULL;
902        }
903    }
904
905    memset(&mock_engine, 0, sizeof(mock_engine));
906    mock_engine.me.interface.interface = 1;
907
908    mock_engine.me.get_info = mock_get_info;
909    mock_engine.me.initialize = mock_initialize;
910    mock_engine.me.destroy = mock_destroy;
911    mock_engine.me.allocate = mock_allocate;
912    mock_engine.me.remove = mock_remove;
913    mock_engine.me.release = mock_release;
914    mock_engine.me.get = mock_get;
915    mock_engine.me.store = mock_store;
916    mock_engine.me.arithmetic = mock_arithmetic;
917    mock_engine.me.flush = mock_flush;
918    mock_engine.me.get_stats = mock_get_stats;
919    mock_engine.me.reset_stats = mock_reset_stats;
920    mock_engine.me.get_stats_struct = mock_get_stats_struct;
921    mock_engine.me.aggregate_stats = mock_aggregate_stats;
922    mock_engine.me.unknown_command = mock_unknown_command;
923    mock_engine.me.tap_notify = mock_tap_notify;
924    mock_engine.me.get_tap_iterator = mock_get_tap_iterator;
925    mock_engine.me.item_set_cas = mock_item_set_cas;
926    mock_engine.me.get_item_info = mock_get_item_info;
927    mock_engine.me.errinfo = mock_errinfo;
928    mock_engine.me.upr.step = mock_upr_step;
929    mock_engine.me.upr.open = mock_upr_open;
930    mock_engine.me.upr.add_stream = mock_upr_add_stream;
931    mock_engine.me.upr.close_stream = mock_upr_close_stream;
932    mock_engine.me.upr.stream_req = mock_upr_stream_req;
933    mock_engine.me.upr.get_failover_log = mock_upr_get_failover_log;
934    mock_engine.me.upr.stream_end = mock_upr_stream_end;
935    mock_engine.me.upr.snapshot_marker = mock_upr_snapshot_marker;
936    mock_engine.me.upr.mutation = mock_upr_mutation;
937    mock_engine.me.upr.deletion = mock_upr_deletion;
938    mock_engine.me.upr.expiration = mock_upr_expiration;
939    mock_engine.me.upr.flush = mock_upr_flush;
940    mock_engine.me.upr.set_vbucket_state = mock_upr_set_vbucket_state;
941    mock_engine.me.upr.noop = mock_upr_noop;
942    mock_engine.me.upr.buffer_acknowledgement = mock_upr_buffer_acknowledgement;
943    mock_engine.me.upr.response_handler = mock_upr_response_handler;
944
945    handle_v1 = mock_engine.the_engine = (ENGINE_HANDLE_V1*)handle;
946    handle = (ENGINE_HANDLE*)&mock_engine.me;
947    handle_v1 = &mock_engine.me;
948
949    /* Reset all members that aren't set (to allow the users to write */
950    /* testcases to verify that they initialize them.. */
951    assert(mock_engine.me.interface.interface == mock_engine.the_engine->interface.interface);
952
953    if (mock_engine.the_engine->get_stats_struct == NULL) {
954        mock_engine.me.get_stats_struct = NULL;
955    }
956    if (mock_engine.the_engine->aggregate_stats == NULL) {
957        mock_engine.me.aggregate_stats = NULL;
958    }
959    if (mock_engine.the_engine->unknown_command == NULL) {
960        mock_engine.me.unknown_command = NULL;
961    }
962    if (mock_engine.the_engine->tap_notify == NULL) {
963        mock_engine.me.tap_notify = NULL;
964    }
965    if (mock_engine.the_engine->get_tap_iterator == NULL) {
966        mock_engine.me.get_tap_iterator = NULL;
967    }
968    if (mock_engine.the_engine->errinfo == NULL) {
969        mock_engine.me.errinfo = NULL;
970    }
971
972    return &mock_engine.me;
973}
974
975static void destroy_engine(bool force) {
976    if (handle_v1) {
977        handle_v1->destroy(handle, force);
978        handle_v1 = NULL;
979        handle = NULL;
980    }
981}
982
983static void reload_engine(ENGINE_HANDLE **h, ENGINE_HANDLE_V1 **h1,
984                          const char* engine, const char *cfg, bool init, bool force) {
985    destroy_engine(force);
986    handle_v1 = start_your_engines(engine, cfg, init);
987    handle = (ENGINE_HANDLE*)(handle_v1);
988    *h1 = handle_v1;
989    *h = handle;
990}
991
992static engine_test_t* current_testcase;
993
994static const engine_test_t* get_current_testcase(void)
995{
996    return current_testcase;
997}
998
999static int execute_test(engine_test_t test,
1000                        const char *engine,
1001                        const char *default_cfg)
1002{
1003    enum test_result ret = PENDING;
1004    if (test.tfun != NULL) {
1005        current_testcase = &test;
1006        if (test.prepare != NULL) {
1007            if ((ret = test.prepare(&test)) == SUCCESS) {
1008                ret = PENDING;
1009            }
1010        }
1011
1012        if (ret == PENDING) {
1013            /* Start the engines and go */
1014            start_your_engines(engine, test.cfg ? test.cfg : default_cfg, true);
1015            if (test.test_setup != NULL) {
1016                if (!test.test_setup(handle, handle_v1)) {
1017                    fprintf(stderr, "Failed to run setup for test %s\n", test.name);
1018                    return FAIL;
1019                }
1020            }
1021            ret = test.tfun(handle, handle_v1);
1022            if (test.test_teardown != NULL) {
1023                if (!test.test_teardown(handle, handle_v1)) {
1024                    fprintf(stderr, "WARNING: Failed to run teardown for test %s\n", test.name);
1025                }
1026            }
1027            destroy_engine(false);
1028
1029            if (test.cleanup) {
1030                test.cleanup(&test, ret);
1031            }
1032        }
1033    }
1034
1035    return (int)ret;
1036}
1037
1038static void setup_alarm_handler() {
1039#ifndef WIN32
1040    struct sigaction sig_handler;
1041
1042    sig_handler.sa_handler = alarm_handler;
1043    sig_handler.sa_flags = 0;
1044
1045    sigaction(SIGALRM, &sig_handler, NULL);
1046#endif
1047}
1048
1049static void set_test_timeout(int timeout) {
1050#ifndef WIN32
1051    alarm(timeout);
1052#endif
1053}
1054
1055static void clear_test_timeout() {
1056#ifndef WIN32
1057    alarm(0);
1058    alarmed = 0;
1059#endif
1060}
1061
1062static int safe_append(char *buffer, const char *txt) {
1063    int len = 0;
1064
1065    /*
1066     * We should probably make this a bit safer (by
1067     * checking if its already escaped etc, but I'll
1068     * do that whenever it turns out to be a problem ;-)
1069     */
1070    while (*txt) {
1071        switch (*txt) {
1072#ifndef WIN32
1073        case ' ':
1074        case '\\':
1075        case ';':
1076        case '|':
1077        case '&':
1078            buffer[len++] = '\\';
1079#endif
1080        default:
1081            buffer[len++] = *txt;
1082        }
1083        ++txt;
1084    }
1085
1086    buffer[len++] = ' ';
1087
1088    return len;
1089}
1090
1091int main(int argc, char **argv) {
1092    int c, exitcode = 0, num_cases = 0, timeout = 0, loop_count = 0;
1093    bool verbose = false;
1094    bool quiet = false;
1095    bool dot = false;
1096    bool loop = false;
1097    bool terminate_on_error = false;
1098    const char *engine = NULL;
1099    const char *engine_args = NULL;
1100    const char *test_suite = NULL;
1101    const char *test_case = NULL;
1102    engine_test_t *testcases = NULL;
1103    cb_dlhandle_t handle;
1104    char *errmsg;
1105    void *symbol;
1106    struct test_harness harness;
1107    int test_case_id = -1;
1108    char *cmdline = malloc(64*1024); /* should be enough */
1109
1110    /* Hack to remove the warning from C99 */
1111    union {
1112        GET_TESTS get_tests;
1113        void* voidptr;
1114    } my_get_test;
1115
1116    /* Hack to remove the warning from C99 */
1117    union {
1118        SETUP_SUITE setup_suite;
1119        void* voidptr;
1120    } my_setup_suite;
1121
1122    /* Hack to remove the warning from C99 */
1123    union {
1124        TEARDOWN_SUITE teardown_suite;
1125        void* voidptr;
1126    } my_teardown_suite;
1127
1128    cb_initialize_sockets();
1129
1130    if (cmdline == NULL) {
1131        fprintf(stderr, "Failed to allocate memory");
1132        exit(EXIT_FAILURE);
1133    }
1134
1135    memset(&my_get_test, 0, sizeof(my_get_test));
1136    memset(&my_setup_suite, 0, sizeof(my_setup_suite));
1137    memset(&my_teardown_suite, 0, sizeof(my_teardown_suite));
1138
1139    logger_descriptor = get_null_logger();
1140    color_enabled = getenv("TESTAPP_ENABLE_COLOR") != NULL;
1141
1142    /* Use unbuffered stdio */
1143    setbuf(stdout, NULL);
1144    setbuf(stderr, NULL);
1145
1146    setup_alarm_handler();
1147
1148    /* process arguments */
1149    while ((c = getopt(argc, argv,
1150                       "h"  /* usage */
1151                       "E:" /* Engine to load */
1152                       "e:" /* Engine options */
1153                       "T:" /* Library with tests to load */
1154                       "t:" /* Timeout */
1155                       "L"  /* Loop until failure */
1156                       "q"  /* Be more quiet (only report failures) */
1157                       "."  /* dot mode. */
1158                       "n:"  /* test case to run */
1159                       "v" /* verbose output */
1160                       "Z"  /* Terminate on first error */
1161                       "C:" /* Test case id */
1162                       "s" /* spinlock the program */
1163                       )) != -1) {
1164        switch (c) {
1165        case 's' : {
1166            int spin = 1;
1167            while (spin) {
1168
1169            }
1170            break;
1171        }
1172        case 'C' :
1173            test_case_id = atoi(optarg);
1174            break;
1175        case 'E':
1176            engine = optarg;
1177            break;
1178        case 'e':
1179            engine_args = optarg;
1180            break;
1181        case 'h':
1182            usage();
1183            return 0;
1184        case 'T':
1185            test_suite = optarg;
1186            break;
1187        case 't':
1188            timeout = atoi(optarg);
1189            break;
1190        case 'L':
1191            loop = true;
1192            break;
1193        case 'n':
1194            test_case = optarg;
1195            break;
1196        case 'v' :
1197            verbose = true;
1198            break;
1199        case 'q':
1200            quiet = true;
1201            break;
1202        case '.':
1203            dot = true;
1204            break;
1205        case 'Z' :
1206            terminate_on_error = true;
1207            break;
1208        default:
1209            fprintf(stderr, "Illegal argument \"%c\"\n", c);
1210            return 1;
1211        }
1212    }
1213
1214    /* validate args */
1215    if (engine == NULL) {
1216        fprintf(stderr, "You must provide a path to the storage engine library.\n");
1217        return 1;
1218    }
1219
1220    if (test_suite == NULL) {
1221        fprintf(stderr, "You must provide a path to the testsuite library.\n");
1222        return 1;
1223    }
1224
1225    /* load test_suite */
1226    handle = cb_dlopen(test_suite, &errmsg);
1227    if (handle == NULL) {
1228        fprintf(stderr, "Failed to load testsuite %s: %s\n", test_suite,
1229                errmsg);
1230        free(errmsg);
1231        return 1;
1232    }
1233
1234    /* get the test cases */
1235    symbol = cb_dlsym(handle, "get_tests", &errmsg);
1236    if (symbol == NULL) {
1237        fprintf(stderr, "Could not find get_tests function in testsuite %s: %s\n", test_suite, errmsg);
1238        free(errmsg);
1239        return 1;
1240    }
1241    my_get_test.voidptr = symbol;
1242    testcases = (*my_get_test.get_tests)();
1243
1244    /* set up the suite if needed */
1245    memset(&harness, 0, sizeof(harness));
1246    harness.default_engine_cfg = engine_args;
1247    harness.engine_path = engine;
1248    harness.reload_engine = reload_engine;
1249    harness.start_engine = start_your_engines;
1250    harness.create_cookie = create_mock_cookie;
1251    harness.destroy_cookie = destroy_mock_cookie;
1252    harness.set_ewouldblock_handling = mock_set_ewouldblock_handling;
1253    harness.lock_cookie = lock_mock_cookie;
1254    harness.unlock_cookie = unlock_mock_cookie;
1255    harness.waitfor_cookie = waitfor_mock_cookie;
1256    harness.time_travel = mock_time_travel;
1257    harness.get_current_testcase = get_current_testcase;
1258
1259    for (num_cases = 0; testcases[num_cases].name; num_cases++) {
1260        /* Just counting */
1261    }
1262
1263    symbol = cb_dlsym(handle, "setup_suite", &errmsg);
1264    if (symbol == NULL) {
1265        free(errmsg);
1266    } else {
1267        my_setup_suite.voidptr = symbol;
1268        if (!(*my_setup_suite.setup_suite)(&harness)) {
1269            fprintf(stderr, "Failed to set up test suite %s \n", test_suite);
1270            return 1;
1271        }
1272    }
1273
1274    if (test_case_id != -1) {
1275        if (test_case_id >= num_cases) {
1276            fprintf(stderr, "Invalid test case id specified\n");
1277            exit(EXIT_FAILURE);
1278        }
1279        exit(execute_test(testcases[test_case_id], engine, engine_args));
1280    }
1281
1282    do {
1283        int i;
1284        bool need_newline = false;
1285        for (i = 0; testcases[i].name; i++) {
1286            int error;
1287            if (test_case != NULL && strcmp(test_case, testcases[i].name) != 0)
1288                continue;
1289            if (!quiet) {
1290                printf("Running [%04d/%04d]: %s...",
1291                       i + num_cases * loop_count,
1292                       num_cases * (loop_count + 1),
1293                       testcases[i].name);
1294                fflush(stdout);
1295            } else if(dot) {
1296                printf(".");
1297                need_newline = true;
1298                /* Add a newline every few tests */
1299                if ((i+1) % 70 == 0) {
1300                    printf("\n");
1301                    need_newline = false;
1302                }
1303            }
1304            set_test_timeout(timeout);
1305
1306            {
1307                int ii;
1308                int offset = 0;
1309                enum test_result ecode;
1310                time_t start;
1311                time_t stop;
1312                int rc;
1313                for (ii = 0; ii < argc; ++ii) {
1314                    offset += safe_append(cmdline + offset, argv[ii]);
1315                }
1316
1317                sprintf(cmdline + offset, "-C %d", i);
1318
1319                start = time(NULL);
1320                rc = system(cmdline);
1321                stop = time(NULL);
1322
1323#ifdef WIN32
1324                ecode = (enum test_result)rc;
1325#else
1326                if (WIFEXITED(rc)) {
1327                    ecode = (enum test_result)WEXITSTATUS(rc);
1328#ifdef WCOREDUMP
1329                } else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) {
1330                    ecode = CORE;
1331#endif
1332                } else {
1333                    ecode = DIED;
1334                }
1335#endif
1336                error = report_test(testcases[i].name,
1337                                    stop - start,
1338                                    ecode, quiet,
1339                                    !verbose);
1340            }
1341            clear_test_timeout();
1342
1343            if (error != 0) {
1344                ++exitcode;
1345                if (terminate_on_error) {
1346                    exit(EXIT_FAILURE);
1347                }
1348            }
1349        }
1350
1351        if (need_newline) {
1352            printf("\n");
1353        }
1354        ++loop_count;
1355    } while (loop && exitcode == 0);
1356
1357    /* tear down the suite if needed */
1358    symbol = cb_dlsym(handle, "teardown_suite", &errmsg);
1359    if (symbol == NULL) {
1360        free(errmsg);
1361    } else {
1362        my_teardown_suite.voidptr = symbol;
1363        if (!(*my_teardown_suite.teardown_suite)()) {
1364            fprintf(stderr, "Failed to teardown up test suite %s \n", test_suite);
1365        }
1366    }
1367
1368    printf("# Passed %d of %d tests\n", num_cases - exitcode, num_cases);
1369    free(cmdline);
1370
1371    return exitcode;
1372}
1373