MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
basic_engine_testsuite.c
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 
12 
13 /*
14  * Make sure that get_info returns something and that repeated calls to it
15  * return the same something.
16  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 
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  */
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  */
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  */
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 
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  */
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  */
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  */
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  */
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 
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;
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 
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 
494 static enum test_result get_stats_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
495  return PENDING;
496 }
497 
498 static enum test_result reset_stats_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
499  return PENDING;
500 }
501 
502 static enum test_result get_stats_struct_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
503  return PENDING;
504 }
505 
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 
512 static void release_last_response(void) {
513  free(last_response);
514  last_response = NULL;
515 }
516 
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 
548 static enum test_result touch_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
549  union request {
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 
630 static enum test_result gat_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
631  union request {
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 
712 static enum test_result gatq_test(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
713  union request {
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
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
828 bool setup_suite(struct test_harness *th) {
829  test_harness = *th;
830  return true;
831 }
832