9faccf4e11cc2fe0e59600a31d82cb490ce2481a
[awesomized/libmemcached] / memcached / testapp.c
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #undef NDEBUG
3 #include <pthread.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <sys/wait.h>
7 #include <netdb.h>
8 #include <arpa/inet.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <assert.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <netinet/in.h>
19 #include <fcntl.h>
20
21 #include "config.h"
22 #include "cache.h"
23 #include "util.h"
24 #include "protocol_binary.h"
25
26 #define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
27
28 enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
29
30 static pid_t server_pid;
31 static in_port_t port;
32 static int sock;
33 static bool allow_closed_read = false;
34
35 static enum test_return cache_create_test(void)
36 {
37 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
38 NULL, NULL);
39 assert(cache != NULL);
40 cache_destroy(cache);
41 return TEST_PASS;
42 }
43
44 const uint64_t constructor_pattern = 0xdeadcafebabebeef;
45
46 static int cache_constructor(void *buffer, void *notused1, int notused2) {
47 uint64_t *ptr = buffer;
48 *ptr = constructor_pattern;
49 return 0;
50 }
51
52 static enum test_return cache_constructor_test(void)
53 {
54 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
55 cache_constructor, NULL);
56 assert(cache != NULL);
57 uint64_t *ptr = cache_alloc(cache);
58 uint64_t pattern = *ptr;
59 cache_free(cache, ptr);
60 cache_destroy(cache);
61 return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
62 }
63
64 static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
65 return 1;
66 }
67
68 static enum test_return cache_fail_constructor_test(void)
69 {
70 enum test_return ret = TEST_PASS;
71
72 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
73 cache_fail_constructor, NULL);
74 assert(cache != NULL);
75 uint64_t *ptr = cache_alloc(cache);
76 if (ptr != NULL) {
77 ret = TEST_FAIL;
78 }
79 cache_destroy(cache);
80 return ret;
81 }
82
83 static void *destruct_data = 0;
84
85 static void cache_destructor(void *buffer, void *notused) {
86 destruct_data = buffer;
87 }
88
89 static enum test_return cache_destructor_test(void)
90 {
91 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
92 NULL, cache_destructor);
93 assert(cache != NULL);
94 char *ptr = cache_alloc(cache);
95 cache_free(cache, ptr);
96 cache_destroy(cache);
97
98 return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
99 }
100
101 static enum test_return cache_reuse_test(void)
102 {
103 int ii;
104 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
105 NULL, NULL);
106 char *ptr = cache_alloc(cache);
107 cache_free(cache, ptr);
108 for (ii = 0; ii < 100; ++ii) {
109 char *p = cache_alloc(cache);
110 assert(p == ptr);
111 cache_free(cache, ptr);
112 }
113 cache_destroy(cache);
114 return TEST_PASS;
115 }
116
117
118 static enum test_return cache_bulkalloc(size_t datasize)
119 {
120 cache_t *cache = cache_create("test", datasize, sizeof(char*),
121 NULL, NULL);
122 #define ITERATIONS 1024
123 void *ptr[ITERATIONS];
124
125 for (int ii = 0; ii < ITERATIONS; ++ii) {
126 ptr[ii] = cache_alloc(cache);
127 assert(ptr[ii] != 0);
128 memset(ptr[ii], 0xff, datasize);
129 }
130
131 for (int ii = 0; ii < ITERATIONS; ++ii) {
132 cache_free(cache, ptr[ii]);
133 }
134
135 #undef ITERATIONS
136 cache_destroy(cache);
137 return TEST_PASS;
138 }
139
140 static enum test_return test_issue_161(void)
141 {
142 enum test_return ret = cache_bulkalloc(1);
143 if (ret == TEST_PASS) {
144 ret = cache_bulkalloc(512);
145 }
146
147 return ret;
148 }
149
150 static enum test_return cache_redzone_test(void)
151 {
152 #ifndef HAVE_UMEM_H
153 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
154 NULL, NULL);
155
156 /* Ignore SIGABORT */
157 struct sigaction old_action;
158 struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
159 sigemptyset(&action.sa_mask);
160 sigaction(SIGABRT, &action, &old_action);
161
162 /* check memory debug.. */
163 char *p = cache_alloc(cache);
164 char old = *(p - 1);
165 *(p - 1) = 0;
166 cache_free(cache, p);
167 assert(cache_error == -1);
168 *(p - 1) = old;
169
170 p[sizeof(uint32_t)] = 0;
171 cache_free(cache, p);
172 assert(cache_error == 1);
173
174 /* restore signal handler */
175 sigaction(SIGABRT, &old_action, NULL);
176
177 cache_destroy(cache);
178
179 return TEST_PASS;
180 #else
181 return TEST_SKIP;
182 #endif
183 }
184
185 static enum test_return test_safe_strtoul(void) {
186 uint32_t val;
187 assert(safe_strtoul("123", &val));
188 assert(val == 123);
189 assert(safe_strtoul("+123", &val));
190 assert(val == 123);
191 assert(!safe_strtoul("", &val)); // empty
192 assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
193 assert(!safe_strtoul(" issue221", &val)); // non-numeric
194 /* Not sure what it does, but this works with ICC :/
195 assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
196 */
197
198 // extremes:
199 assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
200 assert(val == 4294967295L);
201 /* This actually works on 64-bit ubuntu
202 assert(!safe_strtoul("4294967296", &val)); // 2**32
203 */
204 assert(!safe_strtoul("-1", &val)); // negative
205 return TEST_PASS;
206 }
207
208
209 static enum test_return test_safe_strtoull(void) {
210 uint64_t val;
211 assert(safe_strtoull("123", &val));
212 assert(val == 123);
213 assert(safe_strtoull("+123", &val));
214 assert(val == 123);
215 assert(!safe_strtoull("", &val)); // empty
216 assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
217 assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
218 assert(!safe_strtoull(" issue221", &val)); // non-numeric
219
220 // extremes:
221 assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
222 assert(val == 18446744073709551615ULL);
223 assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
224 assert(!safe_strtoull("-1", &val)); // negative
225 return TEST_PASS;
226 }
227
228 static enum test_return test_safe_strtoll(void) {
229 int64_t val;
230 assert(safe_strtoll("123", &val));
231 assert(val == 123);
232 assert(safe_strtoll("+123", &val));
233 assert(val == 123);
234 assert(safe_strtoll("-123", &val));
235 assert(val == -123);
236 assert(!safe_strtoll("", &val)); // empty
237 assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
238 assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
239 assert(!safe_strtoll(" issue221", &val)); // non-numeric
240
241 // extremes:
242 assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
243 assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
244 assert(val == 9223372036854775807LL);
245 /*
246 assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
247 assert(val == -9223372036854775808LL);
248 */
249 assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
250
251 // We'll allow space to terminate the string. And leading space.
252 assert(safe_strtoll(" 123 foo", &val));
253 assert(val == 123);
254 return TEST_PASS;
255 }
256
257 static enum test_return test_safe_strtol(void) {
258 int32_t val;
259 assert(safe_strtol("123", &val));
260 assert(val == 123);
261 assert(safe_strtol("+123", &val));
262 assert(val == 123);
263 assert(safe_strtol("-123", &val));
264 assert(val == -123);
265 assert(!safe_strtol("", &val)); // empty
266 assert(!safe_strtol("123BOGUS", &val)); // non-numeric
267 assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
268 assert(!safe_strtol(" issue221", &val)); // non-numeric
269
270 // extremes:
271 /* This actually works on 64-bit ubuntu
272 assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
273 */
274 assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
275 assert(val == 2147483647L);
276 /* This actually works on 64-bit ubuntu
277 assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
278 */
279
280 // We'll allow space to terminate the string. And leading space.
281 assert(safe_strtol(" 123 foo", &val));
282 assert(val == 123);
283 return TEST_PASS;
284 }
285
286 /**
287 * Function to start the server and let it listen on a random port
288 *
289 * @param port_out where to store the TCP port number the server is
290 * listening on
291 * @param daemon set to true if you want to run the memcached server
292 * as a daemon process
293 * @return the pid of the memcached server
294 */
295 static pid_t start_server(in_port_t *port_out, bool daemon, int timeout) {
296 char environment[80];
297 snprintf(environment, sizeof(environment),
298 "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
299 char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
300 char pid_file[80];
301 snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%lu", (long)getpid());
302
303 remove(filename);
304 remove(pid_file);
305
306 #ifdef __sun
307 /* I want to name the corefiles differently so that they don't
308 overwrite each other
309 */
310 char coreadm[128];
311 snprintf(coreadm, sizeof(coreadm),
312 "coreadm -p core.%%f.%%p %lu", (unsigned long)getpid());
313 system(coreadm);
314 #endif
315
316 pid_t pid = fork();
317 assert(pid != -1);
318
319 if (pid == 0) {
320 /* Child */
321 char *argv[20];
322 int arg = 0;
323 char tmo[24];
324 snprintf(tmo, sizeof(tmo), "%u", timeout);
325
326 putenv(environment);
327 #ifdef __sun
328 putenv("LD_PRELOAD=watchmalloc.so.1");
329 putenv("MALLOC_DEBUG=WATCH");
330 #endif
331
332 if (!daemon) {
333 argv[arg++] = "./timedrun";
334 argv[arg++] = tmo;
335 }
336 argv[arg++] = "./memcached-debug";
337 argv[arg++] = "-p";
338 argv[arg++] = "-1";
339 argv[arg++] = "-U";
340 argv[arg++] = "0";
341 /* Handle rpmbuild and the like doing this as root */
342 if (getuid() == 0) {
343 argv[arg++] = "-u";
344 argv[arg++] = "root";
345 }
346 if (daemon) {
347 argv[arg++] = "-d";
348 argv[arg++] = "-P";
349 argv[arg++] = pid_file;
350 }
351 #ifdef MESSAGE_DEBUG
352 argv[arg++] = "-vvv";
353 #endif
354 argv[arg++] = NULL;
355 assert(execv(argv[0], argv) != -1);
356 }
357
358 /* Yeah just let us "busy-wait" for the file to be created ;-) */
359 while (access(filename, F_OK) == -1) {
360 usleep(10);
361 }
362
363 FILE *fp = fopen(filename, "r");
364 if (fp == NULL) {
365 fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
366 strerror(errno));
367 assert(false);
368 }
369
370 *port_out = (in_port_t)-1;
371 char buffer[80];
372 while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
373 if (strncmp(buffer, "TCP INET: ", 10) == 0) {
374 int32_t val;
375 assert(safe_strtol(buffer + 10, &val));
376 *port_out = (in_port_t)val;
377 }
378 }
379 fclose(fp);
380 assert(remove(filename) == 0);
381
382 if (daemon) {
383 /* loop and wait for the pid file.. There is a potential race
384 * condition that the server just created the file but isn't
385 * finished writing the content, so we loop a few times
386 * reading as well */
387 while (access(pid_file, F_OK) == -1) {
388 usleep(10);
389 }
390
391 fp = fopen(pid_file, "r");
392 if (fp == NULL) {
393 fprintf(stderr, "Failed to open pid file: %s\n",
394 strerror(errno));
395 assert(false);
396 }
397
398 /* Avoid race by retrying 20 times */
399 for (int x = 0; x < 20 && fgets(buffer, sizeof(buffer), fp) == NULL; x++) {
400 usleep(10);
401 }
402 fclose(fp);
403
404 int32_t val;
405 assert(safe_strtol(buffer, &val));
406 pid = (pid_t)val;
407 }
408
409 return pid;
410 }
411
412 static enum test_return test_issue_44(void) {
413 in_port_t port;
414 pid_t pid = start_server(&port, true, 15);
415 assert(kill(pid, SIGHUP) == 0);
416 sleep(1);
417 assert(kill(pid, SIGTERM) == 0);
418
419 return TEST_PASS;
420 }
421
422 static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
423 {
424 struct addrinfo *ai = 0;
425 struct addrinfo hints = { .ai_family = AF_UNSPEC,
426 .ai_protocol = IPPROTO_TCP,
427 .ai_socktype = SOCK_STREAM };
428 char service[NI_MAXSERV];
429 int error;
430
431 (void)snprintf(service, NI_MAXSERV, "%d", port);
432 if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
433 if (error != EAI_SYSTEM) {
434 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
435 } else {
436 perror("getaddrinfo()");
437 }
438 }
439
440 return ai;
441 }
442
443 static int connect_server(const char *hostname, in_port_t port, bool nonblock)
444 {
445 struct addrinfo *ai = lookuphost(hostname, port);
446 int sock = -1;
447 if (ai != NULL) {
448 if ((sock = socket(ai->ai_family, ai->ai_socktype,
449 ai->ai_protocol)) != -1) {
450 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
451 fprintf(stderr, "Failed to connect socket: %s\n",
452 strerror(errno));
453 close(sock);
454 sock = -1;
455 } else if (nonblock) {
456 int flags = fcntl(sock, F_GETFL, 0);
457 if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
458 fprintf(stderr, "Failed to enable nonblocking mode: %s\n",
459 strerror(errno));
460 close(sock);
461 sock = -1;
462 }
463 }
464 } else {
465 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
466 }
467
468 freeaddrinfo(ai);
469 }
470 return sock;
471 }
472
473 static enum test_return test_vperror(void) {
474 int rv = 0;
475 int oldstderr = dup(STDERR_FILENO);
476 char tmpl[sizeof(TMP_TEMPLATE)+1];
477 strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
478
479 int newfile = mkstemp(tmpl);
480 assert(newfile > 0);
481 rv = dup2(newfile, STDERR_FILENO);
482 assert(rv == STDERR_FILENO);
483 rv = close(newfile);
484 assert(rv == 0);
485
486 errno = EIO;
487 vperror("Old McDonald had a farm. %s", "EI EIO");
488
489 /* Restore stderr */
490 rv = dup2(oldstderr, STDERR_FILENO);
491 assert(rv == STDERR_FILENO);
492
493
494 /* Go read the file */
495 char buf[80] = { 0 };
496 FILE *efile = fopen(tmpl, "r");
497 assert(efile);
498 char *prv = fgets(buf, sizeof(buf), efile);
499 assert(prv);
500 fclose(efile);
501
502 unlink(tmpl);
503
504 char expected[80] = { 0 };
505 snprintf(expected, sizeof(expected),
506 "Old McDonald had a farm. EI EIO: %s\n", strerror(EIO));
507
508 /*
509 fprintf(stderr,
510 "\nExpected: ``%s''"
511 "\nGot: ``%s''\n", expected, buf);
512 */
513
514 return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
515 }
516
517 static void send_ascii_command(const char *buf) {
518 off_t offset = 0;
519 const char* ptr = buf;
520 size_t len = strlen(buf);
521
522 do {
523 ssize_t nw = write(sock, ptr + offset, len - offset);
524 if (nw == -1) {
525 if (errno != EINTR) {
526 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
527 abort();
528 }
529 } else {
530 offset += nw;
531 }
532 } while (offset < len);
533 }
534
535 /*
536 * This is a dead slow single byte read, but it should only read out
537 * _one_ response and I don't have an input buffer... The current
538 * implementation only supports single-line responses, so if you want to use
539 * it for get commands you need to implement that first ;-)
540 */
541 static void read_ascii_response(char *buffer, size_t size) {
542 off_t offset = 0;
543 bool need_more = true;
544 do {
545 ssize_t nr = read(sock, buffer + offset, 1);
546 if (nr == -1) {
547 if (errno != EINTR) {
548 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
549 abort();
550 }
551 } else {
552 assert(nr == 1);
553 if (buffer[offset] == '\n') {
554 need_more = false;
555 buffer[offset + 1] = '\0';
556 }
557 offset += nr;
558 assert(offset + 1 < size);
559 }
560 } while (need_more);
561 }
562
563 static enum test_return test_issue_92(void) {
564 char buffer[1024];
565
566 close(sock);
567 sock = connect_server("127.0.0.1", port, false);
568
569 send_ascii_command("stats cachedump 1 0 0\r\n");
570 read_ascii_response(buffer, sizeof(buffer));
571 assert(strncmp(buffer, "END", strlen("END")) == 0);
572
573 send_ascii_command("stats cachedump 200 0 0\r\n");
574 read_ascii_response(buffer, sizeof(buffer));
575 assert(strncmp(buffer, "CLIENT_ERROR", strlen("CLIENT_ERROR")) == 0);
576
577 close(sock);
578 sock = connect_server("127.0.0.1", port, false);
579 return TEST_PASS;
580 }
581
582 static enum test_return test_issue_102(void) {
583 char buffer[4096];
584 memset(buffer, ' ', sizeof(buffer));
585 buffer[sizeof(buffer) - 1] = '\0';
586
587 close(sock);
588 sock = connect_server("127.0.0.1", port, false);
589
590 send_ascii_command(buffer);
591 /* verify that the server closed the connection */
592 assert(read(sock, buffer, sizeof(buffer)) == 0);
593 close(sock);
594 sock = connect_server("127.0.0.1", port, false);
595
596 snprintf(buffer, sizeof(buffer), "gets ");
597 size_t offset = 5;
598 while (offset < 4000) {
599 offset += snprintf(buffer + offset, sizeof(buffer) - offset,
600 "%010u ", (unsigned int)offset);
601 }
602
603 send_ascii_command(buffer);
604 usleep(250);
605
606 send_ascii_command("\r\n");
607 char rsp[80];
608 read_ascii_response(rsp, sizeof(rsp));
609 assert(strncmp(rsp, "END", strlen("END")) == 0);
610 buffer[3]= ' ';
611 send_ascii_command(buffer);
612 usleep(250);
613 send_ascii_command("\r\n");
614 read_ascii_response(rsp, sizeof(rsp));
615 assert(strncmp(rsp, "END", strlen("END")) == 0);
616
617 memset(buffer, ' ', sizeof(buffer));
618 int len = snprintf(buffer + 101, sizeof(buffer) - 101, "gets foo");
619 buffer[101 + len] = ' ';
620 buffer[sizeof(buffer) - 1] = '\0';
621 send_ascii_command(buffer);
622 /* verify that the server closed the connection */
623 assert(read(sock, buffer, sizeof(buffer)) == 0);
624
625 close(sock);
626 sock = connect_server("127.0.0.1", port, false);
627
628 return TEST_PASS;
629 }
630
631 static enum test_return start_memcached_server(void) {
632 server_pid = start_server(&port, false, 600);
633 sock = connect_server("127.0.0.1", port, false);
634 return TEST_PASS;
635 }
636
637 static enum test_return stop_memcached_server(void) {
638 close(sock);
639 assert(kill(server_pid, SIGTERM) == 0);
640 return TEST_PASS;
641 }
642
643 static void safe_send(const void* buf, size_t len, bool hickup)
644 {
645 off_t offset = 0;
646 const char* ptr = buf;
647 #ifdef MESSAGE_DEBUG
648 uint8_t val = *ptr;
649 assert(val == (uint8_t)0x80);
650 fprintf(stderr, "About to send %lu bytes:", (unsigned long)len);
651 for (int ii = 0; ii < len; ++ii) {
652 if (ii % 4 == 0) {
653 fprintf(stderr, "\n ");
654 }
655 val = *(ptr + ii);
656 fprintf(stderr, " 0x%02x", val);
657 }
658 fprintf(stderr, "\n");
659 usleep(500);
660 #endif
661
662 do {
663 size_t num_bytes = len - offset;
664 if (hickup) {
665 if (num_bytes > 1024) {
666 num_bytes = (rand() % 1023) + 1;
667 }
668 }
669
670 ssize_t nw = write(sock, ptr + offset, num_bytes);
671 if (nw == -1) {
672 if (errno != EINTR) {
673 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
674 abort();
675 }
676 } else {
677 if (hickup) {
678 usleep(100);
679 }
680 offset += nw;
681 }
682 } while (offset < len);
683 }
684
685 static bool safe_recv(void *buf, size_t len) {
686 if (len == 0) {
687 return true;
688 }
689 off_t offset = 0;
690 do {
691 ssize_t nr = read(sock, ((char*)buf) + offset, len - offset);
692 if (nr == -1) {
693 if (errno != EINTR) {
694 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
695 abort();
696 }
697 } else {
698 if (nr == 0 && allow_closed_read) {
699 return false;
700 }
701 assert(nr != 0);
702 offset += nr;
703 }
704 } while (offset < len);
705
706 return true;
707 }
708
709 static bool safe_recv_packet(void *buf, size_t size) {
710 protocol_binary_response_no_extras *response = buf;
711 assert(size > sizeof(*response));
712 if (!safe_recv(response, sizeof(*response))) {
713 return false;
714 }
715 response->message.header.response.keylen = ntohs(response->message.header.response.keylen);
716 response->message.header.response.status = ntohs(response->message.header.response.status);
717 response->message.header.response.bodylen = ntohl(response->message.header.response.bodylen);
718
719 size_t len = sizeof(*response);
720
721 char *ptr = buf;
722 ptr += len;
723 if (!safe_recv(ptr, response->message.header.response.bodylen)) {
724 return false;
725 }
726
727 #ifdef MESSAGE_DEBUG
728 usleep(500);
729 ptr = buf;
730 len += response->message.header.response.bodylen;
731 uint8_t val = *ptr;
732 assert(val == (uint8_t)0x81);
733 fprintf(stderr, "Received %lu bytes:", (unsigned long)len);
734 for (int ii = 0; ii < len; ++ii) {
735 if (ii % 4 == 0) {
736 fprintf(stderr, "\n ");
737 }
738 val = *(ptr + ii);
739 fprintf(stderr, " 0x%02x", val);
740 }
741 fprintf(stderr, "\n");
742 #endif
743 return true;
744 }
745
746 static off_t storage_command(char*buf,
747 size_t bufsz,
748 uint8_t cmd,
749 const void* key,
750 size_t keylen,
751 const void* dta,
752 size_t dtalen,
753 uint32_t flags,
754 uint32_t exp) {
755 /* all of the storage commands use the same command layout */
756 protocol_binary_request_set *request = (void*)buf;
757 assert(bufsz > sizeof(*request) + keylen + dtalen);
758
759 memset(request, 0, sizeof(*request));
760 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
761 request->message.header.request.opcode = cmd;
762 request->message.header.request.keylen = htons(keylen);
763 request->message.header.request.extlen = 8;
764 request->message.header.request.bodylen = htonl(keylen + 8 + dtalen);
765 request->message.header.request.opaque = 0xdeadbeef;
766 request->message.body.flags = flags;
767 request->message.body.expiration = exp;
768
769 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 8;
770
771 memcpy(buf + key_offset, key, keylen);
772 if (dta != NULL) {
773 memcpy(buf + key_offset + keylen, dta, dtalen);
774 }
775
776 return key_offset + keylen + dtalen;
777 }
778
779 static off_t raw_command(char* buf,
780 size_t bufsz,
781 uint8_t cmd,
782 const void* key,
783 size_t keylen,
784 const void* dta,
785 size_t dtalen) {
786 /* all of the storage commands use the same command layout */
787 protocol_binary_request_no_extras *request = (void*)buf;
788 assert(bufsz > sizeof(*request) + keylen + dtalen);
789
790 memset(request, 0, sizeof(*request));
791 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
792 request->message.header.request.opcode = cmd;
793 request->message.header.request.keylen = htons(keylen);
794 request->message.header.request.bodylen = htonl(keylen + dtalen);
795 request->message.header.request.opaque = 0xdeadbeef;
796
797 off_t key_offset = sizeof(protocol_binary_request_no_extras);
798
799 if (key != NULL) {
800 memcpy(buf + key_offset, key, keylen);
801 }
802 if (dta != NULL) {
803 memcpy(buf + key_offset + keylen, dta, dtalen);
804 }
805
806 return sizeof(*request) + keylen + dtalen;
807 }
808
809 static off_t flush_command(char* buf, size_t bufsz, uint8_t cmd, uint32_t exptime, bool use_extra) {
810 protocol_binary_request_flush *request = (void*)buf;
811 assert(bufsz > sizeof(*request));
812
813 memset(request, 0, sizeof(*request));
814 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
815 request->message.header.request.opcode = cmd;
816
817 off_t size = sizeof(protocol_binary_request_no_extras);
818 if (use_extra) {
819 request->message.header.request.extlen = 4;
820 request->message.body.expiration = htonl(exptime);
821 request->message.header.request.bodylen = htonl(4);
822 size += 4;
823 }
824
825 request->message.header.request.opaque = 0xdeadbeef;
826
827 return size;
828 }
829
830
831 static off_t touch_command(char* buf,
832 size_t bufsz,
833 uint8_t cmd,
834 const void* key,
835 size_t keylen,
836 uint32_t exptime) {
837 protocol_binary_request_touch *request = (void*)buf;
838 assert(bufsz > sizeof(*request));
839
840 memset(request, 0, sizeof(*request));
841 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
842 request->message.header.request.opcode = cmd;
843
844 request->message.header.request.keylen = htons(keylen);
845 request->message.header.request.extlen = 4;
846 request->message.body.expiration = htonl(exptime);
847 request->message.header.request.bodylen = htonl(keylen + 4);
848
849 request->message.header.request.opaque = 0xdeadbeef;
850
851 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 4;
852
853 memcpy(buf + key_offset, key, keylen);
854 return sizeof(protocol_binary_request_no_extras) + 4 + keylen;
855 }
856
857 static off_t arithmetic_command(char* buf,
858 size_t bufsz,
859 uint8_t cmd,
860 const void* key,
861 size_t keylen,
862 uint64_t delta,
863 uint64_t initial,
864 uint32_t exp) {
865 protocol_binary_request_incr *request = (void*)buf;
866 assert(bufsz > sizeof(*request) + keylen);
867
868 memset(request, 0, sizeof(*request));
869 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
870 request->message.header.request.opcode = cmd;
871 request->message.header.request.keylen = htons(keylen);
872 request->message.header.request.extlen = 20;
873 request->message.header.request.bodylen = htonl(keylen + 20);
874 request->message.header.request.opaque = 0xdeadbeef;
875 request->message.body.delta = htonll(delta);
876 request->message.body.initial = htonll(initial);
877 request->message.body.expiration = htonl(exp);
878
879 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 20;
880
881 memcpy(buf + key_offset, key, keylen);
882 return key_offset + keylen;
883 }
884
885 static void validate_response_header(protocol_binary_response_no_extras *response,
886 uint8_t cmd, uint16_t status)
887 {
888 assert(response->message.header.response.magic == PROTOCOL_BINARY_RES);
889 assert(response->message.header.response.opcode == cmd);
890 assert(response->message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
891 assert(response->message.header.response.status == status);
892 assert(response->message.header.response.opaque == 0xdeadbeef);
893
894 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
895 switch (cmd) {
896 case PROTOCOL_BINARY_CMD_ADDQ:
897 case PROTOCOL_BINARY_CMD_APPENDQ:
898 case PROTOCOL_BINARY_CMD_DECREMENTQ:
899 case PROTOCOL_BINARY_CMD_DELETEQ:
900 case PROTOCOL_BINARY_CMD_FLUSHQ:
901 case PROTOCOL_BINARY_CMD_INCREMENTQ:
902 case PROTOCOL_BINARY_CMD_PREPENDQ:
903 case PROTOCOL_BINARY_CMD_QUITQ:
904 case PROTOCOL_BINARY_CMD_REPLACEQ:
905 case PROTOCOL_BINARY_CMD_SETQ:
906 assert("Quiet command shouldn't return on success" == NULL);
907 default:
908 break;
909 }
910
911 switch (cmd) {
912 case PROTOCOL_BINARY_CMD_ADD:
913 case PROTOCOL_BINARY_CMD_REPLACE:
914 case PROTOCOL_BINARY_CMD_SET:
915 case PROTOCOL_BINARY_CMD_APPEND:
916 case PROTOCOL_BINARY_CMD_PREPEND:
917 assert(response->message.header.response.keylen == 0);
918 assert(response->message.header.response.extlen == 0);
919 assert(response->message.header.response.bodylen == 0);
920 assert(response->message.header.response.cas != 0);
921 break;
922 case PROTOCOL_BINARY_CMD_FLUSH:
923 case PROTOCOL_BINARY_CMD_NOOP:
924 case PROTOCOL_BINARY_CMD_QUIT:
925 case PROTOCOL_BINARY_CMD_DELETE:
926 assert(response->message.header.response.keylen == 0);
927 assert(response->message.header.response.extlen == 0);
928 assert(response->message.header.response.bodylen == 0);
929 assert(response->message.header.response.cas == 0);
930 break;
931
932 case PROTOCOL_BINARY_CMD_DECREMENT:
933 case PROTOCOL_BINARY_CMD_INCREMENT:
934 assert(response->message.header.response.keylen == 0);
935 assert(response->message.header.response.extlen == 0);
936 assert(response->message.header.response.bodylen == 8);
937 assert(response->message.header.response.cas != 0);
938 break;
939
940 case PROTOCOL_BINARY_CMD_STAT:
941 assert(response->message.header.response.extlen == 0);
942 /* key and value exists in all packets except in the terminating */
943 assert(response->message.header.response.cas == 0);
944 break;
945
946 case PROTOCOL_BINARY_CMD_VERSION:
947 assert(response->message.header.response.keylen == 0);
948 assert(response->message.header.response.extlen == 0);
949 assert(response->message.header.response.bodylen != 0);
950 assert(response->message.header.response.cas == 0);
951 break;
952
953 case PROTOCOL_BINARY_CMD_GET:
954 case PROTOCOL_BINARY_CMD_GETQ:
955 assert(response->message.header.response.keylen == 0);
956 assert(response->message.header.response.extlen == 4);
957 assert(response->message.header.response.cas != 0);
958 break;
959
960 case PROTOCOL_BINARY_CMD_GETK:
961 case PROTOCOL_BINARY_CMD_GETKQ:
962 assert(response->message.header.response.keylen != 0);
963 assert(response->message.header.response.extlen == 4);
964 assert(response->message.header.response.cas != 0);
965 break;
966
967 default:
968 /* Undefined command code */
969 break;
970 }
971 } else {
972 assert(response->message.header.response.cas == 0);
973 assert(response->message.header.response.extlen == 0);
974 if (cmd != PROTOCOL_BINARY_CMD_GETK &&
975 cmd != PROTOCOL_BINARY_CMD_GATK) {
976 assert(response->message.header.response.keylen == 0);
977 }
978 }
979 }
980
981 static enum test_return test_binary_noop(void) {
982 union {
983 protocol_binary_request_no_extras request;
984 protocol_binary_response_no_extras response;
985 char bytes[1024];
986 } buffer;
987
988 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
989 PROTOCOL_BINARY_CMD_NOOP,
990 NULL, 0, NULL, 0);
991
992 safe_send(buffer.bytes, len, false);
993 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
994 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_NOOP,
995 PROTOCOL_BINARY_RESPONSE_SUCCESS);
996
997 return TEST_PASS;
998 }
999
1000 static enum test_return test_binary_quit_impl(uint8_t cmd) {
1001 union {
1002 protocol_binary_request_no_extras request;
1003 protocol_binary_response_no_extras response;
1004 char bytes[1024];
1005 } buffer;
1006 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1007 cmd, NULL, 0, NULL, 0);
1008
1009 safe_send(buffer.bytes, len, false);
1010 if (cmd == PROTOCOL_BINARY_CMD_QUIT) {
1011 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1012 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_QUIT,
1013 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1014 }
1015
1016 /* Socket should be closed now, read should return 0 */
1017 assert(read(sock, buffer.bytes, sizeof(buffer.bytes)) == 0);
1018 close(sock);
1019 sock = connect_server("127.0.0.1", port, false);
1020
1021 return TEST_PASS;
1022 }
1023
1024 static enum test_return test_binary_quit(void) {
1025 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
1026 }
1027
1028 static enum test_return test_binary_quitq(void) {
1029 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
1030 }
1031
1032 static enum test_return test_binary_set_impl(const char *key, uint8_t cmd) {
1033 union {
1034 protocol_binary_request_no_extras request;
1035 protocol_binary_response_no_extras response;
1036 char bytes[1024];
1037 } send, receive;
1038 uint64_t value = 0xdeadbeefdeadcafe;
1039 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1040 key, strlen(key), &value, sizeof(value),
1041 0, 0);
1042
1043 /* Set should work over and over again */
1044 int ii;
1045 for (ii = 0; ii < 10; ++ii) {
1046 safe_send(send.bytes, len, false);
1047 if (cmd == PROTOCOL_BINARY_CMD_SET) {
1048 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1049 validate_response_header(&receive.response, cmd,
1050 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1051 }
1052 }
1053
1054 if (cmd == PROTOCOL_BINARY_CMD_SETQ) {
1055 return test_binary_noop();
1056 }
1057
1058 send.request.message.header.request.cas = receive.response.message.header.response.cas;
1059 safe_send(send.bytes, len, false);
1060 if (cmd == PROTOCOL_BINARY_CMD_SET) {
1061 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1062 validate_response_header(&receive.response, cmd,
1063 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1064 assert(receive.response.message.header.response.cas != send.request.message.header.request.cas);
1065 } else {
1066 return test_binary_noop();
1067 }
1068
1069 return TEST_PASS;
1070 }
1071
1072 static enum test_return test_binary_set(void) {
1073 return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
1074 }
1075
1076 static enum test_return test_binary_setq(void) {
1077 return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
1078 }
1079
1080
1081 static enum test_return test_binary_add_impl(const char *key, uint8_t cmd) {
1082 uint64_t value = 0xdeadbeefdeadcafe;
1083 union {
1084 protocol_binary_request_no_extras request;
1085 protocol_binary_response_no_extras response;
1086 char bytes[1024];
1087 } send, receive;
1088 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd, key,
1089 strlen(key), &value, sizeof(value),
1090 0, 0);
1091
1092 /* Add should only work the first time */
1093 int ii;
1094 for (ii = 0; ii < 10; ++ii) {
1095 safe_send(send.bytes, len, false);
1096 if (ii == 0) {
1097 if (cmd == PROTOCOL_BINARY_CMD_ADD) {
1098 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1099 validate_response_header(&receive.response, cmd,
1100 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1101 }
1102 } else {
1103 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1104 validate_response_header(&receive.response, cmd,
1105 PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1106 }
1107 }
1108
1109 return TEST_PASS;
1110 }
1111
1112 static enum test_return test_binary_add(void) {
1113 return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
1114 }
1115
1116 static enum test_return test_binary_addq(void) {
1117 return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
1118 }
1119
1120 static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd) {
1121 uint64_t value = 0xdeadbeefdeadcafe;
1122 union {
1123 protocol_binary_request_no_extras request;
1124 protocol_binary_response_no_extras response;
1125 char bytes[1024];
1126 } send, receive;
1127 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1128 key, strlen(key), &value, sizeof(value),
1129 0, 0);
1130 safe_send(send.bytes, len, false);
1131 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1132 validate_response_header(&receive.response, cmd,
1133 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1134 len = storage_command(send.bytes, sizeof(send.bytes),
1135 PROTOCOL_BINARY_CMD_ADD,
1136 key, strlen(key), &value, sizeof(value), 0, 0);
1137 safe_send(send.bytes, len, false);
1138 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1139 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1140 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1141
1142 len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1143 key, strlen(key), &value, sizeof(value), 0, 0);
1144 int ii;
1145 for (ii = 0; ii < 10; ++ii) {
1146 safe_send(send.bytes, len, false);
1147 if (cmd == PROTOCOL_BINARY_CMD_REPLACE) {
1148 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1149 validate_response_header(&receive.response,
1150 PROTOCOL_BINARY_CMD_REPLACE,
1151 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1152 }
1153 }
1154
1155 if (cmd == PROTOCOL_BINARY_CMD_REPLACEQ) {
1156 test_binary_noop();
1157 }
1158
1159 return TEST_PASS;
1160 }
1161
1162 static enum test_return test_binary_replace(void) {
1163 return test_binary_replace_impl("test_binary_replace",
1164 PROTOCOL_BINARY_CMD_REPLACE);
1165 }
1166
1167 static enum test_return test_binary_replaceq(void) {
1168 return test_binary_replace_impl("test_binary_replaceq",
1169 PROTOCOL_BINARY_CMD_REPLACEQ);
1170 }
1171
1172 static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd) {
1173 union {
1174 protocol_binary_request_no_extras request;
1175 protocol_binary_response_no_extras response;
1176 char bytes[1024];
1177 } send, receive;
1178 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1179 key, strlen(key), NULL, 0);
1180
1181 safe_send(send.bytes, len, false);
1182 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1183 validate_response_header(&receive.response, cmd,
1184 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1185 len = storage_command(send.bytes, sizeof(send.bytes),
1186 PROTOCOL_BINARY_CMD_ADD,
1187 key, strlen(key), NULL, 0, 0, 0);
1188 safe_send(send.bytes, len, false);
1189 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1190 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1191 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1192
1193 len = raw_command(send.bytes, sizeof(send.bytes),
1194 cmd, key, strlen(key), NULL, 0);
1195 safe_send(send.bytes, len, false);
1196
1197 if (cmd == PROTOCOL_BINARY_CMD_DELETE) {
1198 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1199 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_DELETE,
1200 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1201 }
1202
1203 safe_send(send.bytes, len, false);
1204 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1205 validate_response_header(&receive.response, cmd,
1206 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1207
1208 return TEST_PASS;
1209 }
1210
1211 static enum test_return test_binary_delete(void) {
1212 return test_binary_delete_impl("test_binary_delete",
1213 PROTOCOL_BINARY_CMD_DELETE);
1214 }
1215
1216 static enum test_return test_binary_deleteq(void) {
1217 return test_binary_delete_impl("test_binary_deleteq",
1218 PROTOCOL_BINARY_CMD_DELETEQ);
1219 }
1220
1221 static enum test_return test_binary_get_impl(const char *key, uint8_t cmd) {
1222 union {
1223 protocol_binary_request_no_extras request;
1224 protocol_binary_response_no_extras response;
1225 char bytes[1024];
1226 } send, receive;
1227 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1228 key, strlen(key), NULL, 0);
1229
1230 safe_send(send.bytes, len, false);
1231 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1232 validate_response_header(&receive.response, cmd,
1233 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1234
1235 len = storage_command(send.bytes, sizeof(send.bytes),
1236 PROTOCOL_BINARY_CMD_ADD,
1237 key, strlen(key), NULL, 0,
1238 0, 0);
1239 safe_send(send.bytes, len, false);
1240 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1241 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1242 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1243
1244 /* run a little pipeline test ;-) */
1245 len = 0;
1246 int ii;
1247 for (ii = 0; ii < 10; ++ii) {
1248 union {
1249 protocol_binary_request_no_extras request;
1250 char bytes[1024];
1251 } temp;
1252 size_t l = raw_command(temp.bytes, sizeof(temp.bytes),
1253 cmd, key, strlen(key), NULL, 0);
1254 memcpy(send.bytes + len, temp.bytes, l);
1255 len += l;
1256 }
1257
1258 safe_send(send.bytes, len, false);
1259 for (ii = 0; ii < 10; ++ii) {
1260 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1261 validate_response_header(&receive.response, cmd,
1262 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1263 }
1264
1265 return TEST_PASS;
1266 }
1267
1268 static enum test_return test_binary_get(void) {
1269 return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
1270 }
1271
1272 static enum test_return test_binary_getk(void) {
1273 return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
1274 }
1275
1276 static enum test_return test_binary_getq_impl(const char *key, uint8_t cmd) {
1277 const char *missing = "test_binary_getq_missing";
1278 union {
1279 protocol_binary_request_no_extras request;
1280 protocol_binary_response_no_extras response;
1281 char bytes[1024];
1282 } send, temp, receive;
1283 size_t len = storage_command(send.bytes, sizeof(send.bytes),
1284 PROTOCOL_BINARY_CMD_ADD,
1285 key, strlen(key), NULL, 0,
1286 0, 0);
1287 size_t len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
1288 missing, strlen(missing), NULL, 0);
1289 /* I need to change the first opaque so that I can separate the two
1290 * return packets */
1291 temp.request.message.header.request.opaque = 0xfeedface;
1292 memcpy(send.bytes + len, temp.bytes, len2);
1293 len += len2;
1294
1295 len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
1296 key, strlen(key), NULL, 0);
1297 memcpy(send.bytes + len, temp.bytes, len2);
1298 len += len2;
1299
1300 safe_send(send.bytes, len, false);
1301 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1302 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1303 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1304 /* The first GETQ shouldn't return anything */
1305 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1306 validate_response_header(&receive.response, cmd,
1307 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1308
1309 return TEST_PASS;
1310 }
1311
1312 static enum test_return test_binary_getq(void) {
1313 return test_binary_getq_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
1314 }
1315
1316 static enum test_return test_binary_getkq(void) {
1317 return test_binary_getq_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
1318 }
1319
1320 static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd) {
1321 union {
1322 protocol_binary_request_no_extras request;
1323 protocol_binary_response_no_extras response_header;
1324 protocol_binary_response_incr response;
1325 char bytes[1024];
1326 } send, receive;
1327 size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1328 key, strlen(key), 1, 0, 0);
1329
1330 int ii;
1331 for (ii = 0; ii < 10; ++ii) {
1332 safe_send(send.bytes, len, false);
1333 if (cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
1334 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1335 validate_response_header(&receive.response_header, cmd,
1336 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1337 assert(ntohll(receive.response.message.body.value) == ii);
1338 }
1339 }
1340
1341 if (cmd == PROTOCOL_BINARY_CMD_INCREMENTQ) {
1342 test_binary_noop();
1343 }
1344 return TEST_PASS;
1345 }
1346
1347 static enum test_return test_binary_incr(void) {
1348 return test_binary_incr_impl("test_binary_incr",
1349 PROTOCOL_BINARY_CMD_INCREMENT);
1350 }
1351
1352 static enum test_return test_binary_incrq(void) {
1353 return test_binary_incr_impl("test_binary_incrq",
1354 PROTOCOL_BINARY_CMD_INCREMENTQ);
1355 }
1356
1357 static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd) {
1358 union {
1359 protocol_binary_request_no_extras request;
1360 protocol_binary_response_no_extras response_header;
1361 protocol_binary_response_decr response;
1362 char bytes[1024];
1363 } send, receive;
1364 size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1365 key, strlen(key), 1, 9, 0);
1366
1367 int ii;
1368 for (ii = 9; ii >= 0; --ii) {
1369 safe_send(send.bytes, len, false);
1370 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1371 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1372 validate_response_header(&receive.response_header, cmd,
1373 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1374 assert(ntohll(receive.response.message.body.value) == ii);
1375 }
1376 }
1377
1378 /* decr on 0 should not wrap */
1379 safe_send(send.bytes, len, false);
1380 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1381 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1382 validate_response_header(&receive.response_header, cmd,
1383 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1384 assert(ntohll(receive.response.message.body.value) == 0);
1385 } else {
1386 test_binary_noop();
1387 }
1388
1389 return TEST_PASS;
1390 }
1391
1392 static enum test_return test_binary_decr(void) {
1393 return test_binary_decr_impl("test_binary_decr",
1394 PROTOCOL_BINARY_CMD_DECREMENT);
1395 }
1396
1397 static enum test_return test_binary_decrq(void) {
1398 return test_binary_decr_impl("test_binary_decrq",
1399 PROTOCOL_BINARY_CMD_DECREMENTQ);
1400 }
1401
1402 static enum test_return test_binary_version(void) {
1403 union {
1404 protocol_binary_request_no_extras request;
1405 protocol_binary_response_no_extras response;
1406 char bytes[1024];
1407 } buffer;
1408
1409 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1410 PROTOCOL_BINARY_CMD_VERSION,
1411 NULL, 0, NULL, 0);
1412
1413 safe_send(buffer.bytes, len, false);
1414 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1415 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_VERSION,
1416 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1417
1418 return TEST_PASS;
1419 }
1420
1421 static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd) {
1422 union {
1423 protocol_binary_request_no_extras request;
1424 protocol_binary_response_no_extras response;
1425 char bytes[1024];
1426 } send, receive;
1427
1428 size_t len = storage_command(send.bytes, sizeof(send.bytes),
1429 PROTOCOL_BINARY_CMD_ADD,
1430 key, strlen(key), NULL, 0, 0, 0);
1431 safe_send(send.bytes, len, false);
1432 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1433 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1434 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1435
1436 len = flush_command(send.bytes, sizeof(send.bytes), cmd, 2, true);
1437 safe_send(send.bytes, len, false);
1438 if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1439 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1440 validate_response_header(&receive.response, cmd,
1441 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1442 }
1443
1444 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GET,
1445 key, strlen(key), NULL, 0);
1446 safe_send(send.bytes, len, false);
1447 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1448 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1449 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1450
1451 sleep(2);
1452 safe_send(send.bytes, len, false);
1453 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1454 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1455 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1456
1457 int ii;
1458 for (ii = 0; ii < 2; ++ii) {
1459 len = storage_command(send.bytes, sizeof(send.bytes),
1460 PROTOCOL_BINARY_CMD_ADD,
1461 key, strlen(key), NULL, 0, 0, 0);
1462 safe_send(send.bytes, len, false);
1463 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1464 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1465 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1466
1467 len = flush_command(send.bytes, sizeof(send.bytes), cmd, 0, ii == 0);
1468 safe_send(send.bytes, len, false);
1469 if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1470 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1471 validate_response_header(&receive.response, cmd,
1472 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1473 }
1474
1475 len = raw_command(send.bytes, sizeof(send.bytes),
1476 PROTOCOL_BINARY_CMD_GET,
1477 key, strlen(key), NULL, 0);
1478 safe_send(send.bytes, len, false);
1479 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1480 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1481 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1482 }
1483
1484 return TEST_PASS;
1485 }
1486
1487 static enum test_return test_binary_flush(void) {
1488 return test_binary_flush_impl("test_binary_flush",
1489 PROTOCOL_BINARY_CMD_FLUSH);
1490 }
1491
1492 static enum test_return test_binary_flushq(void) {
1493 return test_binary_flush_impl("test_binary_flushq",
1494 PROTOCOL_BINARY_CMD_FLUSHQ);
1495 }
1496
1497 static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd) {
1498 union {
1499 protocol_binary_request_no_extras request;
1500 protocol_binary_response_no_extras response;
1501 char bytes[1024];
1502 } send, receive;
1503 const char *value = "world";
1504
1505 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1506 key, strlen(key), value, strlen(value));
1507
1508
1509 safe_send(send.bytes, len, false);
1510 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1511 validate_response_header(&receive.response, cmd,
1512 PROTOCOL_BINARY_RESPONSE_NOT_STORED);
1513
1514 len = storage_command(send.bytes, sizeof(send.bytes),
1515 PROTOCOL_BINARY_CMD_ADD,
1516 key, strlen(key), value, strlen(value), 0, 0);
1517 safe_send(send.bytes, len, false);
1518 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1519 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1520 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1521
1522 len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1523 key, strlen(key), value, strlen(value));
1524 safe_send(send.bytes, len, false);
1525
1526 if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND) {
1527 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1528 validate_response_header(&receive.response, cmd,
1529 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1530 } else {
1531 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_NOOP,
1532 NULL, 0, NULL, 0);
1533 safe_send(send.bytes, len, false);
1534 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1535 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_NOOP,
1536 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1537 }
1538
1539 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GETK,
1540 key, strlen(key), NULL, 0);
1541
1542 safe_send(send.bytes, len, false);
1543 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1544 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GETK,
1545 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1546
1547 assert(receive.response.message.header.response.keylen == strlen(key));
1548 assert(receive.response.message.header.response.bodylen == (strlen(key) + 2*strlen(value) + 4));
1549
1550 char *ptr = receive.bytes;
1551 ptr += sizeof(receive.response);
1552 ptr += 4;
1553
1554 assert(memcmp(ptr, key, strlen(key)) == 0);
1555 ptr += strlen(key);
1556 assert(memcmp(ptr, value, strlen(value)) == 0);
1557 ptr += strlen(value);
1558 assert(memcmp(ptr, value, strlen(value)) == 0);
1559
1560 return TEST_PASS;
1561 }
1562
1563 static enum test_return test_binary_append(void) {
1564 return test_binary_concat_impl("test_binary_append",
1565 PROTOCOL_BINARY_CMD_APPEND);
1566 }
1567
1568 static enum test_return test_binary_prepend(void) {
1569 return test_binary_concat_impl("test_binary_prepend",
1570 PROTOCOL_BINARY_CMD_PREPEND);
1571 }
1572
1573 static enum test_return test_binary_appendq(void) {
1574 return test_binary_concat_impl("test_binary_appendq",
1575 PROTOCOL_BINARY_CMD_APPENDQ);
1576 }
1577
1578 static enum test_return test_binary_prependq(void) {
1579 return test_binary_concat_impl("test_binary_prependq",
1580 PROTOCOL_BINARY_CMD_PREPENDQ);
1581 }
1582
1583 static enum test_return test_binary_stat(void) {
1584 union {
1585 protocol_binary_request_no_extras request;
1586 protocol_binary_response_no_extras response;
1587 char bytes[1024];
1588 } buffer;
1589
1590 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1591 PROTOCOL_BINARY_CMD_STAT,
1592 NULL, 0, NULL, 0);
1593
1594 safe_send(buffer.bytes, len, false);
1595 do {
1596 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1597 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_STAT,
1598 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1599 } while (buffer.response.message.header.response.keylen != 0);
1600
1601 return TEST_PASS;
1602 }
1603
1604 static enum test_return test_binary_illegal(void) {
1605 uint8_t cmd = 0x25;
1606 while (cmd != 0x00) {
1607 union {
1608 protocol_binary_request_no_extras request;
1609 protocol_binary_response_no_extras response;
1610 char bytes[1024];
1611 } buffer;
1612 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1613 cmd, NULL, 0, NULL, 0);
1614 safe_send(buffer.bytes, len, false);
1615 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1616 validate_response_header(&buffer.response, cmd,
1617 PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
1618 ++cmd;
1619 }
1620
1621 return TEST_PASS;
1622 }
1623
1624 volatile bool hickup_thread_running;
1625
1626 static void *binary_hickup_recv_verification_thread(void *arg) {
1627 protocol_binary_response_no_extras *response = malloc(65*1024);
1628 if (response != NULL) {
1629 while (safe_recv_packet(response, 65*1024)) {
1630 /* Just validate the packet format */
1631 validate_response_header(response,
1632 response->message.header.response.opcode,
1633 response->message.header.response.status);
1634 }
1635 free(response);
1636 }
1637 hickup_thread_running = false;
1638 allow_closed_read = false;
1639 return NULL;
1640 }
1641
1642 static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t buffersize) {
1643 off_t offset = 0;
1644 char *key[256];
1645 uint64_t value = 0xfeedfacedeadbeef;
1646
1647 while (hickup_thread_running &&
1648 offset + sizeof(protocol_binary_request_no_extras) < buffersize) {
1649 union {
1650 protocol_binary_request_no_extras request;
1651 char bytes[65 * 1024];
1652 } command;
1653 uint8_t cmd = (uint8_t)(rand() & 0xff);
1654 size_t len;
1655 size_t keylen = (rand() % 250) + 1;
1656
1657 switch (cmd) {
1658 case PROTOCOL_BINARY_CMD_ADD:
1659 case PROTOCOL_BINARY_CMD_ADDQ:
1660 case PROTOCOL_BINARY_CMD_REPLACE:
1661 case PROTOCOL_BINARY_CMD_REPLACEQ:
1662 case PROTOCOL_BINARY_CMD_SET:
1663 case PROTOCOL_BINARY_CMD_SETQ:
1664 len = storage_command(command.bytes, sizeof(command.bytes), cmd,
1665 key, keylen , &value, sizeof(value),
1666 0, 0);
1667 break;
1668 case PROTOCOL_BINARY_CMD_APPEND:
1669 case PROTOCOL_BINARY_CMD_APPENDQ:
1670 case PROTOCOL_BINARY_CMD_PREPEND:
1671 case PROTOCOL_BINARY_CMD_PREPENDQ:
1672 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1673 key, keylen, &value, sizeof(value));
1674 break;
1675 case PROTOCOL_BINARY_CMD_FLUSH:
1676 case PROTOCOL_BINARY_CMD_FLUSHQ:
1677 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1678 NULL, 0, NULL, 0);
1679 break;
1680 case PROTOCOL_BINARY_CMD_NOOP:
1681 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1682 NULL, 0, NULL, 0);
1683 break;
1684 case PROTOCOL_BINARY_CMD_DELETE:
1685 case PROTOCOL_BINARY_CMD_DELETEQ:
1686 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1687 key, keylen, NULL, 0);
1688 break;
1689 case PROTOCOL_BINARY_CMD_DECREMENT:
1690 case PROTOCOL_BINARY_CMD_DECREMENTQ:
1691 case PROTOCOL_BINARY_CMD_INCREMENT:
1692 case PROTOCOL_BINARY_CMD_INCREMENTQ:
1693 len = arithmetic_command(command.bytes, sizeof(command.bytes), cmd,
1694 key, keylen, 1, 0, 0);
1695 break;
1696 case PROTOCOL_BINARY_CMD_VERSION:
1697 len = raw_command(command.bytes, sizeof(command.bytes),
1698 PROTOCOL_BINARY_CMD_VERSION,
1699 NULL, 0, NULL, 0);
1700 break;
1701 case PROTOCOL_BINARY_CMD_GET:
1702 case PROTOCOL_BINARY_CMD_GETK:
1703 case PROTOCOL_BINARY_CMD_GETKQ:
1704 case PROTOCOL_BINARY_CMD_GETQ:
1705 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1706 key, keylen, NULL, 0);
1707 break;
1708
1709 case PROTOCOL_BINARY_CMD_TOUCH:
1710 case PROTOCOL_BINARY_CMD_GAT:
1711 case PROTOCOL_BINARY_CMD_GATQ:
1712 case PROTOCOL_BINARY_CMD_GATK:
1713 case PROTOCOL_BINARY_CMD_GATKQ:
1714 len = touch_command(command.bytes, sizeof(command.bytes), cmd,
1715 key, keylen, 10);
1716 break;
1717
1718 case PROTOCOL_BINARY_CMD_STAT:
1719 len = raw_command(command.bytes, sizeof(command.bytes),
1720 PROTOCOL_BINARY_CMD_STAT,
1721 NULL, 0, NULL, 0);
1722 break;
1723
1724 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
1725 case PROTOCOL_BINARY_CMD_SASL_AUTH:
1726 case PROTOCOL_BINARY_CMD_SASL_STEP:
1727 /* Ignoring SASL */
1728 case PROTOCOL_BINARY_CMD_QUITQ:
1729 case PROTOCOL_BINARY_CMD_QUIT:
1730 /* I don't want to pass on the quit commands ;-) */
1731 cmd |= 0xf0;
1732 /* FALLTHROUGH */
1733 default:
1734 len = raw_command(command.bytes, sizeof(command.bytes),
1735 cmd, NULL, 0, NULL, 0);
1736 }
1737
1738 if ((len + offset) < buffersize) {
1739 memcpy(((char*)buffer) + offset, command.bytes, len);
1740 offset += len;
1741 } else {
1742 break;
1743 }
1744 }
1745 safe_send(buffer, offset, true);
1746
1747 return TEST_PASS;
1748 }
1749
1750 static enum test_return test_binary_pipeline_hickup(void)
1751 {
1752 size_t buffersize = 65 * 1024;
1753 void *buffer = malloc(buffersize);
1754 int ii;
1755
1756 pthread_t tid;
1757 int ret;
1758 allow_closed_read = true;
1759 hickup_thread_running = true;
1760 if ((ret = pthread_create(&tid, NULL,
1761 binary_hickup_recv_verification_thread, NULL)) != 0) {
1762 fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
1763 return TEST_FAIL;
1764 }
1765
1766 /* Allow the thread to start */
1767 usleep(250);
1768
1769 srand((int)time(NULL));
1770 for (ii = 0; ii < 2; ++ii) {
1771 test_binary_pipeline_hickup_chunk(buffer, buffersize);
1772 }
1773
1774 /* send quitq to shut down the read thread ;-) */
1775 size_t len = raw_command(buffer, buffersize, PROTOCOL_BINARY_CMD_QUITQ,
1776 NULL, 0, NULL, 0);
1777 safe_send(buffer, len, false);
1778
1779 pthread_join(tid, NULL);
1780 free(buffer);
1781 return TEST_PASS;
1782 }
1783
1784
1785 static enum test_return test_issue_101(void) {
1786 const int max = 2;
1787 enum test_return ret = TEST_PASS;
1788 int fds[max];
1789 int ii = 0;
1790 pid_t child = 0;
1791
1792 if (getenv("SKIP_TEST_101") != NULL) {
1793 return TEST_SKIP;
1794 }
1795
1796 const char *command = "stats\r\nstats\r\nstats\r\nstats\r\nstats\r\n";
1797 size_t cmdlen = strlen(command);
1798
1799 server_pid = start_server(&port, false, 1000);
1800
1801 for (ii = 0; ii < max; ++ii) {
1802 fds[ii] = connect_server("127.0.0.1", port, true);
1803 assert(fds[ii] > 0);
1804 }
1805
1806 /* Send command on the connection until it blocks */
1807 for (ii = 0; ii < max; ++ii) {
1808 bool more = true;
1809 do {
1810 ssize_t err = write(fds[ii], command, cmdlen);
1811 if (err == -1) {
1812 switch (errno) {
1813 case EINTR:
1814 break;
1815 case ENOMEM:
1816 case EWOULDBLOCK:
1817 more = false;
1818 break;
1819 default:
1820 ret = TEST_FAIL;
1821 goto cleanup;
1822 }
1823 }
1824 } while (more);
1825 }
1826
1827 child = fork();
1828 if (child == (pid_t)-1) {
1829 abort();
1830 } else if (child > 0) {
1831 int stat;
1832 pid_t c;
1833 while ((c = waitpid(child, &stat, 0)) == (pid_t)-1 && errno == EINTR);
1834 assert(c == child);
1835 assert(stat == 0);
1836 } else {
1837 sock = connect_server("127.0.0.1", port, false);
1838 ret = test_binary_noop();
1839 close(sock);
1840 exit(0);
1841 }
1842
1843 cleanup:
1844 /* close all connections */
1845 for (ii = 0; ii < max; ++ii) {
1846 close(fds[ii]);
1847 }
1848
1849 assert(kill(server_pid, SIGTERM) == 0);
1850
1851 return ret;
1852 }
1853
1854 typedef enum test_return (*TEST_FUNC)(void);
1855 struct testcase {
1856 const char *description;
1857 TEST_FUNC function;
1858 };
1859
1860 struct testcase testcases[] = {
1861 { "cache_create", cache_create_test },
1862 { "cache_constructor", cache_constructor_test },
1863 { "cache_constructor_fail", cache_fail_constructor_test },
1864 { "cache_destructor", cache_destructor_test },
1865 { "cache_reuse", cache_reuse_test },
1866 { "cache_redzone", cache_redzone_test },
1867 { "issue_161", test_issue_161 },
1868 { "strtol", test_safe_strtol },
1869 { "strtoll", test_safe_strtoll },
1870 { "strtoul", test_safe_strtoul },
1871 { "strtoull", test_safe_strtoull },
1872 { "issue_44", test_issue_44 },
1873 { "vperror", test_vperror },
1874 { "issue_101", test_issue_101 },
1875 /* The following tests all run towards the same server */
1876 { "start_server", start_memcached_server },
1877 { "issue_92", test_issue_92 },
1878 { "issue_102", test_issue_102 },
1879 { "binary_noop", test_binary_noop },
1880 { "binary_quit", test_binary_quit },
1881 { "binary_quitq", test_binary_quitq },
1882 { "binary_set", test_binary_set },
1883 { "binary_setq", test_binary_setq },
1884 { "binary_add", test_binary_add },
1885 { "binary_addq", test_binary_addq },
1886 { "binary_replace", test_binary_replace },
1887 { "binary_replaceq", test_binary_replaceq },
1888 { "binary_delete", test_binary_delete },
1889 { "binary_deleteq", test_binary_deleteq },
1890 { "binary_get", test_binary_get },
1891 { "binary_getq", test_binary_getq },
1892 { "binary_getk", test_binary_getk },
1893 { "binary_getkq", test_binary_getkq },
1894 { "binary_incr", test_binary_incr },
1895 { "binary_incrq", test_binary_incrq },
1896 { "binary_decr", test_binary_decr },
1897 { "binary_decrq", test_binary_decrq },
1898 { "binary_version", test_binary_version },
1899 { "binary_flush", test_binary_flush },
1900 { "binary_flushq", test_binary_flushq },
1901 { "binary_append", test_binary_append },
1902 { "binary_appendq", test_binary_appendq },
1903 { "binary_prepend", test_binary_prepend },
1904 { "binary_prependq", test_binary_prependq },
1905 { "binary_stat", test_binary_stat },
1906 { "binary_illegal", test_binary_illegal },
1907 { "binary_pipeline_hickup", test_binary_pipeline_hickup },
1908 { "stop_server", stop_memcached_server },
1909 { NULL, NULL }
1910 };
1911
1912 int main(int argc, char **argv)
1913 {
1914 int exitcode = 0;
1915 int ii = 0, num_cases = 0;
1916
1917 for (num_cases = 0; testcases[num_cases].description; num_cases++) {
1918 /* Just counting */
1919 }
1920
1921 printf("1..%d\n", num_cases);
1922
1923 for (ii = 0; testcases[ii].description != NULL; ++ii) {
1924 fflush(stdout);
1925 #ifndef DEBUG
1926 /* the test program shouldn't run longer than 10 minutes... */
1927 alarm(600);
1928 #endif
1929 enum test_return ret = testcases[ii].function();
1930 if (ret == TEST_SKIP) {
1931 fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
1932 } else if (ret == TEST_PASS) {
1933 fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
1934 } else {
1935 fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
1936 exitcode = 1;
1937 }
1938 fflush(stdout);
1939 }
1940
1941 return exitcode;
1942 }