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