aba0b90519b78d9726be67ebaf1a1083ecae4bb8
[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, but I'll take the chance....
386 */
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 assert(fgets(buffer, sizeof(buffer), fp) != NULL);
398 fclose(fp);
399
400 int32_t val;
401 assert(safe_strtol(buffer, &val));
402 pid = (pid_t)val;
403 }
404
405 return pid;
406 }
407
408 static enum test_return test_issue_44(void) {
409 in_port_t port;
410 pid_t pid = start_server(&port, true, 15);
411 assert(kill(pid, SIGHUP) == 0);
412 sleep(1);
413 assert(kill(pid, SIGTERM) == 0);
414
415 return TEST_PASS;
416 }
417
418 static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
419 {
420 struct addrinfo *ai = 0;
421 struct addrinfo hints = { .ai_family = AF_UNSPEC,
422 .ai_protocol = IPPROTO_TCP,
423 .ai_socktype = SOCK_STREAM };
424 char service[NI_MAXSERV];
425 int error;
426
427 (void)snprintf(service, NI_MAXSERV, "%d", port);
428 if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
429 if (error != EAI_SYSTEM) {
430 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
431 } else {
432 perror("getaddrinfo()");
433 }
434 }
435
436 return ai;
437 }
438
439 static int connect_server(const char *hostname, in_port_t port, bool nonblock)
440 {
441 struct addrinfo *ai = lookuphost(hostname, port);
442 int sock = -1;
443 if (ai != NULL) {
444 if ((sock = socket(ai->ai_family, ai->ai_socktype,
445 ai->ai_protocol)) != -1) {
446 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
447 fprintf(stderr, "Failed to connect socket: %s\n",
448 strerror(errno));
449 close(sock);
450 sock = -1;
451 } else if (nonblock) {
452 int flags = fcntl(sock, F_GETFL, 0);
453 if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
454 fprintf(stderr, "Failed to enable nonblocking mode: %s\n",
455 strerror(errno));
456 close(sock);
457 sock = -1;
458 }
459 }
460 } else {
461 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
462 }
463
464 freeaddrinfo(ai);
465 }
466 return sock;
467 }
468
469 static enum test_return test_vperror(void) {
470 int rv = 0;
471 int oldstderr = dup(STDERR_FILENO);
472 char tmpl[sizeof(TMP_TEMPLATE)+1];
473 strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
474
475 int newfile = mkstemp(tmpl);
476 assert(newfile > 0);
477 rv = dup2(newfile, STDERR_FILENO);
478 assert(rv == STDERR_FILENO);
479 rv = close(newfile);
480 assert(rv == 0);
481
482 errno = EIO;
483 vperror("Old McDonald had a farm. %s", "EI EIO");
484
485 /* Restore stderr */
486 rv = dup2(oldstderr, STDERR_FILENO);
487 assert(rv == STDERR_FILENO);
488
489
490 /* Go read the file */
491 char buf[80] = { 0 };
492 FILE *efile = fopen(tmpl, "r");
493 assert(efile);
494 char *prv = fgets(buf, sizeof(buf), efile);
495 assert(prv);
496 fclose(efile);
497
498 unlink(tmpl);
499
500 char expected[80] = { 0 };
501 snprintf(expected, sizeof(expected),
502 "Old McDonald had a farm. EI EIO: %s\n", strerror(EIO));
503
504 /*
505 fprintf(stderr,
506 "\nExpected: ``%s''"
507 "\nGot: ``%s''\n", expected, buf);
508 */
509
510 return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
511 }
512
513 static void send_ascii_command(const char *buf) {
514 off_t offset = 0;
515 const char* ptr = buf;
516 size_t len = strlen(buf);
517
518 do {
519 ssize_t nw = write(sock, ptr + offset, len - offset);
520 if (nw == -1) {
521 if (errno != EINTR) {
522 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
523 abort();
524 }
525 } else {
526 offset += nw;
527 }
528 } while (offset < len);
529 }
530
531 /*
532 * This is a dead slow single byte read, but it should only read out
533 * _one_ response and I don't have an input buffer... The current
534 * implementation only supports single-line responses, so if you want to use
535 * it for get commands you need to implement that first ;-)
536 */
537 static void read_ascii_response(char *buffer, size_t size) {
538 off_t offset = 0;
539 bool need_more = true;
540 do {
541 ssize_t nr = read(sock, buffer + offset, 1);
542 if (nr == -1) {
543 if (errno != EINTR) {
544 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
545 abort();
546 }
547 } else {
548 assert(nr == 1);
549 if (buffer[offset] == '\n') {
550 need_more = false;
551 buffer[offset + 1] = '\0';
552 }
553 offset += nr;
554 assert(offset + 1 < size);
555 }
556 } while (need_more);
557 }
558
559 static enum test_return test_issue_92(void) {
560 char buffer[1024];
561
562 close(sock);
563 sock = connect_server("127.0.0.1", port, false);
564
565 send_ascii_command("stats cachedump 1 0 0\r\n");
566 read_ascii_response(buffer, sizeof(buffer));
567 assert(strncmp(buffer, "END", strlen("END")) == 0);
568
569 send_ascii_command("stats cachedump 200 0 0\r\n");
570 read_ascii_response(buffer, sizeof(buffer));
571 assert(strncmp(buffer, "CLIENT_ERROR", strlen("CLIENT_ERROR")) == 0);
572
573 close(sock);
574 sock = connect_server("127.0.0.1", port, false);
575 return TEST_PASS;
576 }
577
578 static enum test_return test_issue_102(void) {
579 char buffer[4096];
580 memset(buffer, ' ', sizeof(buffer));
581 buffer[sizeof(buffer) - 1] = '\0';
582
583 close(sock);
584 sock = connect_server("127.0.0.1", port, false);
585
586 send_ascii_command(buffer);
587 /* verify that the server closed the connection */
588 assert(read(sock, buffer, sizeof(buffer)) == 0);
589 close(sock);
590 sock = connect_server("127.0.0.1", port, false);
591
592 snprintf(buffer, sizeof(buffer), "gets ");
593 size_t offset = 5;
594 while (offset < 4000) {
595 offset += snprintf(buffer + offset, sizeof(buffer) - offset,
596 "%010u ", (unsigned int)offset);
597 }
598
599 send_ascii_command(buffer);
600 usleep(250);
601
602 send_ascii_command("\r\n");
603 char rsp[80];
604 read_ascii_response(rsp, sizeof(rsp));
605 assert(strncmp(rsp, "END", strlen("END")) == 0);
606 buffer[3]= ' ';
607 send_ascii_command(buffer);
608 usleep(250);
609 send_ascii_command("\r\n");
610 read_ascii_response(rsp, sizeof(rsp));
611 assert(strncmp(rsp, "END", strlen("END")) == 0);
612
613 memset(buffer, ' ', sizeof(buffer));
614 int len = snprintf(buffer + 101, sizeof(buffer) - 101, "gets foo");
615 buffer[101 + len] = ' ';
616 buffer[sizeof(buffer) - 1] = '\0';
617 send_ascii_command(buffer);
618 /* verify that the server closed the connection */
619 assert(read(sock, buffer, sizeof(buffer)) == 0);
620
621 close(sock);
622 sock = connect_server("127.0.0.1", port, false);
623
624 return TEST_PASS;
625 }
626
627 static enum test_return start_memcached_server(void) {
628 server_pid = start_server(&port, false, 600);
629 sock = connect_server("127.0.0.1", port, false);
630 return TEST_PASS;
631 }
632
633 static enum test_return stop_memcached_server(void) {
634 close(sock);
635 assert(kill(server_pid, SIGTERM) == 0);
636 return TEST_PASS;
637 }
638
639 static void safe_send(const void* buf, size_t len, bool hickup)
640 {
641 off_t offset = 0;
642 const char* ptr = buf;
643 #ifdef MESSAGE_DEBUG
644 uint8_t val = *ptr;
645 assert(val == (uint8_t)0x80);
646 fprintf(stderr, "About to send %lu bytes:", (unsigned long)len);
647 for (int ii = 0; ii < len; ++ii) {
648 if (ii % 4 == 0) {
649 fprintf(stderr, "\n ");
650 }
651 val = *(ptr + ii);
652 fprintf(stderr, " 0x%02x", val);
653 }
654 fprintf(stderr, "\n");
655 usleep(500);
656 #endif
657
658 do {
659 size_t num_bytes = len - offset;
660 if (hickup) {
661 if (num_bytes > 1024) {
662 num_bytes = (rand() % 1023) + 1;
663 }
664 }
665
666 ssize_t nw = write(sock, ptr + offset, num_bytes);
667 if (nw == -1) {
668 if (errno != EINTR) {
669 fprintf(stderr, "Failed to write: %s\n", strerror(errno));
670 abort();
671 }
672 } else {
673 if (hickup) {
674 usleep(100);
675 }
676 offset += nw;
677 }
678 } while (offset < len);
679 }
680
681 static bool safe_recv(void *buf, size_t len) {
682 if (len == 0) {
683 return true;
684 }
685 off_t offset = 0;
686 do {
687 ssize_t nr = read(sock, ((char*)buf) + offset, len - offset);
688 if (nr == -1) {
689 if (errno != EINTR) {
690 fprintf(stderr, "Failed to read: %s\n", strerror(errno));
691 abort();
692 }
693 } else {
694 if (nr == 0 && allow_closed_read) {
695 return false;
696 }
697 assert(nr != 0);
698 offset += nr;
699 }
700 } while (offset < len);
701
702 return true;
703 }
704
705 static bool safe_recv_packet(void *buf, size_t size) {
706 protocol_binary_response_no_extras *response = buf;
707 assert(size > sizeof(*response));
708 if (!safe_recv(response, sizeof(*response))) {
709 return false;
710 }
711 response->message.header.response.keylen = ntohs(response->message.header.response.keylen);
712 response->message.header.response.status = ntohs(response->message.header.response.status);
713 response->message.header.response.bodylen = ntohl(response->message.header.response.bodylen);
714
715 size_t len = sizeof(*response);
716
717 char *ptr = buf;
718 ptr += len;
719 if (!safe_recv(ptr, response->message.header.response.bodylen)) {
720 return false;
721 }
722
723 #ifdef MESSAGE_DEBUG
724 usleep(500);
725 ptr = buf;
726 len += response->message.header.response.bodylen;
727 uint8_t val = *ptr;
728 assert(val == (uint8_t)0x81);
729 fprintf(stderr, "Received %lu bytes:", (unsigned long)len);
730 for (int ii = 0; ii < len; ++ii) {
731 if (ii % 4 == 0) {
732 fprintf(stderr, "\n ");
733 }
734 val = *(ptr + ii);
735 fprintf(stderr, " 0x%02x", val);
736 }
737 fprintf(stderr, "\n");
738 #endif
739 return true;
740 }
741
742 static off_t storage_command(char*buf,
743 size_t bufsz,
744 uint8_t cmd,
745 const void* key,
746 size_t keylen,
747 const void* dta,
748 size_t dtalen,
749 uint32_t flags,
750 uint32_t exp) {
751 /* all of the storage commands use the same command layout */
752 protocol_binary_request_set *request = (void*)buf;
753 assert(bufsz > sizeof(*request) + keylen + dtalen);
754
755 memset(request, 0, sizeof(*request));
756 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
757 request->message.header.request.opcode = cmd;
758 request->message.header.request.keylen = htons(keylen);
759 request->message.header.request.extlen = 8;
760 request->message.header.request.bodylen = htonl(keylen + 8 + dtalen);
761 request->message.header.request.opaque = 0xdeadbeef;
762 request->message.body.flags = flags;
763 request->message.body.expiration = exp;
764
765 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 8;
766
767 memcpy(buf + key_offset, key, keylen);
768 if (dta != NULL) {
769 memcpy(buf + key_offset + keylen, dta, dtalen);
770 }
771
772 return key_offset + keylen + dtalen;
773 }
774
775 static off_t raw_command(char* buf,
776 size_t bufsz,
777 uint8_t cmd,
778 const void* key,
779 size_t keylen,
780 const void* dta,
781 size_t dtalen) {
782 /* all of the storage commands use the same command layout */
783 protocol_binary_request_no_extras *request = (void*)buf;
784 assert(bufsz > sizeof(*request) + keylen + dtalen);
785
786 memset(request, 0, sizeof(*request));
787 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
788 request->message.header.request.opcode = cmd;
789 request->message.header.request.keylen = htons(keylen);
790 request->message.header.request.bodylen = htonl(keylen + dtalen);
791 request->message.header.request.opaque = 0xdeadbeef;
792
793 off_t key_offset = sizeof(protocol_binary_request_no_extras);
794
795 if (key != NULL) {
796 memcpy(buf + key_offset, key, keylen);
797 }
798 if (dta != NULL) {
799 memcpy(buf + key_offset + keylen, dta, dtalen);
800 }
801
802 return sizeof(*request) + keylen + dtalen;
803 }
804
805 static off_t flush_command(char* buf, size_t bufsz, uint8_t cmd, uint32_t exptime, bool use_extra) {
806 protocol_binary_request_flush *request = (void*)buf;
807 assert(bufsz > sizeof(*request));
808
809 memset(request, 0, sizeof(*request));
810 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
811 request->message.header.request.opcode = cmd;
812
813 off_t size = sizeof(protocol_binary_request_no_extras);
814 if (use_extra) {
815 request->message.header.request.extlen = 4;
816 request->message.body.expiration = htonl(exptime);
817 request->message.header.request.bodylen = htonl(4);
818 size += 4;
819 }
820
821 request->message.header.request.opaque = 0xdeadbeef;
822
823 return size;
824 }
825
826
827 static off_t touch_command(char* buf,
828 size_t bufsz,
829 uint8_t cmd,
830 const void* key,
831 size_t keylen,
832 uint32_t exptime) {
833 protocol_binary_request_touch *request = (void*)buf;
834 assert(bufsz > sizeof(*request));
835
836 memset(request, 0, sizeof(*request));
837 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
838 request->message.header.request.opcode = cmd;
839
840 request->message.header.request.keylen = htons(keylen);
841 request->message.header.request.extlen = 4;
842 request->message.body.expiration = htonl(exptime);
843 request->message.header.request.bodylen = htonl(keylen + 4);
844
845 request->message.header.request.opaque = 0xdeadbeef;
846
847 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 4;
848
849 memcpy(buf + key_offset, key, keylen);
850 return sizeof(protocol_binary_request_no_extras) + 4 + keylen;
851 }
852
853 static off_t arithmetic_command(char* buf,
854 size_t bufsz,
855 uint8_t cmd,
856 const void* key,
857 size_t keylen,
858 uint64_t delta,
859 uint64_t initial,
860 uint32_t exp) {
861 protocol_binary_request_incr *request = (void*)buf;
862 assert(bufsz > sizeof(*request) + keylen);
863
864 memset(request, 0, sizeof(*request));
865 request->message.header.request.magic = PROTOCOL_BINARY_REQ;
866 request->message.header.request.opcode = cmd;
867 request->message.header.request.keylen = htons(keylen);
868 request->message.header.request.extlen = 20;
869 request->message.header.request.bodylen = htonl(keylen + 20);
870 request->message.header.request.opaque = 0xdeadbeef;
871 request->message.body.delta = htonll(delta);
872 request->message.body.initial = htonll(initial);
873 request->message.body.expiration = htonl(exp);
874
875 off_t key_offset = sizeof(protocol_binary_request_no_extras) + 20;
876
877 memcpy(buf + key_offset, key, keylen);
878 return key_offset + keylen;
879 }
880
881 static void validate_response_header(protocol_binary_response_no_extras *response,
882 uint8_t cmd, uint16_t status)
883 {
884 assert(response->message.header.response.magic == PROTOCOL_BINARY_RES);
885 assert(response->message.header.response.opcode == cmd);
886 assert(response->message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
887 assert(response->message.header.response.status == status);
888 assert(response->message.header.response.opaque == 0xdeadbeef);
889
890 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
891 switch (cmd) {
892 case PROTOCOL_BINARY_CMD_ADDQ:
893 case PROTOCOL_BINARY_CMD_APPENDQ:
894 case PROTOCOL_BINARY_CMD_DECREMENTQ:
895 case PROTOCOL_BINARY_CMD_DELETEQ:
896 case PROTOCOL_BINARY_CMD_FLUSHQ:
897 case PROTOCOL_BINARY_CMD_INCREMENTQ:
898 case PROTOCOL_BINARY_CMD_PREPENDQ:
899 case PROTOCOL_BINARY_CMD_QUITQ:
900 case PROTOCOL_BINARY_CMD_REPLACEQ:
901 case PROTOCOL_BINARY_CMD_SETQ:
902 assert("Quiet command shouldn't return on success" == NULL);
903 default:
904 break;
905 }
906
907 switch (cmd) {
908 case PROTOCOL_BINARY_CMD_ADD:
909 case PROTOCOL_BINARY_CMD_REPLACE:
910 case PROTOCOL_BINARY_CMD_SET:
911 case PROTOCOL_BINARY_CMD_APPEND:
912 case PROTOCOL_BINARY_CMD_PREPEND:
913 assert(response->message.header.response.keylen == 0);
914 assert(response->message.header.response.extlen == 0);
915 assert(response->message.header.response.bodylen == 0);
916 assert(response->message.header.response.cas != 0);
917 break;
918 case PROTOCOL_BINARY_CMD_FLUSH:
919 case PROTOCOL_BINARY_CMD_NOOP:
920 case PROTOCOL_BINARY_CMD_QUIT:
921 case PROTOCOL_BINARY_CMD_DELETE:
922 assert(response->message.header.response.keylen == 0);
923 assert(response->message.header.response.extlen == 0);
924 assert(response->message.header.response.bodylen == 0);
925 assert(response->message.header.response.cas == 0);
926 break;
927
928 case PROTOCOL_BINARY_CMD_DECREMENT:
929 case PROTOCOL_BINARY_CMD_INCREMENT:
930 assert(response->message.header.response.keylen == 0);
931 assert(response->message.header.response.extlen == 0);
932 assert(response->message.header.response.bodylen == 8);
933 assert(response->message.header.response.cas != 0);
934 break;
935
936 case PROTOCOL_BINARY_CMD_STAT:
937 assert(response->message.header.response.extlen == 0);
938 /* key and value exists in all packets except in the terminating */
939 assert(response->message.header.response.cas == 0);
940 break;
941
942 case PROTOCOL_BINARY_CMD_VERSION:
943 assert(response->message.header.response.keylen == 0);
944 assert(response->message.header.response.extlen == 0);
945 assert(response->message.header.response.bodylen != 0);
946 assert(response->message.header.response.cas == 0);
947 break;
948
949 case PROTOCOL_BINARY_CMD_GET:
950 case PROTOCOL_BINARY_CMD_GETQ:
951 assert(response->message.header.response.keylen == 0);
952 assert(response->message.header.response.extlen == 4);
953 assert(response->message.header.response.cas != 0);
954 break;
955
956 case PROTOCOL_BINARY_CMD_GETK:
957 case PROTOCOL_BINARY_CMD_GETKQ:
958 assert(response->message.header.response.keylen != 0);
959 assert(response->message.header.response.extlen == 4);
960 assert(response->message.header.response.cas != 0);
961 break;
962
963 default:
964 /* Undefined command code */
965 break;
966 }
967 } else {
968 assert(response->message.header.response.cas == 0);
969 assert(response->message.header.response.extlen == 0);
970 if (cmd != PROTOCOL_BINARY_CMD_GETK &&
971 cmd != PROTOCOL_BINARY_CMD_GATK) {
972 assert(response->message.header.response.keylen == 0);
973 }
974 }
975 }
976
977 static enum test_return test_binary_noop(void) {
978 union {
979 protocol_binary_request_no_extras request;
980 protocol_binary_response_no_extras response;
981 char bytes[1024];
982 } buffer;
983
984 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
985 PROTOCOL_BINARY_CMD_NOOP,
986 NULL, 0, NULL, 0);
987
988 safe_send(buffer.bytes, len, false);
989 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
990 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_NOOP,
991 PROTOCOL_BINARY_RESPONSE_SUCCESS);
992
993 return TEST_PASS;
994 }
995
996 static enum test_return test_binary_quit_impl(uint8_t cmd) {
997 union {
998 protocol_binary_request_no_extras request;
999 protocol_binary_response_no_extras response;
1000 char bytes[1024];
1001 } buffer;
1002 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1003 cmd, NULL, 0, NULL, 0);
1004
1005 safe_send(buffer.bytes, len, false);
1006 if (cmd == PROTOCOL_BINARY_CMD_QUIT) {
1007 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1008 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_QUIT,
1009 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1010 }
1011
1012 /* Socket should be closed now, read should return 0 */
1013 assert(read(sock, buffer.bytes, sizeof(buffer.bytes)) == 0);
1014 close(sock);
1015 sock = connect_server("127.0.0.1", port, false);
1016
1017 return TEST_PASS;
1018 }
1019
1020 static enum test_return test_binary_quit(void) {
1021 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
1022 }
1023
1024 static enum test_return test_binary_quitq(void) {
1025 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
1026 }
1027
1028 static enum test_return test_binary_set_impl(const char *key, uint8_t cmd) {
1029 union {
1030 protocol_binary_request_no_extras request;
1031 protocol_binary_response_no_extras response;
1032 char bytes[1024];
1033 } send, receive;
1034 uint64_t value = 0xdeadbeefdeadcafe;
1035 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1036 key, strlen(key), &value, sizeof(value),
1037 0, 0);
1038
1039 /* Set should work over and over again */
1040 int ii;
1041 for (ii = 0; ii < 10; ++ii) {
1042 safe_send(send.bytes, len, false);
1043 if (cmd == PROTOCOL_BINARY_CMD_SET) {
1044 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1045 validate_response_header(&receive.response, cmd,
1046 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1047 }
1048 }
1049
1050 if (cmd == PROTOCOL_BINARY_CMD_SETQ) {
1051 return test_binary_noop();
1052 }
1053
1054 send.request.message.header.request.cas = receive.response.message.header.response.cas;
1055 safe_send(send.bytes, len, false);
1056 if (cmd == PROTOCOL_BINARY_CMD_SET) {
1057 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1058 validate_response_header(&receive.response, cmd,
1059 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1060 assert(receive.response.message.header.response.cas != send.request.message.header.request.cas);
1061 } else {
1062 return test_binary_noop();
1063 }
1064
1065 return TEST_PASS;
1066 }
1067
1068 static enum test_return test_binary_set(void) {
1069 return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
1070 }
1071
1072 static enum test_return test_binary_setq(void) {
1073 return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
1074 }
1075
1076
1077 static enum test_return test_binary_add_impl(const char *key, uint8_t cmd) {
1078 uint64_t value = 0xdeadbeefdeadcafe;
1079 union {
1080 protocol_binary_request_no_extras request;
1081 protocol_binary_response_no_extras response;
1082 char bytes[1024];
1083 } send, receive;
1084 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd, key,
1085 strlen(key), &value, sizeof(value),
1086 0, 0);
1087
1088 /* Add should only work the first time */
1089 int ii;
1090 for (ii = 0; ii < 10; ++ii) {
1091 safe_send(send.bytes, len, false);
1092 if (ii == 0) {
1093 if (cmd == PROTOCOL_BINARY_CMD_ADD) {
1094 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1095 validate_response_header(&receive.response, cmd,
1096 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1097 }
1098 } else {
1099 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1100 validate_response_header(&receive.response, cmd,
1101 PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1102 }
1103 }
1104
1105 return TEST_PASS;
1106 }
1107
1108 static enum test_return test_binary_add(void) {
1109 return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
1110 }
1111
1112 static enum test_return test_binary_addq(void) {
1113 return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
1114 }
1115
1116 static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd) {
1117 uint64_t value = 0xdeadbeefdeadcafe;
1118 union {
1119 protocol_binary_request_no_extras request;
1120 protocol_binary_response_no_extras response;
1121 char bytes[1024];
1122 } send, receive;
1123 size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1124 key, strlen(key), &value, sizeof(value),
1125 0, 0);
1126 safe_send(send.bytes, len, false);
1127 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1128 validate_response_header(&receive.response, cmd,
1129 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1130 len = storage_command(send.bytes, sizeof(send.bytes),
1131 PROTOCOL_BINARY_CMD_ADD,
1132 key, strlen(key), &value, sizeof(value), 0, 0);
1133 safe_send(send.bytes, len, false);
1134 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1135 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1136 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1137
1138 len = storage_command(send.bytes, sizeof(send.bytes), cmd,
1139 key, strlen(key), &value, sizeof(value), 0, 0);
1140 int ii;
1141 for (ii = 0; ii < 10; ++ii) {
1142 safe_send(send.bytes, len, false);
1143 if (cmd == PROTOCOL_BINARY_CMD_REPLACE) {
1144 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1145 validate_response_header(&receive.response,
1146 PROTOCOL_BINARY_CMD_REPLACE,
1147 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1148 }
1149 }
1150
1151 if (cmd == PROTOCOL_BINARY_CMD_REPLACEQ) {
1152 test_binary_noop();
1153 }
1154
1155 return TEST_PASS;
1156 }
1157
1158 static enum test_return test_binary_replace(void) {
1159 return test_binary_replace_impl("test_binary_replace",
1160 PROTOCOL_BINARY_CMD_REPLACE);
1161 }
1162
1163 static enum test_return test_binary_replaceq(void) {
1164 return test_binary_replace_impl("test_binary_replaceq",
1165 PROTOCOL_BINARY_CMD_REPLACEQ);
1166 }
1167
1168 static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd) {
1169 union {
1170 protocol_binary_request_no_extras request;
1171 protocol_binary_response_no_extras response;
1172 char bytes[1024];
1173 } send, receive;
1174 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1175 key, strlen(key), NULL, 0);
1176
1177 safe_send(send.bytes, len, false);
1178 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1179 validate_response_header(&receive.response, cmd,
1180 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1181 len = storage_command(send.bytes, sizeof(send.bytes),
1182 PROTOCOL_BINARY_CMD_ADD,
1183 key, strlen(key), NULL, 0, 0, 0);
1184 safe_send(send.bytes, len, false);
1185 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1186 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1187 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1188
1189 len = raw_command(send.bytes, sizeof(send.bytes),
1190 cmd, key, strlen(key), NULL, 0);
1191 safe_send(send.bytes, len, false);
1192
1193 if (cmd == PROTOCOL_BINARY_CMD_DELETE) {
1194 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1195 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_DELETE,
1196 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1197 }
1198
1199 safe_send(send.bytes, len, false);
1200 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1201 validate_response_header(&receive.response, cmd,
1202 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1203
1204 return TEST_PASS;
1205 }
1206
1207 static enum test_return test_binary_delete(void) {
1208 return test_binary_delete_impl("test_binary_delete",
1209 PROTOCOL_BINARY_CMD_DELETE);
1210 }
1211
1212 static enum test_return test_binary_deleteq(void) {
1213 return test_binary_delete_impl("test_binary_deleteq",
1214 PROTOCOL_BINARY_CMD_DELETEQ);
1215 }
1216
1217 static enum test_return test_binary_get_impl(const char *key, uint8_t cmd) {
1218 union {
1219 protocol_binary_request_no_extras request;
1220 protocol_binary_response_no_extras response;
1221 char bytes[1024];
1222 } send, receive;
1223 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1224 key, strlen(key), NULL, 0);
1225
1226 safe_send(send.bytes, len, false);
1227 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1228 validate_response_header(&receive.response, cmd,
1229 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1230
1231 len = storage_command(send.bytes, sizeof(send.bytes),
1232 PROTOCOL_BINARY_CMD_ADD,
1233 key, strlen(key), NULL, 0,
1234 0, 0);
1235 safe_send(send.bytes, len, false);
1236 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1237 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1238 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1239
1240 /* run a little pipeline test ;-) */
1241 len = 0;
1242 int ii;
1243 for (ii = 0; ii < 10; ++ii) {
1244 union {
1245 protocol_binary_request_no_extras request;
1246 char bytes[1024];
1247 } temp;
1248 size_t l = raw_command(temp.bytes, sizeof(temp.bytes),
1249 cmd, key, strlen(key), NULL, 0);
1250 memcpy(send.bytes + len, temp.bytes, l);
1251 len += l;
1252 }
1253
1254 safe_send(send.bytes, len, false);
1255 for (ii = 0; ii < 10; ++ii) {
1256 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1257 validate_response_header(&receive.response, cmd,
1258 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1259 }
1260
1261 return TEST_PASS;
1262 }
1263
1264 static enum test_return test_binary_get(void) {
1265 return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
1266 }
1267
1268 static enum test_return test_binary_getk(void) {
1269 return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
1270 }
1271
1272 static enum test_return test_binary_getq_impl(const char *key, uint8_t cmd) {
1273 const char *missing = "test_binary_getq_missing";
1274 union {
1275 protocol_binary_request_no_extras request;
1276 protocol_binary_response_no_extras response;
1277 char bytes[1024];
1278 } send, temp, receive;
1279 size_t len = storage_command(send.bytes, sizeof(send.bytes),
1280 PROTOCOL_BINARY_CMD_ADD,
1281 key, strlen(key), NULL, 0,
1282 0, 0);
1283 size_t len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
1284 missing, strlen(missing), NULL, 0);
1285 /* I need to change the first opaque so that I can separate the two
1286 * return packets */
1287 temp.request.message.header.request.opaque = 0xfeedface;
1288 memcpy(send.bytes + len, temp.bytes, len2);
1289 len += len2;
1290
1291 len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
1292 key, strlen(key), NULL, 0);
1293 memcpy(send.bytes + len, temp.bytes, len2);
1294 len += len2;
1295
1296 safe_send(send.bytes, len, false);
1297 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1298 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1299 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1300 /* The first GETQ shouldn't return anything */
1301 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1302 validate_response_header(&receive.response, cmd,
1303 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1304
1305 return TEST_PASS;
1306 }
1307
1308 static enum test_return test_binary_getq(void) {
1309 return test_binary_getq_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
1310 }
1311
1312 static enum test_return test_binary_getkq(void) {
1313 return test_binary_getq_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
1314 }
1315
1316 static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd) {
1317 union {
1318 protocol_binary_request_no_extras request;
1319 protocol_binary_response_no_extras response_header;
1320 protocol_binary_response_incr response;
1321 char bytes[1024];
1322 } send, receive;
1323 size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1324 key, strlen(key), 1, 0, 0);
1325
1326 int ii;
1327 for (ii = 0; ii < 10; ++ii) {
1328 safe_send(send.bytes, len, false);
1329 if (cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
1330 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1331 validate_response_header(&receive.response_header, cmd,
1332 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1333 assert(ntohll(receive.response.message.body.value) == ii);
1334 }
1335 }
1336
1337 if (cmd == PROTOCOL_BINARY_CMD_INCREMENTQ) {
1338 test_binary_noop();
1339 }
1340 return TEST_PASS;
1341 }
1342
1343 static enum test_return test_binary_incr(void) {
1344 return test_binary_incr_impl("test_binary_incr",
1345 PROTOCOL_BINARY_CMD_INCREMENT);
1346 }
1347
1348 static enum test_return test_binary_incrq(void) {
1349 return test_binary_incr_impl("test_binary_incrq",
1350 PROTOCOL_BINARY_CMD_INCREMENTQ);
1351 }
1352
1353 static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd) {
1354 union {
1355 protocol_binary_request_no_extras request;
1356 protocol_binary_response_no_extras response_header;
1357 protocol_binary_response_decr response;
1358 char bytes[1024];
1359 } send, receive;
1360 size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
1361 key, strlen(key), 1, 9, 0);
1362
1363 int ii;
1364 for (ii = 9; ii >= 0; --ii) {
1365 safe_send(send.bytes, len, false);
1366 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1367 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1368 validate_response_header(&receive.response_header, cmd,
1369 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1370 assert(ntohll(receive.response.message.body.value) == ii);
1371 }
1372 }
1373
1374 /* decr on 0 should not wrap */
1375 safe_send(send.bytes, len, false);
1376 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
1377 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1378 validate_response_header(&receive.response_header, cmd,
1379 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1380 assert(ntohll(receive.response.message.body.value) == 0);
1381 } else {
1382 test_binary_noop();
1383 }
1384
1385 return TEST_PASS;
1386 }
1387
1388 static enum test_return test_binary_decr(void) {
1389 return test_binary_decr_impl("test_binary_decr",
1390 PROTOCOL_BINARY_CMD_DECREMENT);
1391 }
1392
1393 static enum test_return test_binary_decrq(void) {
1394 return test_binary_decr_impl("test_binary_decrq",
1395 PROTOCOL_BINARY_CMD_DECREMENTQ);
1396 }
1397
1398 static enum test_return test_binary_version(void) {
1399 union {
1400 protocol_binary_request_no_extras request;
1401 protocol_binary_response_no_extras response;
1402 char bytes[1024];
1403 } buffer;
1404
1405 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1406 PROTOCOL_BINARY_CMD_VERSION,
1407 NULL, 0, NULL, 0);
1408
1409 safe_send(buffer.bytes, len, false);
1410 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1411 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_VERSION,
1412 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1413
1414 return TEST_PASS;
1415 }
1416
1417 static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd) {
1418 union {
1419 protocol_binary_request_no_extras request;
1420 protocol_binary_response_no_extras response;
1421 char bytes[1024];
1422 } send, receive;
1423
1424 size_t len = storage_command(send.bytes, sizeof(send.bytes),
1425 PROTOCOL_BINARY_CMD_ADD,
1426 key, strlen(key), NULL, 0, 0, 0);
1427 safe_send(send.bytes, len, false);
1428 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1429 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1430 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1431
1432 len = flush_command(send.bytes, sizeof(send.bytes), cmd, 2, true);
1433 safe_send(send.bytes, len, false);
1434 if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1435 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1436 validate_response_header(&receive.response, cmd,
1437 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1438 }
1439
1440 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GET,
1441 key, strlen(key), NULL, 0);
1442 safe_send(send.bytes, len, false);
1443 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1444 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1445 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1446
1447 sleep(2);
1448 safe_send(send.bytes, len, false);
1449 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1450 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1451 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1452
1453 int ii;
1454 for (ii = 0; ii < 2; ++ii) {
1455 len = storage_command(send.bytes, sizeof(send.bytes),
1456 PROTOCOL_BINARY_CMD_ADD,
1457 key, strlen(key), NULL, 0, 0, 0);
1458 safe_send(send.bytes, len, false);
1459 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1460 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1461 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1462
1463 len = flush_command(send.bytes, sizeof(send.bytes), cmd, 0, ii == 0);
1464 safe_send(send.bytes, len, false);
1465 if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
1466 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1467 validate_response_header(&receive.response, cmd,
1468 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1469 }
1470
1471 len = raw_command(send.bytes, sizeof(send.bytes),
1472 PROTOCOL_BINARY_CMD_GET,
1473 key, strlen(key), NULL, 0);
1474 safe_send(send.bytes, len, false);
1475 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1476 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
1477 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
1478 }
1479
1480 return TEST_PASS;
1481 }
1482
1483 static enum test_return test_binary_flush(void) {
1484 return test_binary_flush_impl("test_binary_flush",
1485 PROTOCOL_BINARY_CMD_FLUSH);
1486 }
1487
1488 static enum test_return test_binary_flushq(void) {
1489 return test_binary_flush_impl("test_binary_flushq",
1490 PROTOCOL_BINARY_CMD_FLUSHQ);
1491 }
1492
1493 static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd) {
1494 union {
1495 protocol_binary_request_no_extras request;
1496 protocol_binary_response_no_extras response;
1497 char bytes[1024];
1498 } send, receive;
1499 const char *value = "world";
1500
1501 size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1502 key, strlen(key), value, strlen(value));
1503
1504
1505 safe_send(send.bytes, len, false);
1506 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1507 validate_response_header(&receive.response, cmd,
1508 PROTOCOL_BINARY_RESPONSE_NOT_STORED);
1509
1510 len = storage_command(send.bytes, sizeof(send.bytes),
1511 PROTOCOL_BINARY_CMD_ADD,
1512 key, strlen(key), value, strlen(value), 0, 0);
1513 safe_send(send.bytes, len, false);
1514 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1515 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
1516 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1517
1518 len = raw_command(send.bytes, sizeof(send.bytes), cmd,
1519 key, strlen(key), value, strlen(value));
1520 safe_send(send.bytes, len, false);
1521
1522 if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND) {
1523 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1524 validate_response_header(&receive.response, cmd,
1525 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1526 } else {
1527 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_NOOP,
1528 NULL, 0, NULL, 0);
1529 safe_send(send.bytes, len, false);
1530 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1531 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_NOOP,
1532 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1533 }
1534
1535 len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GETK,
1536 key, strlen(key), NULL, 0);
1537
1538 safe_send(send.bytes, len, false);
1539 safe_recv_packet(receive.bytes, sizeof(receive.bytes));
1540 validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GETK,
1541 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1542
1543 assert(receive.response.message.header.response.keylen == strlen(key));
1544 assert(receive.response.message.header.response.bodylen == (strlen(key) + 2*strlen(value) + 4));
1545
1546 char *ptr = receive.bytes;
1547 ptr += sizeof(receive.response);
1548 ptr += 4;
1549
1550 assert(memcmp(ptr, key, strlen(key)) == 0);
1551 ptr += strlen(key);
1552 assert(memcmp(ptr, value, strlen(value)) == 0);
1553 ptr += strlen(value);
1554 assert(memcmp(ptr, value, strlen(value)) == 0);
1555
1556 return TEST_PASS;
1557 }
1558
1559 static enum test_return test_binary_append(void) {
1560 return test_binary_concat_impl("test_binary_append",
1561 PROTOCOL_BINARY_CMD_APPEND);
1562 }
1563
1564 static enum test_return test_binary_prepend(void) {
1565 return test_binary_concat_impl("test_binary_prepend",
1566 PROTOCOL_BINARY_CMD_PREPEND);
1567 }
1568
1569 static enum test_return test_binary_appendq(void) {
1570 return test_binary_concat_impl("test_binary_appendq",
1571 PROTOCOL_BINARY_CMD_APPENDQ);
1572 }
1573
1574 static enum test_return test_binary_prependq(void) {
1575 return test_binary_concat_impl("test_binary_prependq",
1576 PROTOCOL_BINARY_CMD_PREPENDQ);
1577 }
1578
1579 static enum test_return test_binary_stat(void) {
1580 union {
1581 protocol_binary_request_no_extras request;
1582 protocol_binary_response_no_extras response;
1583 char bytes[1024];
1584 } buffer;
1585
1586 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1587 PROTOCOL_BINARY_CMD_STAT,
1588 NULL, 0, NULL, 0);
1589
1590 safe_send(buffer.bytes, len, false);
1591 do {
1592 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1593 validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_STAT,
1594 PROTOCOL_BINARY_RESPONSE_SUCCESS);
1595 } while (buffer.response.message.header.response.keylen != 0);
1596
1597 return TEST_PASS;
1598 }
1599
1600 static enum test_return test_binary_illegal(void) {
1601 uint8_t cmd = 0x25;
1602 while (cmd != 0x00) {
1603 union {
1604 protocol_binary_request_no_extras request;
1605 protocol_binary_response_no_extras response;
1606 char bytes[1024];
1607 } buffer;
1608 size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
1609 cmd, NULL, 0, NULL, 0);
1610 safe_send(buffer.bytes, len, false);
1611 safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
1612 validate_response_header(&buffer.response, cmd,
1613 PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
1614 ++cmd;
1615 }
1616
1617 return TEST_PASS;
1618 }
1619
1620 volatile bool hickup_thread_running;
1621
1622 static void *binary_hickup_recv_verification_thread(void *arg) {
1623 protocol_binary_response_no_extras *response = malloc(65*1024);
1624 if (response != NULL) {
1625 while (safe_recv_packet(response, 65*1024)) {
1626 /* Just validate the packet format */
1627 validate_response_header(response,
1628 response->message.header.response.opcode,
1629 response->message.header.response.status);
1630 }
1631 free(response);
1632 }
1633 hickup_thread_running = false;
1634 allow_closed_read = false;
1635 return NULL;
1636 }
1637
1638 static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t buffersize) {
1639 off_t offset = 0;
1640 char *key[256];
1641 uint64_t value = 0xfeedfacedeadbeef;
1642
1643 while (hickup_thread_running &&
1644 offset + sizeof(protocol_binary_request_no_extras) < buffersize) {
1645 union {
1646 protocol_binary_request_no_extras request;
1647 char bytes[65 * 1024];
1648 } command;
1649 uint8_t cmd = (uint8_t)(rand() & 0xff);
1650 size_t len;
1651 size_t keylen = (rand() % 250) + 1;
1652
1653 switch (cmd) {
1654 case PROTOCOL_BINARY_CMD_ADD:
1655 case PROTOCOL_BINARY_CMD_ADDQ:
1656 case PROTOCOL_BINARY_CMD_REPLACE:
1657 case PROTOCOL_BINARY_CMD_REPLACEQ:
1658 case PROTOCOL_BINARY_CMD_SET:
1659 case PROTOCOL_BINARY_CMD_SETQ:
1660 len = storage_command(command.bytes, sizeof(command.bytes), cmd,
1661 key, keylen , &value, sizeof(value),
1662 0, 0);
1663 break;
1664 case PROTOCOL_BINARY_CMD_APPEND:
1665 case PROTOCOL_BINARY_CMD_APPENDQ:
1666 case PROTOCOL_BINARY_CMD_PREPEND:
1667 case PROTOCOL_BINARY_CMD_PREPENDQ:
1668 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1669 key, keylen, &value, sizeof(value));
1670 break;
1671 case PROTOCOL_BINARY_CMD_FLUSH:
1672 case PROTOCOL_BINARY_CMD_FLUSHQ:
1673 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1674 NULL, 0, NULL, 0);
1675 break;
1676 case PROTOCOL_BINARY_CMD_NOOP:
1677 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1678 NULL, 0, NULL, 0);
1679 break;
1680 case PROTOCOL_BINARY_CMD_DELETE:
1681 case PROTOCOL_BINARY_CMD_DELETEQ:
1682 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1683 key, keylen, NULL, 0);
1684 break;
1685 case PROTOCOL_BINARY_CMD_DECREMENT:
1686 case PROTOCOL_BINARY_CMD_DECREMENTQ:
1687 case PROTOCOL_BINARY_CMD_INCREMENT:
1688 case PROTOCOL_BINARY_CMD_INCREMENTQ:
1689 len = arithmetic_command(command.bytes, sizeof(command.bytes), cmd,
1690 key, keylen, 1, 0, 0);
1691 break;
1692 case PROTOCOL_BINARY_CMD_VERSION:
1693 len = raw_command(command.bytes, sizeof(command.bytes),
1694 PROTOCOL_BINARY_CMD_VERSION,
1695 NULL, 0, NULL, 0);
1696 break;
1697 case PROTOCOL_BINARY_CMD_GET:
1698 case PROTOCOL_BINARY_CMD_GETK:
1699 case PROTOCOL_BINARY_CMD_GETKQ:
1700 case PROTOCOL_BINARY_CMD_GETQ:
1701 len = raw_command(command.bytes, sizeof(command.bytes), cmd,
1702 key, keylen, NULL, 0);
1703 break;
1704
1705 case PROTOCOL_BINARY_CMD_TOUCH:
1706 case PROTOCOL_BINARY_CMD_GAT:
1707 case PROTOCOL_BINARY_CMD_GATQ:
1708 case PROTOCOL_BINARY_CMD_GATK:
1709 case PROTOCOL_BINARY_CMD_GATKQ:
1710 len = touch_command(command.bytes, sizeof(command.bytes), cmd,
1711 key, keylen, 10);
1712 break;
1713
1714 case PROTOCOL_BINARY_CMD_STAT:
1715 len = raw_command(command.bytes, sizeof(command.bytes),
1716 PROTOCOL_BINARY_CMD_STAT,
1717 NULL, 0, NULL, 0);
1718 break;
1719
1720 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
1721 case PROTOCOL_BINARY_CMD_SASL_AUTH:
1722 case PROTOCOL_BINARY_CMD_SASL_STEP:
1723 /* Ignoring SASL */
1724 case PROTOCOL_BINARY_CMD_QUITQ:
1725 case PROTOCOL_BINARY_CMD_QUIT:
1726 /* I don't want to pass on the quit commands ;-) */
1727 cmd |= 0xf0;
1728 /* FALLTHROUGH */
1729 default:
1730 len = raw_command(command.bytes, sizeof(command.bytes),
1731 cmd, NULL, 0, NULL, 0);
1732 }
1733
1734 if ((len + offset) < buffersize) {
1735 memcpy(((char*)buffer) + offset, command.bytes, len);
1736 offset += len;
1737 } else {
1738 break;
1739 }
1740 }
1741 safe_send(buffer, offset, true);
1742
1743 return TEST_PASS;
1744 }
1745
1746 static enum test_return test_binary_pipeline_hickup(void)
1747 {
1748 size_t buffersize = 65 * 1024;
1749 void *buffer = malloc(buffersize);
1750 int ii;
1751
1752 pthread_t tid;
1753 int ret;
1754 allow_closed_read = true;
1755 hickup_thread_running = true;
1756 if ((ret = pthread_create(&tid, NULL,
1757 binary_hickup_recv_verification_thread, NULL)) != 0) {
1758 fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
1759 return TEST_FAIL;
1760 }
1761
1762 /* Allow the thread to start */
1763 usleep(250);
1764
1765 srand((int)time(NULL));
1766 for (ii = 0; ii < 2; ++ii) {
1767 test_binary_pipeline_hickup_chunk(buffer, buffersize);
1768 }
1769
1770 /* send quitq to shut down the read thread ;-) */
1771 size_t len = raw_command(buffer, buffersize, PROTOCOL_BINARY_CMD_QUITQ,
1772 NULL, 0, NULL, 0);
1773 safe_send(buffer, len, false);
1774
1775 pthread_join(tid, NULL);
1776 free(buffer);
1777 return TEST_PASS;
1778 }
1779
1780
1781 static enum test_return test_issue_101(void) {
1782 const int max = 2;
1783 enum test_return ret = TEST_PASS;
1784 int fds[max];
1785 int ii = 0;
1786 pid_t child = 0;
1787
1788 if (getenv("SKIP_TEST_101") != NULL) {
1789 return TEST_SKIP;
1790 }
1791
1792 const char *command = "stats\r\nstats\r\nstats\r\nstats\r\nstats\r\n";
1793 size_t cmdlen = strlen(command);
1794
1795 server_pid = start_server(&port, false, 1000);
1796
1797 for (ii = 0; ii < max; ++ii) {
1798 fds[ii] = connect_server("127.0.0.1", port, true);
1799 assert(fds[ii] > 0);
1800 }
1801
1802 /* Send command on the connection until it blocks */
1803 for (ii = 0; ii < max; ++ii) {
1804 bool more = true;
1805 do {
1806 ssize_t err = write(fds[ii], command, cmdlen);
1807 if (err == -1) {
1808 switch (errno) {
1809 case EINTR:
1810 break;
1811 case ENOMEM:
1812 case EWOULDBLOCK:
1813 more = false;
1814 break;
1815 default:
1816 ret = TEST_FAIL;
1817 goto cleanup;
1818 }
1819 }
1820 } while (more);
1821 }
1822
1823 child = fork();
1824 if (child == (pid_t)-1) {
1825 abort();
1826 } else if (child > 0) {
1827 int stat;
1828 pid_t c;
1829 while ((c = waitpid(child, &stat, 0)) == (pid_t)-1 && errno == EINTR);
1830 assert(c == child);
1831 assert(stat == 0);
1832 } else {
1833 sock = connect_server("127.0.0.1", port, false);
1834 ret = test_binary_noop();
1835 close(sock);
1836 exit(0);
1837 }
1838
1839 cleanup:
1840 /* close all connections */
1841 for (ii = 0; ii < max; ++ii) {
1842 close(fds[ii]);
1843 }
1844
1845 assert(kill(server_pid, SIGTERM) == 0);
1846
1847 return ret;
1848 }
1849
1850 typedef enum test_return (*TEST_FUNC)(void);
1851 struct testcase {
1852 const char *description;
1853 TEST_FUNC function;
1854 };
1855
1856 struct testcase testcases[] = {
1857 { "cache_create", cache_create_test },
1858 { "cache_constructor", cache_constructor_test },
1859 { "cache_constructor_fail", cache_fail_constructor_test },
1860 { "cache_destructor", cache_destructor_test },
1861 { "cache_reuse", cache_reuse_test },
1862 { "cache_redzone", cache_redzone_test },
1863 { "issue_161", test_issue_161 },
1864 { "strtol", test_safe_strtol },
1865 { "strtoll", test_safe_strtoll },
1866 { "strtoul", test_safe_strtoul },
1867 { "strtoull", test_safe_strtoull },
1868 { "issue_44", test_issue_44 },
1869 { "vperror", test_vperror },
1870 { "issue_101", test_issue_101 },
1871 /* The following tests all run towards the same server */
1872 { "start_server", start_memcached_server },
1873 { "issue_92", test_issue_92 },
1874 { "issue_102", test_issue_102 },
1875 { "binary_noop", test_binary_noop },
1876 { "binary_quit", test_binary_quit },
1877 { "binary_quitq", test_binary_quitq },
1878 { "binary_set", test_binary_set },
1879 { "binary_setq", test_binary_setq },
1880 { "binary_add", test_binary_add },
1881 { "binary_addq", test_binary_addq },
1882 { "binary_replace", test_binary_replace },
1883 { "binary_replaceq", test_binary_replaceq },
1884 { "binary_delete", test_binary_delete },
1885 { "binary_deleteq", test_binary_deleteq },
1886 { "binary_get", test_binary_get },
1887 { "binary_getq", test_binary_getq },
1888 { "binary_getk", test_binary_getk },
1889 { "binary_getkq", test_binary_getkq },
1890 { "binary_incr", test_binary_incr },
1891 { "binary_incrq", test_binary_incrq },
1892 { "binary_decr", test_binary_decr },
1893 { "binary_decrq", test_binary_decrq },
1894 { "binary_version", test_binary_version },
1895 { "binary_flush", test_binary_flush },
1896 { "binary_flushq", test_binary_flushq },
1897 { "binary_append", test_binary_append },
1898 { "binary_appendq", test_binary_appendq },
1899 { "binary_prepend", test_binary_prepend },
1900 { "binary_prependq", test_binary_prependq },
1901 { "binary_stat", test_binary_stat },
1902 { "binary_illegal", test_binary_illegal },
1903 { "binary_pipeline_hickup", test_binary_pipeline_hickup },
1904 { "stop_server", stop_memcached_server },
1905 { NULL, NULL }
1906 };
1907
1908 int main(int argc, char **argv)
1909 {
1910 int exitcode = 0;
1911 int ii = 0, num_cases = 0;
1912
1913 for (num_cases = 0; testcases[num_cases].description; num_cases++) {
1914 /* Just counting */
1915 }
1916
1917 printf("1..%d\n", num_cases);
1918
1919 for (ii = 0; testcases[ii].description != NULL; ++ii) {
1920 fflush(stdout);
1921 #ifndef DEBUG
1922 /* the test program shouldn't run longer than 10 minutes... */
1923 alarm(600);
1924 #endif
1925 enum test_return ret = testcases[ii].function();
1926 if (ret == TEST_SKIP) {
1927 fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
1928 } else if (ret == TEST_PASS) {
1929 fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
1930 } else {
1931 fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
1932 exitcode = 1;
1933 }
1934 fflush(stdout);
1935 }
1936
1937 return exitcode;
1938 }