1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #undef NDEBUG
3 #include "config.h"
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <pthread.h>
9 #include "basic_engine_testsuite.h"
10 
11 struct test_harness test_harness;
12 
13 /*
14  * Make sure that get_info returns something and that repeated calls to it
15  * return the same something.
16  */
get_info_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)17 static enum test_result get_info_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
18     const engine_info *info = h1->get_info(h);
19     assert(info != NULL);
20     assert(info == h1->get_info(h));
21     return SUCCESS;
22 }
23 
24 /*
25  * Make sure that the structure returned by get_info has a non-null description.
26  */
get_info_description_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)27 static enum test_result get_info_description_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
28     const engine_info *info = h1->get_info(h);
29     assert(info->description != NULL);
30     return SUCCESS;
31 }
32 
33 /*
34  * Make sure that the structure returned by get_info has a valid number of
35  * features and that the size of the feautes array equals that value
36  */
get_info_features_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)37 static enum test_result get_info_features_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
38     const engine_info *info = h1->get_info(h);
39     uint32_t nfeats = info->num_features;
40     assert (nfeats > 0);
41     const feature_info *fi = info->features;
42     while (nfeats-- > 0) {
43         assert(fi++ != NULL);
44     }
45 
46     return SUCCESS;
47 }
48 
49 /*
50  * Make sure we can successfully allocate an item, allocate op returns success
51  * and that item struct is populated
52  */
allocate_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)53 static enum test_result allocate_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
54     item *test_item = NULL;
55     void *key = "akey";
56     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,1,1) == ENGINE_SUCCESS);
57     assert(test_item != NULL);
58     h1->release(h,NULL,test_item);
59     return SUCCESS;
60 }
61 
62 /*
63  * Verify set behavior
64  */
set_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)65 static enum test_result set_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
66     item *it;
67     void *key = "key";
68     assert(h1->allocate(h, NULL, &it, key,
69                         strlen(key), 1, 1, 0) == ENGINE_SUCCESS);
70 
71     uint64_t prev_cas;
72     uint64_t cas = 0;
73 
74     for (int ii = 0; ii < 10; ++ii) {
75         prev_cas = cas;
76         assert(h1->store(h, NULL, it, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
77         assert(cas != prev_cas);
78     }
79     h1->release(h, NULL, it);
80     return SUCCESS;
81 }
82 
83 /*
84  * Verify add behavior
85  */
add_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)86 static enum test_result add_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
87     item *it;
88     void *key = "key";
89     assert(h1->allocate(h, NULL, &it, key,
90                         strlen(key), 1, 1, 0) == ENGINE_SUCCESS);
91     uint64_t cas;
92 
93     for (int ii = 0; ii < 10; ++ii) {
94         ENGINE_ERROR_CODE ret = h1->store(h, NULL, it, &cas, OPERATION_ADD, 0);
95         if (ii == 0) {
96             assert(ret == ENGINE_SUCCESS);
97             assert(cas != 0);
98         } else {
99             assert(ret == ENGINE_NOT_STORED);
100         }
101     }
102     h1->release(h, NULL, it);
103     return SUCCESS;
104 }
105 
106 /*
107  * Verify replace behavior
108  */
replace_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)109 static enum test_result replace_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
110     item *it;
111     void *key = "key";
112     assert(set_test(h, h1) == SUCCESS);
113 
114     assert(h1->allocate(h, NULL, &it, key,
115                         strlen(key), sizeof(int), 1, 0) == ENGINE_SUCCESS);
116     item_info item_info = { .nvalue = 1 };
117     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
118 
119     uint64_t prev_cas;
120     uint64_t cas = 0;
121 
122     for (int ii = 0; ii < 10; ++ii) {
123         prev_cas = cas;
124         *(int*)(item_info.value[0].iov_base) = ii;
125         assert(h1->store(h, NULL, it, &cas, OPERATION_REPLACE,0) == ENGINE_SUCCESS);
126         assert(cas != prev_cas);
127     }
128     h1->release(h, NULL, it);
129 
130     assert(h1->get(h, NULL, &it, key, strlen(key), 0) == ENGINE_SUCCESS);
131     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
132     assert(item_info.value[0].iov_len == sizeof(int));
133     assert(*(int*)(item_info.value[0].iov_base) == 9);
134     h1->release(h, NULL, it);
135 
136     return SUCCESS;
137 }
138 
139 /*
140  * Verify append behavior
141  */
append_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)142 static enum test_result append_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
143     item *it;
144     void *key = "key";
145     uint64_t cas;
146 
147     assert(h1->allocate(h, NULL, &it, key,
148                         strlen(key), 5, 1, 0) == ENGINE_SUCCESS);
149     item_info item_info = { .nvalue = 1 };
150     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
151     memcpy(item_info.value[0].iov_base, "HELLO", 5);
152     assert(h1->store(h, NULL, it, &cas, OPERATION_SET, 0) == ENGINE_SUCCESS);
153     h1->release(h, NULL, it);
154     assert(h1->allocate(h, NULL, &it, key,
155                         strlen(key), 6, 1, 0) == ENGINE_SUCCESS);
156     item_info.nvalue = 1;
157     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
158     memcpy(item_info.value[0].iov_base, " WORLD", 6);
159     assert(h1->store(h, NULL, it, &cas, OPERATION_APPEND, 0) == ENGINE_SUCCESS);
160     h1->release(h, NULL, it);
161 
162     assert(h1->get(h, NULL, &it, key, strlen(key), 0) == ENGINE_SUCCESS);
163     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
164     assert(item_info.value[0].iov_len == 11);
165     assert(memcmp(item_info.value[0].iov_base, "HELLO WORLD", 11) == 0);
166     h1->release(h, NULL, it);
167 
168     return SUCCESS;
169 }
170 
171 /*
172  * Verify prepend behavior
173  */
prepend_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)174 static enum test_result prepend_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
175     item *it;
176     void *key = "key";
177     uint64_t cas;
178 
179     assert(h1->allocate(h, NULL, &it, key,
180                         strlen(key), 5, 1, 0) == ENGINE_SUCCESS);
181     item_info item_info = { .nvalue = 1 };
182     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
183     memcpy(item_info.value[0].iov_base, "HELLO", 5);
184     assert(h1->store(h, NULL, it, &cas, OPERATION_SET, 0) == ENGINE_SUCCESS);
185     h1->release(h, NULL, it);
186     assert(h1->allocate(h, NULL, &it, key,
187                         strlen(key), 6, 1, 0) == ENGINE_SUCCESS);
188     item_info.nvalue = 1;
189     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
190     memcpy(item_info.value[0].iov_base, " WORLD", 6);
191     assert(h1->store(h, NULL, it, &cas, OPERATION_PREPEND, 0) == ENGINE_SUCCESS);
192     h1->release(h, NULL, it);
193 
194     assert(h1->get(h, NULL, &it, key, strlen(key), 0) == ENGINE_SUCCESS);
195     assert(h1->get_item_info(h, NULL, it, &item_info) == true);
196     assert(item_info.value[0].iov_len == 11);
197     assert(memcmp(item_info.value[0].iov_base, " WORLDHELLO", 11) == 0);
198     h1->release(h, NULL, it);
199 
200     return SUCCESS;
201 }
202 
203 /*
204  * Make sure when we can successfully store an item after it has been allocated
205  * and that the cas for the stored item has been generated.
206  */
store_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)207 static enum test_result store_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
208     item *test_item = NULL;
209     void *key = "bkey";
210     uint64_t cas = 0;
211     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,1,1) == ENGINE_SUCCESS);
212     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
213     assert(cas != 0);
214     h1->release(h,NULL,test_item);
215     return SUCCESS;
216 }
217 
218 /*
219  * Make sure when we can successfully retrieve an item that has been stored in
220  * the engine
221  */
get_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)222 static enum test_result get_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
223     item *test_item = NULL;
224     item *test_item_get = NULL;
225     void *key = "get_test_key";
226     uint64_t cas = 0;
227     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,0, 0) == ENGINE_SUCCESS);
228     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
229     assert(h1->get(h,NULL,&test_item_get,key,strlen(key),0) == ENGINE_SUCCESS);
230     h1->release(h,NULL,test_item);
231     h1->release(h,NULL,test_item_get);
232     return SUCCESS;
233 }
234 
expiry_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)235 static enum test_result expiry_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
236     item *test_item = NULL;
237     item *test_item_get = NULL;
238     void *key = "get_test_key";
239     uint64_t cas = 0;
240     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1, 0, 10) == ENGINE_SUCCESS);
241     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET, 0) == ENGINE_SUCCESS);
242     test_harness.time_travel(11);
243     assert(h1->get(h,NULL,&test_item_get,key,strlen(key),0) == ENGINE_KEY_ENOENT);
244     h1->release(h,NULL,test_item);
245     return SUCCESS;
246 }
247 
248 /*
249  * Make sure that we can release an item. For the most part all this test does
250  * is ensure that thinds dont go splat when we call release. It does nothing to
251  * ensure that release did much of anything.
252  */
release_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)253 static enum test_result release_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
254     item *test_item = NULL;
255     void *key = "release_test_key";
256     uint64_t cas = 0;
257     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1, 0, 0) == ENGINE_SUCCESS);
258     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
259     h1->release(h, NULL, test_item);
260     return SUCCESS;
261 }
262 
263 /*
264  * Make sure that we can remove an item and that after the item has been
265  * removed it can not be retrieved.
266  */
remove_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)267 static enum test_result remove_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
268     item *test_item = NULL;
269     void *key = "remove_test_key";
270     uint64_t cas = 0;
271     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,0, 0) == ENGINE_SUCCESS);
272     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
273     assert(h1->remove(h, NULL, key, strlen(key), cas, 0) == ENGINE_SUCCESS);
274     item *check_item = test_item;
275     assert(h1->get(h, NULL, &check_item, key, strlen(key), 0) ==  ENGINE_KEY_ENOENT);
276     assert(check_item == NULL);
277     h1->release(h, NULL, test_item);
278     return SUCCESS;
279 }
280 
281 /*
282  * Make sure we can arithmetic operations to set the initial value of a key and
283  * to then later increment that value
284  */
incr_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)285 static enum test_result incr_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
286     item *test_item = NULL;
287     void *key = "incr_test_key";
288     uint64_t cas = 0;
289     uint64_t res = 0;
290     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1, 0, 0) == ENGINE_SUCCESS);
291     assert(h1->arithmetic(h, NULL, key, strlen(key), true, true, 0, 1,
292            0, &cas, &res, 0 ) == ENGINE_SUCCESS);
293     assert(res == 1);
294     assert(h1->arithmetic(h, NULL, key, strlen(key), true, false, 1, 0,
295            0, &cas, &res, 0 ) == ENGINE_SUCCESS);
296     assert(res == 2);
297     h1->release(h, NULL, test_item);
298     return SUCCESS;
299 }
300 
incr_test_main(void * arg)301 static void *incr_test_main(void *arg) {
302     ENGINE_HANDLE *h = arg;
303     ENGINE_HANDLE_V1 *h1 = arg;
304     void *key = "incr_test_key";
305     uint64_t cas = 0;
306     uint64_t res = 0;
307 
308     for (int ii = 0; ii < 1000; ++ii) {
309         assert(h1->arithmetic(h, NULL, key, strlen(key), false, false, 1, 0,
310                               0, &cas, &res, 0 ) == ENGINE_SUCCESS);
311 
312     }
313 
314     return NULL;
315 }
316 
317 
318 /*
319  * Make sure we can arithmetic operations to set the initial value of a key and
320  * to then later increment that value
321  */
mt_incr_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)322 static enum test_result mt_incr_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
323 #ifdef __arm__
324     const int max_threads = 1;
325 #else
326     const int max_threads = 30;
327 #endif
328     pthread_t tid[max_threads];
329 
330     if (max_threads < 2) {
331         return SKIPPED;
332     }
333 
334     item *test_item = NULL;
335     void *key = "incr_test_key";
336     uint64_t cas = 0;
337     uint64_t res = 0;
338     assert(h1->allocate(h, NULL, &test_item, key,
339                         strlen(key), 1, 0, 0) == ENGINE_SUCCESS);
340     assert(h1->arithmetic(h, NULL, key, strlen(key), true, true, 0, 1,
341                           0, &cas, &res, 0 ) == ENGINE_SUCCESS);
342     h1->release(h, NULL, test_item);
343 
344     for (int ii = 0; ii < max_threads; ++ii) {
345         assert(pthread_create(&tid[ii], NULL, incr_test_main, h) == 0);
346     }
347 
348     for (int ii = 0; ii < max_threads; ++ii) {
349         void *ret;
350         assert(pthread_join(tid[ii], &ret) == 0);
351         assert(ret == NULL);
352     }
353 
354     return SUCCESS;
355 }
356 
357 /*
358  * Make sure we can arithmetic operations to set the initial value of a key and
359  * to then later decrement that value
360  */
decr_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)361 static enum test_result decr_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
362     item *test_item = NULL;
363     void *key = "decr_test_key";
364     uint64_t cas = 0;
365     uint64_t res = 0;
366     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,0, 0) == ENGINE_SUCCESS);
367     assert(h1->arithmetic(h, NULL, key, strlen(key), false, true, 0, 1,
368            0, &cas, &res, 0 ) == ENGINE_SUCCESS);
369     assert(res == 1);
370     assert(h1->arithmetic(h, NULL, key, strlen(key), false, false, 1, 0,
371            0, &cas, &res, 0 ) == ENGINE_SUCCESS);
372     assert(res == 0);
373     h1->release(h, NULL, test_item);
374     return SUCCESS;
375 }
376 
377 /*
378  * Make sure we can successfully perform a flush operation and that any item
379  * stored before the flush can not be retrieved
380  */
flush_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)381 static enum test_result flush_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
382     item *test_item = NULL;
383     void *key = "flush_test_key";
384     uint64_t cas = 0;
385     test_harness.time_travel(3);
386     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1, 0, 0) == ENGINE_SUCCESS);
387     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
388     assert(h1->flush(h, NULL, 0) == ENGINE_SUCCESS);
389     item *check_item = test_item;
390     assert(h1->get(h, NULL, &check_item, key, strlen(key), 0) ==  ENGINE_KEY_ENOENT);
391     assert(check_item == NULL);
392     h1->release(h, NULL, test_item);
393     return SUCCESS;
394 }
395 
396 /*
397  * Make sure we can successfully retrieve the item info struct for an item and
398  * that the contents of the item_info are as expected.
399  */
get_item_info_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)400 static enum test_result get_item_info_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
401     item *test_item = NULL;
402     char *key = "get_item_info_test_key";
403     uint64_t cas = 0;
404     const rel_time_t exp = 1;
405     item_info ii = { .nvalue = 1 };
406     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,0, exp) == ENGINE_SUCCESS);
407     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
408     /* Had this been actual code, there'd be a connection here */
409     assert(h1->get_item_info(h, NULL, test_item, &ii) == true);
410     assert(ii.cas == cas);
411     assert(ii.flags == 0);
412     assert(strcmp(key,ii.key) == 0);
413     assert(ii.nkey == strlen(key));
414     assert(ii.nbytes == 1);
415     assert(ii.exptime == exp);
416     h1->release(h, NULL, test_item);
417     return SUCCESS;
418 }
419 
item_set_cas_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)420 static enum test_result item_set_cas_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
421     item *test_item = NULL;
422     char *key = "item_set_cas_test_key";
423     uint64_t cas = 0;
424     const rel_time_t exp = 1;
425     item_info ii = { .nvalue = 1 };
426     assert(h1->allocate(h, NULL, &test_item, key, strlen(key), 1,0, exp) == ENGINE_SUCCESS);
427     assert(h1->store(h, NULL, test_item, &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
428     uint64_t newcas = cas + 1;
429     h1->item_set_cas(h, NULL, test_item, newcas);
430     assert(h1->get_item_info(h, NULL, test_item, &ii) == true);
431     assert(ii.cas == newcas);
432     h1->release(h, NULL, test_item);
433     return SUCCESS;
434 }
435 
436 uint32_t evictions;
eviction_stats_handler(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,const void * cookie)437 static void eviction_stats_handler(const char *key, const uint16_t klen,
438                                    const char *val, const uint32_t vlen,
439                                    const void *cookie) {
440     if (memcmp(key, "evictions", klen) == 0) {
441         char buffer[vlen + 1];
442         memcpy(buffer, val, vlen);
443         buffer[vlen] = '\0';
444         evictions = atoi(buffer);
445     }
446 }
447 
lru_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)448 static enum test_result lru_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
449     item *test_item = NULL;
450     const char *hot_key = "hot_key";
451     uint64_t cas = 0;
452     assert(h1->allocate(h, NULL, &test_item,
453                         hot_key, strlen(hot_key), 4096, 0, 0) == ENGINE_SUCCESS);
454     assert(h1->store(h, NULL, test_item,
455                      &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
456     h1->release(h, NULL, test_item);
457 
458     int ii;
459     for (ii = 0; ii < 250; ++ii) {
460         assert(h1->get(h, NULL, &test_item,
461                        hot_key, strlen(hot_key), 0) ==  ENGINE_SUCCESS);
462         h1->release(h, NULL, test_item);
463         char key[1024];
464         size_t keylen = snprintf(key, sizeof(key), "lru_test_key_%08d", ii);
465         assert(h1->allocate(h, NULL, &test_item,
466                             key, keylen, 4096, 0, 0) == ENGINE_SUCCESS);
467         assert(h1->store(h, NULL, test_item,
468                          &cas, OPERATION_SET,0) == ENGINE_SUCCESS);
469         h1->release(h, NULL, test_item);
470         assert(h1->get_stats(h, NULL, NULL, 0,
471                              eviction_stats_handler) == ENGINE_SUCCESS);
472         if (evictions == 2) {
473             break;
474         }
475     }
476 
477     assert(ii < 250);
478     for (int jj = 0; jj <= ii; ++jj) {
479         char key[1024];
480         size_t keylen = snprintf(key, sizeof(key), "lru_test_key_%08d", jj);
481         if (jj == 0 || jj == 1) {
482             assert(h1->get(h, NULL, &test_item,
483                            key, keylen, 0) == ENGINE_KEY_ENOENT);
484         } else {
485             assert(h1->get(h, NULL, &test_item,
486                            key, keylen, 0) == ENGINE_SUCCESS);
487             assert(test_item != NULL);
488             h1->release(h, NULL, test_item);
489         }
490     }
491     return SUCCESS;
492 }
493 
get_stats_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)494 static enum test_result get_stats_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
495     return PENDING;
496 }
497 
reset_stats_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)498 static enum test_result reset_stats_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
499     return PENDING;
500 }
501 
get_stats_struct_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)502 static enum test_result get_stats_struct_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
503     return PENDING;
504 }
505 
aggregate_stats_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)506 static enum test_result aggregate_stats_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
507     return PENDING;
508 }
509 
510 static protocol_binary_response_header *last_response;
511 
release_last_response(void)512 static void release_last_response(void) {
513     free(last_response);
514     last_response = NULL;
515 }
516 
response_handler(const void * key,uint16_t keylen,const void * ext,uint8_t extlen,const void * body,uint32_t bodylen,uint8_t datatype,uint16_t status,uint64_t cas,const void * cookie)517 static bool response_handler(const void *key, uint16_t keylen,
518                              const void *ext, uint8_t extlen,
519                              const void *body, uint32_t bodylen,
520                              uint8_t datatype, uint16_t status,
521                              uint64_t cas, const void *cookie)
522 {
523     assert(last_response == NULL);
524     last_response = malloc(sizeof(*last_response) + keylen + extlen + bodylen);
525     if (last_response == NULL) {
526         return false;
527     }
528     protocol_binary_response_header *r = last_response;
529     r->response.magic = PROTOCOL_BINARY_RES;
530     r->response.opcode = 0xff; // we don't know this!
531     r->response.keylen = htons(keylen);
532     r->response.extlen = extlen;
533     r->response.datatype = PROTOCOL_BINARY_RAW_BYTES;
534     r->response.status = htons(status);
535     r->response.bodylen = htonl(keylen + extlen + bodylen);
536     r->response.opaque = 0xffffff; // we don't know this
537     r->response.cas = cas;
538     char *ptr = (void*)(r + 1);
539     memcpy(ptr, ext, extlen);
540     ptr += extlen;
541     memcpy(ptr, key, keylen);
542     ptr += keylen;
543     memcpy(ptr, body, bodylen);
544 
545     return true;
546 }
547 
touch_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)548 static enum test_result touch_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
549     union request {
550         protocol_binary_request_touch touch;
551         char buffer[512];
552     };
553 
554     void *key = "get_test_key";
555     size_t keylen = strlen(key);
556     union request r = {
557         .touch = {
558             .message = {
559                 .header.request = {
560                     .magic = PROTOCOL_BINARY_REQ,
561                     .opcode = PROTOCOL_BINARY_CMD_TOUCH,
562                     .keylen = htons((uint16_t)keylen),
563                     .extlen = 4,
564                     .datatype = PROTOCOL_BINARY_RAW_BYTES,
565                     .vbucket = 0,
566                     .bodylen = htonl(keylen + 4),
567                     .opaque = 0xdeadbeef,
568                     .cas = 0
569                 },
570                 .body = {
571                     .expiration = htonl(10)
572                 }
573             }
574         }
575     };
576 
577     memcpy(r.buffer + sizeof(r.touch.bytes), key, keylen);
578     ENGINE_ERROR_CODE ret;
579     ret = h1->unknown_command(h, NULL, &r.touch.message.header, response_handler);
580     assert(ret == ENGINE_SUCCESS);
581     assert(last_response != NULL);
582     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
583     assert(last_response->response.keylen == 0);
584     assert(last_response->response.extlen == 0);
585     assert(last_response->response.bodylen == 0);
586     release_last_response();
587 
588     // store and get a key
589     assert(get_test(h, h1) == SUCCESS);
590 
591     // Set expiry time to 10 secs..
592     ret = h1->unknown_command(h, NULL, &r.touch.message.header, response_handler);
593     assert(ret == ENGINE_SUCCESS);
594     assert(last_response != NULL);
595     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_SUCCESS);
596     assert(last_response->response.keylen == 0);
597     assert(last_response->response.extlen == 0);
598     assert(last_response->response.bodylen == 0);
599     release_last_response();
600 
601     // time-travel 11 secs..
602     test_harness.time_travel(11);
603 
604     // The item should have expired now...
605     item *item = NULL;
606     assert(h1->get(h, NULL, &item, key, keylen, 0) == ENGINE_KEY_ENOENT);
607 
608     // Verify that it doesn't accept bogus packets. extlen is mandatory
609     r.touch.message.header.request.extlen = 0;
610     r.touch.message.header.request.bodylen = htonl(keylen);
611     ret = h1->unknown_command(h, NULL, &r.touch.message.header, response_handler);
612     assert(ret == ENGINE_SUCCESS);
613     assert(last_response != NULL);
614     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
615     release_last_response();
616 
617     // key is mandatory!
618     r.touch.message.header.request.extlen = 4;
619     r.touch.message.header.request.keylen = 0;
620     r.touch.message.header.request.bodylen = htonl(4);
621     ret = h1->unknown_command(h, NULL, &r.touch.message.header, response_handler);
622     assert(ret == ENGINE_SUCCESS);
623     assert(last_response != NULL);
624     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
625     release_last_response();
626 
627     return SUCCESS;
628 }
629 
gat_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)630 static enum test_result gat_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
631     union request {
632         protocol_binary_request_gat gat;
633         char buffer[512];
634     };
635 
636     void *key = "get_test_key";
637     size_t keylen = strlen(key);
638     union request r = {
639         .gat = {
640             .message = {
641                 .header.request = {
642                     .magic = PROTOCOL_BINARY_REQ,
643                     .opcode = PROTOCOL_BINARY_CMD_GAT,
644                     .keylen = htons((uint16_t)keylen),
645                     .extlen = 4,
646                     .datatype = PROTOCOL_BINARY_RAW_BYTES,
647                     .vbucket = 0,
648                     .bodylen = htonl(keylen + 4),
649                     .opaque = 0xdeadbeef,
650                     .cas = 0
651                 },
652                 .body = {
653                     .expiration = htonl(10)
654                 }
655             }
656         }
657     };
658 
659     memcpy(r.buffer + sizeof(r.gat.bytes), key, keylen);
660     ENGINE_ERROR_CODE ret;
661     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
662     assert(ret == ENGINE_SUCCESS);
663     assert(last_response != NULL);
664     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
665     assert(last_response->response.keylen == 0);
666     assert(last_response->response.extlen == 0);
667     assert(last_response->response.bodylen == 0);
668     release_last_response();
669 
670     // store and get a key
671     assert(get_test(h, h1) == SUCCESS);
672 
673     // Set expiry time to 10 secs..
674     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
675     assert(ret == ENGINE_SUCCESS);
676     assert(last_response != NULL);
677     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_SUCCESS);
678     assert(last_response->response.keylen == 0);
679     assert(last_response->response.extlen == 4);
680     assert(ntohl(last_response->response.bodylen) == 5); // get_test sets 1 byte datalen
681     release_last_response();
682 
683     // time-travel 11 secs..
684     test_harness.time_travel(11);
685 
686     // The item should have expired now...
687     item *item = NULL;
688     assert(h1->get(h, NULL, &item, key, keylen, 0) == ENGINE_KEY_ENOENT);
689 
690     // Verify that it doesn't accept bogus packets. extlen is mandatory
691     r.gat.message.header.request.extlen = 0;
692     r.gat.message.header.request.bodylen = htonl(keylen);
693     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
694     assert(ret == ENGINE_SUCCESS);
695     assert(last_response != NULL);
696     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
697     release_last_response();
698 
699     // key is mandatory!
700     r.gat.message.header.request.extlen = 4;
701     r.gat.message.header.request.keylen = 0;
702     r.gat.message.header.request.bodylen = htonl(4);
703     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
704     assert(ret == ENGINE_SUCCESS);
705     assert(last_response != NULL);
706     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
707     release_last_response();
708 
709     return SUCCESS;
710 }
711 
gatq_test(ENGINE_HANDLE * h,ENGINE_HANDLE_V1 * h1)712 static enum test_result gatq_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
713     union request {
714         protocol_binary_request_gat gat;
715         char buffer[512];
716     };
717 
718     void *key = "get_test_key";
719     size_t keylen = strlen(key);
720     union request r = {
721         .gat = {
722             .message = {
723                 .header.request = {
724                     .magic = PROTOCOL_BINARY_REQ,
725                     .opcode = PROTOCOL_BINARY_CMD_GATQ,
726                     .keylen = htons((uint16_t)keylen),
727                     .extlen = 4,
728                     .datatype = PROTOCOL_BINARY_RAW_BYTES,
729                     .vbucket = 0,
730                     .bodylen = htonl(keylen + 4),
731                     .opaque = 0xdeadbeef,
732                     .cas = 0
733                 },
734                 .body = {
735                     .expiration = htonl(10)
736                 }
737             }
738         }
739     };
740 
741     memcpy(r.buffer + sizeof(r.gat.bytes), key, keylen);
742     ENGINE_ERROR_CODE ret;
743     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
744     assert(ret == ENGINE_SUCCESS);
745 
746     // GATQ is quiet and should not produce any result
747     assert(last_response == NULL);
748 
749     // store and get a key
750     assert(get_test(h, h1) == SUCCESS);
751 
752     // Set expiry time to 10 secs..
753     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
754     assert(ret == ENGINE_SUCCESS);
755     assert(last_response != NULL);
756     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_SUCCESS);
757     assert(last_response->response.keylen == 0);
758     assert(last_response->response.extlen == 4);
759     assert(ntohl(last_response->response.bodylen) == 5); // get_test sets 1 byte datalen
760     release_last_response();
761 
762     // time-travel 11 secs..
763     test_harness.time_travel(11);
764 
765     // The item should have expired now...
766     item *item = NULL;
767     assert(h1->get(h, NULL, &item, key, keylen, 0) == ENGINE_KEY_ENOENT);
768 
769     // Verify that it doesn't accept bogus packets. extlen is mandatory
770     r.gat.message.header.request.extlen = 0;
771     r.gat.message.header.request.bodylen = htonl(keylen);
772     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
773     assert(ret == ENGINE_SUCCESS);
774     assert(last_response != NULL);
775     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
776     release_last_response();
777 
778     // key is mandatory!
779     r.gat.message.header.request.extlen = 4;
780     r.gat.message.header.request.keylen = 0;
781     r.gat.message.header.request.bodylen = htonl(4);
782     ret = h1->unknown_command(h, NULL, &r.gat.message.header, response_handler);
783     assert(ret == ENGINE_SUCCESS);
784     assert(last_response != NULL);
785     assert(ntohs(last_response->response.status) == PROTOCOL_BINARY_RESPONSE_EINVAL);
786     release_last_response();
787 
788     return SUCCESS;
789 }
790 
791 MEMCACHED_PUBLIC_API
get_tests(void)792 engine_test_t* get_tests(void) {
793     static engine_test_t tests[]  = {
794         {"get info test", get_info_test, NULL, NULL, NULL},
795         {"get info description test", get_info_description_test, NULL, NULL, NULL},
796         {"get info features test", get_info_features_test, NULL, NULL, NULL},
797         {"allocate test", allocate_test, NULL, NULL, NULL},
798         {"set test", set_test, NULL, NULL, NULL},
799         {"add test", add_test, NULL, NULL, NULL},
800         {"replace test", replace_test, NULL, NULL, NULL},
801         {"append test", append_test, NULL, NULL, NULL},
802         {"prepend test", prepend_test, NULL, NULL, NULL},
803         {"store test", store_test, NULL, NULL, NULL},
804         {"get test", get_test, NULL, NULL, NULL},
805         {"expiry test", expiry_test, NULL, NULL, NULL},
806         {"remove test", remove_test, NULL, NULL, NULL},
807         {"release test", release_test, NULL, NULL, NULL},
808         {"incr test", incr_test, NULL, NULL, NULL},
809         {"mt incr test", mt_incr_test, NULL, NULL, NULL},
810         {"decr test", decr_test, NULL, NULL, NULL},
811         {"flush test", flush_test, NULL, NULL, NULL},
812         {"get item info test", get_item_info_test, NULL, NULL, NULL},
813         {"set cas test", item_set_cas_test, NULL, NULL, NULL},
814         {"LRU test", lru_test, NULL, NULL, "cache_size=48"},
815         {"get stats test", get_stats_test, NULL, NULL, NULL},
816         {"reset stats test", reset_stats_test, NULL, NULL, NULL},
817         {"get stats struct test", get_stats_struct_test, NULL, NULL, NULL},
818         {"aggregate stats test", aggregate_stats_test, NULL, NULL, NULL},
819         {"touch", touch_test, NULL, NULL, NULL},
820         {"Get And Touch", gat_test, NULL, NULL, NULL},
821         {"Get And Touch Quiet", gatq_test, NULL, NULL, NULL},
822         {NULL, NULL, NULL, NULL, NULL}
823     };
824     return tests;
825 }
826 
827 MEMCACHED_PUBLIC_API
setup_suite(struct test_harness * th)828 bool setup_suite(struct test_harness *th) {
829     test_harness = *th;
830     return true;
831 }
832 
833