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